Skip to main content

database

Run SQL against PostgreSQL, MySQL, or SQLite from the iii engine — reads, writes, transactions, and prepared statements over managed connection pools.

Quellinformationen

Repository
iii-hq/workers
Letzte Quellaktivität
23. Oktober 2026 um 11:34
Erkannte Sprache von SKILL.md
Englisch
Sterne
114
Forks
28

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
database
description
Run SQL against PostgreSQL, MySQL, or SQLite from the iii engine — reads, writes, transactions, and prepared statements over managed connection pools.
# database The database worker connects to PostgreSQL, MySQL, and SQLite through a managed per-database connection pool. Every callable surface lives under the `database::*` namespace. The driver is chosen from each database URL scheme (`sqlite:`, `postgres://`, `postgresql://`, `mysql://`). Runtime settings live in the `configuration` worker under id `database` (or the `config_id` that `database::listDatabases` reports); pools hot-reload when the value changes. SQLite is the recommended starting point. Placeholder syntax: `?` for SQLite and MySQL, `$1`/`$2`/… for Postgres. ## Querying a connection Each configured connection has a name; pass it as `db` on every call. 1. `database::listDatabases` — the connections: `name`, `driver`, `host`, `database`, `folder` (where its saved `.sql` files live, if any) and `error` when it is unavailable (unresolvable secret, SSH failure). An unavailable connection fails its calls with `CONFIG_ERROR` saying why; tell the person rather than retrying. 2. `database::listTables {db}` / `database::describeTable {db, table}` — the schema, one shape on every driver. 3. `database::query {db, sql, params?, max_rows?}` — reads. Pass `max_rows` (e.g. 100) for exploratory queries: the result says `truncated: true` when more rows existed, and the SQL is never rewritten. `elapsed_ms` is the statement's time. 4. `database::execute {db, sql, params?}` — writes. A chat may mention a connection as `@database(id="shop")`: that is the connection named `shop`; use `db: "shop"`. ### Saving a query "Save a query" means a `.sql` file in the connection's queries folder — the `folder` from `database::listDatabases` (or the mention's summary). Write the file there with the file tools (format it first with `database::formatSql`); that folder is what the apps list as the connection's queries. A connection without a `folder` has nowhere to save one: say so. `database::saveQuery` is something else — a console-only snippet list in the `state` worker, not a file — so never answer "saved as t3_status.sql" after calling it. Connections are managed by people (the console, the iii app): agents cannot read a stored entry (`getConnection`), save, delete or test one. Never ask for or handle database passwords; connections reference them as `secret://NAME`. ## When to Use - You need to read rows from a configured database (`database::query`). - You need to insert, update, delete, or run DDL and read affected-row counts or autoincrement ids (`database::execute`). - Several statements must commit or roll back together as one unit (`database::transaction`, `database::executeBatch`, or the interactive transaction surface). - The same parameterized SQL will run many times and you want to skip per-call parse/plan cost (`database::prepareStatement` + `database::runStatement`). - You need read-your-writes across round-trips with logic between steps (`database::beginTransaction` … `commitTransaction` / `rollbackTransaction`). ## Boundaries - Not a migration tool, ORM, or schema designer — pass raw SQL only. - Not a general pub/sub bus. `database::on-row-changed` reports what THIS worker wrote, on commit (`capture: statements`, the default) or, on a `capture: native` database, any client's committed writes. - `database::query` is read-oriented; use `database::execute` for writes. Keep reads on `query`: it runs them read-only, `execute` does not. - Prepared handles pin a pool connection until TTL expiry — not transactions. Batch `database::transaction` / `database::executeBatch` need every statement up front; use the interactive surface when code must branch between steps. - Rows come back from a `RETURNING` clause written into the SQL (SQLite, Postgres; MySQL has none). The `returning` option never adds the clause: SQLite rejects it without one, Postgres and MySQL ignore it. SQLite degrades `read_committed` / `repeatable_read` isolation to serializable. - For filesystem or shell operations, use the `shell` worker instead. ## Functions - `database::query` — run read-only SQL and return rows, row count, and column metadata. - `database::execute` — run write SQL (INSERT/UPDATE/DELETE/DDL) and return affected rows, optional last insert id, and the rows of a `RETURNING` clause in the SQL. - `database::executeBatch` — convenience form of `transaction`: statements may be bare SQL strings or `{sql, params}` objects (prefer `params` for dynamic values). Same atomic semantics, envelope, and `failed_index` reporting as `transaction`. - `database::prepareStatement` — parse and plan SQL once; return a handle that pins a pool connection until TTL expiry. - `database::runStatement` — re-execute a prepared handle with new bind params; response shape matches `query`. - `database::transaction` — run an ordered batch of statements atomically; rolls back on first failure and reports `failed_index`. - `database::beginTransaction` — open an interactive transaction and return an id plus expiry deadline. - `database::transactionQuery` — read SQL inside an open interactive transaction; same envelope as `query`. - `database::transactionExecute` — write SQL inside an open interactive transaction; same envelope as `execute`. Rejects bare transaction-control SQL — finalize via `commitTransaction` or `rollbackTransaction`. - `database::commitTransaction` — commit and finalize an interactive transaction. - `database::rollbackTransaction` — roll back and finalize an interactive transaction. - `database::listDatabases` — every configured connection with its driver, host, database, folder, credential-redacted URL, pool settings, TLS mode and `error` when unavailable. Config only; use `database::health` for live state. - `database::testConnection` — probe a candidate connection entry without saving it (people only: agents are denied it, like the connection editor functions). Interactive transactions auto-roll back when `timeout_ms` elapses (default 30 s, max 5 min). Prepared handles default to a 1 h TTL (max 24 h) with no explicit release call — let them expire or stop using them when done. ### Reading the schema One shape across all three drivers — prefer these over hand-writing `sqlite_master` / `information_schema` / `PRAGMA`. - `database::listTables` — tables and views, with kind, (postgres) schema and a `row_count_estimate` where the catalog has one (postgres, mysql). - `database::describeTable` — columns with type, nullability, default, primary key, a structured `foreign_key` of `{ schema, table, column }`, `auto_increment` (identity / serial / AUTO_INCREMENT / sqlite rowid — leave it out of `insertRow`) and `generated` (computed, never written); plus indexes and a planner row estimate. - `database::describeSchema` — the same for every table in one pass. Use this rather than looping `describeTable`. - `database::schemaDiagram` — positioned nodes, routed foreign-key edges, hub `degree` and `isolated` tables. For reasoning about a schema's shape, not only for drawing it. ### Reading data - `database::browseTable` — paged, sorted, filtered reads with no SQL. Filters are `{ column, op, value }` and `total` honours them; values compare as the column's type (on postgres `{column: "total", op: "gt", value: "100"}` is a numeric comparison; dates, uuids, enums and int8 as strings work too). Follow a foreign key with an equality filter at `page_size: 1`. A filter may be `{ raw: "price > 10 AND name LIKE 'a%'" }` — one boolean expression (no `;`, comments or placeholders), run read-only. - `database::distinctValues` — a column's values by frequency (`search` to narrow), e.g. to learn the statuses a table actually uses. - `database::explain` — the plan as a tree with costs and warnings. `analyze` **runs** the statement, so it defaults to false and is refused for anything that is not a single read. - `database::columnStats` — planner statistics by default (approximate, labelled `source: planner`); `exact: true` scans. To profile rows you already hold, use the `fp` worker on a `browseTable` result instead. ### Writing rows and formatting SQL - `database::insertRow {db, table, values}` — insert one row from a `{column: value}` map; values are cast to each column's type (numbers and dates may be strings, bytea/blob take base64) and the inserted row comes back. Prefer it over hand-built INSERTs when you have a record in hand. - `database::formatSql {sql, db}` (or `driver`) — pretty-print SQL for the connection's dialect, comments kept. Use it before writing a `.sql` file into a connection's `folder`. ### Values Rows are JSON objects. Postgres `bigint`/`numeric` and every `decimal` come back as strings (exact), `json` columns as JSON values, binary columns as base64, dates as `YYYY-MM-DD`, timestamps as RFC 3339 with `Z`, postgres arrays as JSON arrays. When writing them back, pass the same forms. ### Operations and reuse - `database::health` — pool occupancy, active queries, table sizes, locks, cache ratio. Each section is `available`, `unsupported` or `denied`, so a driver gap is never mistaken for an empty result. - `database::terminateQuery` — end a session, or cancel its statement with `cancel_only`. Takes an id from `database::health`. - `database::saveQuery`, `database::listSavedQueries`, `database::deleteSavedQuery` — the web console's snippet list per database, kept in the `state` worker: console-only, not files. To save a query for the person, write a `.sql` file into the connection's `folder` (see "Saving a query"). - `database::history` — recent queries, newest first. Best effort, not an audit log; observe `database::on-row-changed` for that. ## Reacting to writes `database::on-row-changed` announces each committed write statement, instead of polling. Code observes it (iii 0.25) with args naming the database, and optionally the table and operations, plus filters on what changed: ```json { "function_id": "database::on-row-changed", "args": { "db": "primary", "table": "orders", "ops": ["update"] }, "filters": { "input.returning": { "$any": { "@.status": { "$eq": "paid" } } } } } ``` `table` is the bare lowercase name (`orders`, not `public.Orders`); anything else is refused with the name to use. The event is `{ db, schema, table, op, affected_rows, returning?, truncated?, at }`, and `caller.parent` names the function that made the write. Nothing can invoke the function: only the database worker emits its events. In a session, subscribe with a `database::row-changed` trigger (served on 0.24 and 0.25 engines; the event has no `schema`): ```json { "trigger_type": "database::row-changed", "config": { "db": "primary", "table": "orders", "ops": ["insert"] } } ``` Either way it fires on commit — an interactive transaction's writes are announced by `commitTransaction`, and a rollback announces nothing. `returning` says which rows: on a `capture: statements` database it is the writer's own `RETURNING` projection, so write `RETURNING <primary key>` into the SQL when a listener needs row identity; on a `capture: native` database (which also hears other clients' writes, and needs a `table`) it is the changed rows' primary keys, capped at 100 with `truncated: true` past that. `table` is null when the statement's table cannot be read off the SQL (a CTE-wrapped write), and `runStatement` does not fire on the statements path because it has no affected-row count to report. Delivery is best-effort: it is not durable with the commit and has no replay or exactly-once guarantee.
Auf GitHub ansehen