# Saved queries

A saved query is a named SELECT plus a chart hint. Widgets reference saved
queries by `query_id`.

## Create — `bi_save_query`

| field | required | notes |
|-------|----------|-------|
| `name` | yes | display name |
| `sql` | yes | a SELECT; validated and executed before saving (invalid SQL is rejected) |
| `description` | optional | what it measures / how to read it |
| `chart_type` | optional | `bar`, `line`, `pie`, `table`, `kpi`, etc. |
| `tags` | optional | list of strings for categorisation |
| `parameters` | optional | typed-placeholder schema (see below) |

## Modify — `bi_modify_query`

Takes `query_id` plus any of `name`, `description`, `sql`, `chart_type`,
`parameters`. Only the fields you pass change. A new `sql` triggers
re-execution; passing `description=""` clears the description.

## The `parameters` field

`parameters` declares the typed placeholders a parameterized query uses. Each
entry: `{name (req), type (req), enumOptions (for enum), default?, title?,
queryId (for type=query)}`. Valid `type` values:
`text`, `text-pattern`, `number`, `enum`, `query`, `date`, `datetime-local`,
`datetime-with-seconds`, `date-range`, `datetime-range`,
`datetime-range-with-seconds`.

- Every `{{name}}` placeholder in the SQL needs a matching entry; a mismatch
  is rejected with `error_code=parameter_schema_invalid`.
- Range types use dotted `{{name.start}}` / `{{name.end}}` placeholders.

On `bi_modify_query`:

- **Omit** `parameters` → existing schema left **unchanged**.
- Pass `[]` → **clears** all parameters.

Full parameter walkthrough (plus how widgets bind to them): see the
`widget-parameters` topic.
