Expand description
SQL connectivity (Phase 7): import external SQL query results into an Improv
Model, and export a measure’s cells back to a SQL table.
Design invariants (see .agent/steering/AGENT_DATABASE_CONNECTIVITY.md §7):
- Storage separation. External SQL is a data source/sink; the canonical model lives in Mentat. Import produces ordinary categories/items/measures/ input cells — the engine gains no SQL-specific paths and stays deterministic.
- Security. An import/refresh query must be a single
SELECT/WITHstatement (check_read_only_sql), and every backend runs it read-only (Postgres and DuckDB in aREAD ONLYtransaction, DuckDB with external access off; SQLite only if the statement is read-only). Data values are bound as parameters, never interpolated. Postgres has no TLS in this build, soconnect_postgresrefuses a non-loopback host unless the connection string sayssslmode=disable.
Testing Postgres: the live tests skip unless IMPROV_TEST_PG holds a
libpq DSN to a scratch database they may create/drop tables in, e.g.
IMPROV_TEST_PG="host=127.0.0.1 user=improv password=improv dbname=improv_test" cargo test -p improv_storage_sql.
Backends sit behind a common Backend enum (backend module): SQLite
(the reference driver, and what the embedded Mentat store already uses) and
Postgres. Both produce a common typed backend::Table, so the column→model
mapping below is backend-agnostic. Connections are described out of band by
conn::Connection (no plaintext secrets) and opened via conn::open.
Re-exports§
pub use backend::Backend;pub use backend::Cell;pub use backend::SqlConn;pub use backend::Table;pub use conn::ConnKind;pub use conn::Connection;pub use duck::connect_duckdb;pub use pg::connect_postgres;
Modules§
- backend
- Backend abstraction over SQL drivers.
- conn
- Connection descriptors with credentials handled out of band.
- duck
- DuckDB backend (embedded, via the
duckdbcrate with thebundledfeature so no system libduckdb is required). - pg
- Postgres backend (synchronous
postgrescrate).
Structs§
- Dimension
Mapping - Import
Spec - How to map a query’s result columns onto model elements.
Enums§
Functions§
- add_
sql_ measure - Register an SQL-backed input measure (records its
SqlSourceon the model) and populate it by running the query once. The measure’s categories must already exist or be creatable from the import; this is a convenience overimport_querythat also marks the measure as SQL-backed so it can be refreshed later. - check_
read_ only_ sql - Reject anything that is not a single read statement.
- export_
measure - Write a measure’s input cells to a SQL table: one column per dimension category (item name), plus a value column. Creates the table if missing and inserts one row per cell. Values are bound as parameters (no interpolation).
- import_
query - Import the result of
spec.queryintomodelas a new input measure over the mapped dimension categories. Distinct dimension-column values become items (interned by their string form). Returns the number of cells imported. - refresh_
sql_ measure - Refresh an SQL-backed measure: re-run its stored query and replace its input
cells with the fresh result. New dimension values become new items. The
measure must have a
SqlSource(added viaadd_sql_measure).