Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 90 additions & 6 deletions docs/api/rest-api/other/sql-endpoints.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Execute raw SQL queries directly in Aidbox using $sql endpoint with JDBC parameter support.
description: Execute raw SQL queries directly in Aidbox via $sql, $psql, and $psql-cancel endpoints.
---

# SQL endpoints
Expand All @@ -8,7 +8,7 @@ description: Execute raw SQL queries directly in Aidbox using $sql endpoint with
Execute SQL in Aidbox.

Supported params:
- SQL string
- SQL string
- jdbc friendly array [SQL, param, param]

Example request:
Expand All @@ -20,7 +20,7 @@ POST /$sql?_format=yaml

SELECT count(*) FROM patient

# Response
# Response
#
# - {count: 7}
```
Expand All @@ -32,13 +32,97 @@ POST /$sql?_format=yaml

["SELECT count(*) FROM patient where resource->'status' = ?", true]

# Response
# Response
#
# - {count: 2}
```
{% endtab %}
{% endtabs %}

## $psql

Run a raw multi-statement SQL script in a single request. The Aidbox UI SQL Console uses this endpoint.

Request body:

```yaml
POST /$psql

{ "query": "SELECT 1; SELECT 2", "limit": 1000 }
```

- `query` — the SQL text. Aidbox sends the query verbatim, without splitting, trimming, or rewriting it.
- `limit` (optional) — caps the number of rows returned per statement.

Response (success):

```yaml
{ "status": "success",
"duration": 12,
"query": "SELECT 1; SELECT 2",
"result": [
{ "type": "rset", "data": [{ "?column?": 1 }] },
{ "type": "rset", "data": [{ "?column?": 2 }] }
] }
```

`result` is an array of one entry per statement. `:type` is `rset` for queries that return rows (`SELECT`, `INSERT … RETURNING`, `EXPLAIN`, …) and `count` for statements that report a row count (`UPDATE`, `DELETE`, `INSERT` without `RETURNING`).

Response (error):

```yaml
{ "status": "error",
"error": "ERROR: syntax error at or near \"SELEC\"\n Position: 1",
"duration": 4,
"query": "SELEC 1",
"position": 1 }
```

### Execution headers

Every header below is optional. Defaults match a single-transaction read-write run.

| Header | Value | Effect |
|---|---|---|
| `Aidbox-Sql-Autocommit` | `true` | Run outside a transaction. Required for `VACUUM`, `CREATE INDEX CONCURRENTLY`, `REINDEX CONCURRENTLY`. Rejected when [`db.pass-auth-vars`](../../../reference/all-settings.md#db.pass-auth-vars) is on **and** the request carries a resolvable identity — autocommit would drop the SQL identity injection that RLS relies on. |
| `Aidbox-Sql-Timeout` | seconds, 1..86400 | Per-query `statement_timeout`. Empty / non-numeric / negative / out-of-range values are ignored. |
| `Aidbox-Sql-Read-Only` | `true` | Run as read-only. Writes raise `ERROR: cannot execute … in a read-only transaction`. |
| `Aidbox-Sql-Query-Id` | UUID | Tags the PG session via `application_name = aidbox-psql:<uuid>`. The same UUID is used to cancel via `/$psql-cancel`. |
| `Aidbox-Sql-Async` | `true` | Fire-and-forget background execution. Returns `202 { "operation-id": "<uuid>" }` immediately. The query runs server-side; result rows are not retained — only `status` / `duration` / `query` / `error` are kept in `db_scheduler.scheduled_tasks_history`. The same handler accepts the operation-id as a `query-id` for cancellation. |

### Access scope

`$psql` is privileged-by-design. The SQL text a caller sends lands in several stores beyond the immediate response:

- `pg_stat_activity` while the query is in flight (visible to anyone with `pg_read_all_stats` or superuser).
- `klog` `:db/q` events — every successful run is logged, truncated by `proto.klog/sql-max-length` (default 500 chars).
- `AuditEvent` resources via the standard audit pipeline (base64-encoded SQL text).
- `db_scheduler.scheduled_tasks_history` rows for `Aidbox-Sql-Async: true` runs (until the row is cleaned up).

Anyone with permission to call `$psql` therefore has effective access to whatever the SQL itself reads, plus a long-lived trail in logs, audit, and scheduler history. Treat the endpoint as superuser-equivalent — do not expose it to non-admin roles via AccessPolicy.

### Breaking change in 2604

Prior versions of `$psql` returned a vector of per-statement debug objects and accepted an `execute=true` query parameter that switched between two execution paths; multi-statement scripts were split on `\n----\n`. Aidbox removed all three behaviours. Old clients that posted to `/$psql` without `execute=true` and parsed `[{:result …}, …]` need to update to the response shape above. The endpoint URL is unchanged.

## $psql-cancel

Cancel an in-flight query (sync or async) by its tag UUID.

```yaml
POST /$psql-cancel

{ "query-id": "<uuid sent in Aidbox-Sql-Query-Id, or operation-id from async kick-off>" }
```

The handler runs `pg_cancel_backend(pid)` on rows in `pg_stat_activity` whose `application_name` matches `aidbox-psql:<uuid>` and returns the matched backends:

```yaml
{ "cancelled": [ true ] }
```

The same endpoint covers sync and async runs because both tag the session with the same prefix.

## SQL migrations

Aidbox provides `POST and GET /db/migrations` operations to enable SQL migrations, which can be used to migrate/transform data, create helper functions, views etc.
Expand All @@ -53,7 +137,7 @@ POST /db/migrations
update patient set resource = resource - 'extension'
- id: create-policy-helper
sql: |
create function patient_for_user(u jsonb) returns jsonb
create function patient_for_user(u jsonb) returns jsonb
as $$
select resource || jsonb_build_object('id', id)
from patient
Expand All @@ -65,7 +149,7 @@ POST /db/migrations
sql: ...
- id: create-policy-helper
sql: ...

-- second run response
[]
```
Expand Down
9 changes: 9 additions & 0 deletions docs/overview/aidbox-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ An interactive HTTP client built into Aidbox. Use it to execute REST API request

Run SQL queries directly against the Aidbox database. Useful for debugging, analytics, and working with [SQL on FHIR](../../modules/sql-on-fhir/README.md) ViewDefinitions.

Per-tab settings:

* **Transaction mode** — `Autocommit` (each statement commits immediately; required for `VACUUM`, `CREATE INDEX CONCURRENTLY`) or `Transaction` (whole script wrapped in a single transaction).
* **Timeout** — per-query `statement_timeout`, default 1 minute.
* **Limit** — JDBC fetch size cap.
* **Execution** — `Foreground` (synchronous, results returned to the UI) or `Background` (fire-and-forget; the server runs the query, the UI does not retain results).

Stop button cancels the running query via [`$psql-cancel`](../../api/rest-api/other/sql-endpoints.md#usdpsql-cancel). The `Tab` key indents inside the editor; `EXPLAIN` plans render as a monospace block. Each tab keeps its own settings, query text, and running state. Backed by the [`$psql` endpoint](../../api/rest-api/other/sql-endpoints.md#usdpsql).

### FHIR Packages

Browse, install, and manage FHIR Implementation Guides and NPM packages loaded into Aidbox via the [FHIR Artifact Registry](../../artifact-registry/artifact-registry-overview.md). Inspect individual resources within each package.
Expand Down
13 changes: 13 additions & 0 deletions docs/overview/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ description: >-
# Release Notes
## April 2026 _`edge`_

* Aidbox FHIR server

**Features**

* Reworked SQL Console — per-tab transaction mode (transaction / autocommit), `statement_timeout`, foreground / background execution, and a `Tab` keybinding that indents.
* Background SQL execution via [`Aidbox-Sql-Async: true`](../api/rest-api/other/sql-endpoints.md#usdpsql). The server runs the query without retaining result rows.
* Query cancellation via [`$psql-cancel`](../api/rest-api/other/sql-endpoints.md#usdpsql-cancel).

**Breaking changes**

* [`$psql` response shape changed](../api/rest-api/other/sql-endpoints.md#breaking-change-in-2604). The `execute=true` query parameter and the `\n----\n` multi-statement separator are no longer recognised. `$sql` is unchanged.
* The legacy DB Console at `/ui/db` now redirects to the new SQL Console at `/u/db-console`.

## March 2026 _`latest, 2603`_

* Aidbox FHIR server
Expand Down
Loading