Skip to content

SQLite and DuckDB

Pass a local database directly to jt (the short alias for jevotron). Both engines work with the normal installation; no export or connection string is needed. SQLite and DuckDB are identified by their file headers, including files without extensions and DuckDB databases named .db.

jt tables inventory.db
jt preview inventory.db
jt scan inventory.db --guidance "Check product names and prices."

tables lists user tables and views as JSONL, including their columns, primary keys, and exact quoted selectors. It reads catalog metadata, without counting or scanning rows. Like preview, it needs no API key.

Try both engines

From the repository checkout, create two small synthetic databases in a new directory:

uv run python examples/databases/create.py /tmp/jt-database-demo
uv run jt tables /tmp/jt-database-demo/inventory.sqlite
uv run jt preview /tmp/jt-database-demo/inventory.sqlite --table products
uv run jt preview /tmp/jt-database-demo/inventory.duckdb --table products

Both contain products and suppliers tables, plus a product_names view. The product rows deliberately contain a negative price and a misspelled name. Once your API key is set, scan either file:

uv run jt scan /tmp/jt-database-demo/inventory.duckdb \
  --table products --field /name --field /price \
  --guidance "Product names should be correctly spelled. Prices must be positive." \
  --warnings-only --output-format csv -o product-warnings.csv

Defaults and selection

With no --table, scan processes all user tables, alphabetically by schema and table. System tables, SQLite FTS/RTree shadow tables, and views are excluded from this default. Empty tables produce no entries; a database with no user tables produces a descriptive error.

Each row becomes an entry whose keys are column names. All columns are assessed by default. --field selects JSON Pointers exactly as for files, and the entire row remains visible as context. Rows are assessed independently; joins, foreign key traversal, and database-wide consistency checks are not automatic.

Repeat --table to select several tables, in the order given. Views can be selected explicitly:

jt preview warehouse.duckdb --table products --table suppliers
jt preview warehouse.duckdb --table analytics.products
jt preview warehouse.duckdb --table product_names
jt preview inventory.sqlite --table 'odd table name'

An unqualified name must identify exactly one table or view. For duplicate names across DuckDB schemas, use schema.table. For names containing dots or quotes, copy the exact quoted table selector from jt tables, preserving the double quotes inside shell single quotes, for example --table '"main"."a.b"'. Unknown or ambiguous names produce an error listing available choices.

preview defaults to three rows total, and scan defaults to all rows. --limit N caps the total across selected tables; it is not a per-table limit. Use --table to preview a later table directly.

IDs, ordering, and caching

Primary keys supply entry IDs automatically, including composite keys. Rows with primary keys are ordered by those keys. For example, product 1 has ID "main"."products":key:[1]; supplier 1 has a different table-qualified ID.

Use --id-column sku to override the primary key. This column must exist in every selected table and contain nonempty scalar values unique within each table. The schema and table qualifier is retained. Duplicate IDs fail the run.

Without a usable primary key, IDs use row positions, such as "main"."products":row:1. This also covers views and SQLite rows with NULL primary-key components. Unkeyed row order is engine-defined; these positional IDs are not stable across database changes. Each result's source names the input, schema, table, and row position.

Caching uses the request's values, fields, guidance, exemplars, and model, just as for files. Reporting IDs and source paths do not affect the cache. Identical row requests can reuse assessments across tables and files. Schema definitions and table names are not added to model context; use guidance when that context matters.

SQL values in JSON

SQL NULL becomes JSON null. Strings, integers, finite floating-point values, and booleans retain their Python/JSON types. SQLite uses its stored value types; it does not infer dates or booleans from declared column types.

DuckDB values that JSON cannot represent directly use these conversions:

SQL value JSON representation
DECIMAL Exact decimal string, preserving scale
DATE, TIME, TIMESTAMP ISO-format string
UUID String
INTERVAL Python driver's duration string
BLOB (both engines) hex: followed by hexadecimal bytes
NaN, positive/negative infinity "nan", "inf", "-inf" strings
LIST, ARRAY, STRUCT Arrays or objects, recursively converted
MAP with non-string keys Array of {"key": ..., "value": ...} objects

JSON stored in a text column remains text; it is not parsed automatically.

Read-only behavior

Connections open read-only and use a transaction for a consistent snapshot. Rows are fetched incrementally; --limit, errors, and completed scans close the connection. The engine may still sort or buffer data to execute a query. SQLite reads committed WAL data; DuckDB may require a concurrent writer to close its connection before the file can be opened.

The input cannot also be used as the report or assessment cache, including through symlinks or hardlinks. DuckDB external file/network access is disabled; views that depend on external sources cannot be scanned. Files must be local, unencrypted, and readable by the installed engine version. Decompress .gz database files before using them. Missing files are never created by a scan.

The adapters use SQLite read-only URI mode and DuckDB's read-only connection option.

Python configuration

from jevotron import Config
from jevotron.databases import DuckDB

config = Config(
    parser=DuckDB(tables=["products"], id_column="sku", fields=["/name"]),
    guidance="Check product names.",
)

SQLite accepts the same arguments. As with other custom parser configs, put table selection in the config rather than combining it with CLI parser flags. Guidance-only configs work with automatic detection and --table.