Inspect
sq inspect
inspects metadata (schema/structure, tables, columns) for a source,
or for an individual table. When used with --json, the output of sq inspect can
be fed into other tools such as jq
to enable complex data pipelines.
Let’s start off with a single source, a Postgres Sakila database:
# Start the Postgres container
$ docker run -d -p 5432:5432 sakiladb/postgres:12
# Add the source
$ sq add postgres://sakila:p_ssW0rd@localhost/sakila --handle @sakila_pg
@sakila_pg postgres sakila@localhost/sakila
Inspect source
Use sq inspect @sakila_pg to inspect the source.
You can also use sq inspect with stdin, e.g.:
$ cat actor.csv | sq inspect
However, note that stdin sources can’t take advantage of ingest caching
, because
the stdin pipe is “anonymous”, and sq can’t do a cache lookup for it. If you’re going to
repeatedly inspect the same stdin data, you should probably just sq add
it.
This output includes the source metadata, and the schema structure (tables, columns, etc.).
--text (default)
$ sq inspect @sakila_pg

--verbose
To see more detail, use the --verbose (-v) flag with the --text format.

--yaml
To see the full output, use the --yaml (-y) flag. YAML has the advantage
of being reasonably human-readable.

sq to introspect the schema.--json
The --json (-j) format renders the same content as --yaml, but is more
suited for use with other tools, such as jq
.

Here’s an example of using sq with jq to list all table names:
$ sq inspect -j | jq -r '.tables[] | .name'

See more examples in the cookbook .
Source overview
Sometimes you don’t need the full schema, but still want to view the source
metadata. Use the --overview (-O) mode to see just the top-level metadata.
This excludes the schema structure, and is also much faster to complete.

Well, that’s not a lot of detail. The --yaml output is more useful:

The --json format produces similar output.
Database properties
The --dbprops mode displays the underlying database’s properties, server config,
and the like.
$ sq inspect @sakila_pg --dbprops

Use --dbprops with --yaml or --json to get the properties in machine-readable
format. Note that while the returned structure is generally a set of key-value
pairs, the specifics can vary significantly from one driver type to another.
Here’s --dbprops from a SQLite
database (in --yaml format):

Catalogs
The --catalogs mode lists the catalogs
(databases)
available in the source.

Schemata
Like --catalogs, the --schemata mode lists the schemas
available in the source.

To list the schemas in a specific catalog, supply CATALOG. to the
--src.schema
flag:
# List the schemas in the "inventory" catalog.
$ sq inspect @sakila/pg12 --schemata --src.schema inventory.
Inspect table
In additional to inspecting a source, you can drill down on a specific table.
$ sq inspect @sakila_pg.actor

Use --verbose mode for more detail:

And, as you might expect, you can also see the output in --json and --yaml formats.

--overview and --dbprops flags apply only to inspecting sources,
not tables.Foreign-key relationships
sq inspect reports foreign-key constraints for any SQL source that
supports them (SQLite, Postgres, MySQL, SQL Server, Oracle, DuckDB).
The relationships appear under each table’s fk object — the same FK
shows up once under its owning table’s fk.outgoing and once under
the referenced table’s fk.incoming:
tables[].fk.outgoing— constraints declared on this table (its outgoing edges). Tells you what rows in this table depend on.tables[].fk.incoming— constraints declared on other tables whose referenced side is this table (its incoming edges). Tells you what depends on rows in this table. Useful for “blast radius” questions (“if I delete this row, what else breaks?”) and for visualization tools that want to render the schema as a directed graph without walking every table to discover incoming edges.
Composite foreign keys are supported by every driver. Cross-schema
and cross-catalog references are reported by Postgres, MySQL, and
SQL Server. Oracle reports cross-schema references via ref_schema
for outgoing FKs only — fk.incoming is scoped to the current
user’s schema, so FKs from tables in other schemas are not surfaced.
DuckDB’s duckdb_constraints() view does not expose the referenced
table’s schema, so a DuckDB FK that crosses schemas is reported as
same-schema.
The on_delete and on_update referential actions are surfaced
where the driver reports them (Oracle exposes on_delete only;
DuckDB’s duckdb_constraints() view doesn’t expose either action, so
both fields are left empty for DuckDB sources).
For example, to list every parent → child relationship in the Sakila schema:
$ sq inspect -j @sakila_pg | jq -r '
.tables[]
| .name as $child
| .fk.outgoing[]?
| "\($child).\(.columns | join(",")) -> \(.ref_table).\(.ref_columns | join(","))"'
film.original_language_id -> language.language_id
film.language_id -> language.language_id
film_actor.actor_id -> actor.actor_id
film_actor.film_id -> film.film_id
...
Filtering composites
Composite constraints (FK, unique, or index) are represented as a
single entry whose columns slice carries every participating column
in declaration order — single-column constraints are just the
one-element case. There’s no separate composite flag because the
arity is the slice length, so (.columns | length) > 1 is the
idiomatic filter:
# Composite foreign keys only
$ sq inspect -j @sakila_pg | jq -r '
.tables[] | .fk.outgoing[]?
| select((.columns | length) > 1)
| "\(.table)(\(.columns | join(","))) -> \(.ref_table)(\(.ref_columns | join(",")))"'
The same pattern works for unique_constraints[] and indexes[].
The --verbose text output also gains an FK column listing the
referenced table and columns for each FK column.
Unique constraints and indexes
In addition to foreign keys, each table reports its UNIQUE constraints and the physical indexes that back it:
tables[].unique_constraints— UNIQUE declarations (inline or viaALTER TABLE ADD CONSTRAINT). Primary keys are reported separately viacolumns[].primary_keyand are not repeated here. Composite members appear in declaration order.tables[].indexes— physical indexes, including the implicit PK-backing index, unique-constraint-backing indexes, and any user-declaredCREATE INDEXentries. Each entry carriesunique,primary, and a driver-specifictype(e.g.BTREE,HASH,NONCLUSTERED). DuckDB is the exception: itsduckdb_indexes()catalog only lists explicitCREATE INDEXdefinitions, so PK-backing and UNIQUE-backing indexes don’t appear there. The PK and UNIQUE information is still available viacolumns[].primary_keyandunique_constraints.
For example, list non-unique indexes per table:
$ sq inspect -j @sakila_pg | jq -r '
.tables[]
| .name as $t
| .indexes[]?
| select(.unique == false)
| "\($t).\(.name) (\(.columns | join(\",\"))) [\(.type)]"'
How verbose text marks redundant index entries
The INDEXES column of sq inspect --verbose text output applies
two display rules so the column reads cleanly without losing
information:
- PK-backing indexes are dropped entirely. The
PKcolumn already marks the participating columns; repeating the implicit index name adds nothing. - UNIQUE-constraint-backing indexes are shown muted, with the
index name wrapped in parentheses and rendered in a subdued style
(italic + faint, on color terminals). The UC name also appears
under
UNIQUE CONSTRAINTS, so the parens signal “this is the implicit backing index” while keeping the name visible. The match is by column-set, not name, so SQLite’s auto-generatedsqlite_autoindex_*entries pair up with the right UC.
NAME TYPE ROWS COLS NAME TYPE PK FK INDEXES UNIQUE CONSTRAINTS
demo_uc table 0 5 id int4 pk
email varchar (demo_uc_email_key), idx_solo_unique demo_uc_email_key
first_name varchar (uniq_full_name) uniq_full_name
last_name varchar (uniq_full_name) uniq_full_name
nickname varchar idx_demo_nickname
A user-declared CREATE UNIQUE INDEX that doesn’t back any formal
constraint (e.g. idx_solo_unique above) renders unmuted, alongside
the parenthesized constraint-backing entry.
These display rules apply only to the verbose text renderer. The
JSON and YAML formats always emit the full tables[].indexes slice
— every physical index, including PK- and UC-backing ones — so
tooling consuming the machine-readable forms sees the complete
picture.
Override active schema
By default, sq inspect uses the active schema
for the source. You can override the active schema (and catalog)
using the --src.schema
flag. See the sources
section
for a fuller explanation of --src.schema, but here’s a quick example of
inspecting Postgres’s information_schema schema:
$ sq inspect @sakila/pg12 --src.schema sakila.information_schema
SOURCE DRIVER NAME FQ NAME SIZE TABLES VIEWS LOCATION
@sakila/pg12 postgres sakila sakila.information_schema 16.6MB 7 61 postgres://sakila:xxxxx@192.168.50.132/sakila
NAME TYPE ROWS COLS
sql_features table 716 feature_id, feature_name, sub_feature_id, sub_feature_name, is_supported, is_verified_by, comments
sql_implementation_info table 12 implementation_info_id, implementation_info_name, integer_value, character_value, comments
sql_languages table 4 sql_language_source, sql_language_year, sql_language_conformance, sql_language_integrity, sql_language_implementation, sql_language_binding_style, sql_language_programming_language
sql_packages table 10 feature_id, feature_name, is_supported, is_verified_by, comments
sql_parts table 9 feature_id, feature_name, is_supported, is_verified_by, comments
sql_sizing table 23 sizing_id, sizing_name, supported_value, comments
sql_sizing_profiles table 0 sizing_id, sizing_name, profile_id, required_value, comments
_pg_foreign_data_wrappers view 0 oid, fdwowner, fdwoptions, foreign_data_wrapper_catalog, foreign_da