Shape / inspect · All operations

List keys, constraints and indexes

A variant of List columns and types

List the keys, constraints, indexes, partitions, dependencies or relationships declared on tables.

Status

In arcform

Runs as a SQL step

arcform carries it as a sql: step today.

The DuckDB step

List the keys, constraints, indexes, partitions, dependencies and relationships declared on tables

SQL
CREATE TABLE dept (id INTEGER PRIMARY KEY, name VARCHAR UNIQUE);
CREATE TABLE emp (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES dept (id), salary INTEGER CHECK (salary > 0), email VARCHAR NOT NULL);
CREATE INDEX emp_dept ON emp (dept_id);
CREATE SEQUENCE badge_numbers;
CREATE TABLE badges (badge INTEGER DEFAULT nextval('badge_numbers'));
-- partitions of a table kept as hive-style directories
COPY (SELECT i % 3 AS region, i FROM range(9) t(i)) TO '<your_scratch_dir>/inspect_parts' (FORMAT parquet, PARTITION_BY (region));
CREATE TEMP TABLE constraints AS
SELECT table_name, constraint_type, constraint_column_names AS columns, expression
FROM duckdb_constraints() WHERE table_name IN ('dept', 'emp');
CREATE TEMP TABLE keys AS SELECT table_name, constraint_type, columns FROM constraints WHERE constraint_type IN ('PRIMARY KEY', 'UNIQUE');
CREATE TEMP TABLE relationships AS
SELECT table_name AS child, constraint_column_names AS child_columns, referenced_table AS parent, referenced_column_names AS parent_columns
FROM duckdb_constraints() WHERE constraint_type = 'FOREIGN KEY';
CREATE TEMP TABLE indexes AS SELECT table_name, index_name, is_unique, expressions FROM duckdb_indexes();
CREATE TEMP TABLE partitions AS
SELECT DISTINCT regexp_extract(file, 'region=([^/]+)', 1) AS region FROM glob('<your_scratch_dir>/inspect_parts/**/*.parquet');
-- each dependency row names what is depended on and what depends on it
CREATE TEMP TABLE objects AS
SELECT table_oid AS oid, table_name AS name FROM duckdb_tables() UNION ALL
SELECT index_oid, index_name FROM duckdb_indexes() UNION ALL
SELECT sequence_oid, sequence_name FROM duckdb_sequences();
CREATE TEMP TABLE dependencies AS
SELECT a.name AS depended_on, b.name AS dependent
FROM duckdb_dependencies() d JOIN objects a ON a.oid = d.objid JOIN objects b ON b.oid = d.refobjid;
SELECT * FROM constraints ORDER BY ALL;
SELECT * FROM dependencies ORDER BY ALL;
SELECT CASE WHEN ((SELECT count(*) FROM constraints) = 8 AND (SELECT list((table_name, constraint_type, columns) ORDER BY table_name, constraint_type) FROM keys) = [('dept', 'PRIMARY KEY', ['id']), ('dept', 'UNIQUE', ['name']), ('emp', 'PRIMARY KEY', ['id'])] AND (SELECT expression FROM constraints WHERE constraint_type = 'CHECK') = '(salary > 0)' AND (SELECT list(columns ORDER BY columns) FROM constraints WHERE constraint_type = 'NOT NULL' AND table_name = 'emp') = [['email'], ['id']] AND (SELECT list((child, child_columns, parent, parent_columns)) FROM relationships) = [('emp', ['dept_id'], 'dept', ['id'])] AND (SELECT list((table_name, index_name, is_unique, expressions)) FROM indexes) = [('emp', 'emp_dept', false, ['dept_id'])] AND (SELECT list(region ORDER BY region) FROM partitions) = ['0', '1', '2'] AND (SELECT list((depended_on, dependent) ORDER BY depended_on, dependent) FROM dependencies) = [('badge_numbers', 'badges'), ('dept', 'emp'), ('emp', 'emp_dept')]) THEN 'as expected' ELSE error('the declared keys, constraints, foreign key, index, partitions and dependencies were not the expected rows') END AS verdict;

Replace <your_scratch_dir> with a directory of your own that you can write to before running the statement.

  • The statement writes inspect_parts itself, in <your_scratch_dir>.
  • It needs nothing installed beyond DuckDB itself.
  • The statement checks its own result and raises an error when it is wrong: “the declared keys, constraints, foreign key, index, partitions and dependencies were not the expected rows”.
  • It passed on DuckDB v2.0.0 (Cyanoptera) d54cbbe3df.

What other tools call it

Meridian's classification; an independent blind rating agreed on 95.7% of 324 rows (variant, version 3).

DuckDB (3)
  • duckdb_constraintsread 2026-09-27

    duckdb_constraints table function

  • duckdb_dependenciesread 2026-09-27

    duckdb_dependencies table function

  • duckdb_indexesread 2026-09-27

    duckdb_indexes table function

Power Query M (4)
subtoken (1)
  • pragma_hnsw_index_inforead 2026-09-27

    Table function listing each HNSW index: table, metric, dimensions, count, memory, levels

Background

In DuckDB, duckdb_constraints() gives metadata about constraints: each one's table, type and columns, and any table it references.[1] In DuckDB, duckdb_indexes() covers only secondary indexes made by CREATE INDEX, and the keys and UNIQUE constraints appear in duckdb_constraints().[1] In DuckDB, information_schema.table_constraints and referential_constraints are a more standardised view of constraints, built on duckdb_constraints().[1]

When to use it

Snowflake does not enforce PRIMARY KEY, UNIQUE and FOREIGN KEY constraints on standard tables, while NOT NULL and CHECK constraints are enforced.[2] In Snowflake, unenforced constraints are still useful as metadata, since business intelligence (BI) and data modelling tools can read them to discover keys and relationships between tables.[2] BigQuery does not enforce primary and foreign key constraints, so whoever declares one must ensure the data conforms to it.[3] Read a key that a warehouse lists as a statement of intent, and test the rows for duplicates or orphans before relying on it in a join. (Meridian's view)

In PostgreSQL, the SQL standard requires constraint names to be unique within a schema but PostgreSQL does not enforce this, so a constraint query expecting one row can return several.[4]

The method

Codd's twelve rules, proposed by Edgar F. Codd, include Rule 10, which asks that integrity constraints be definable in the relational data sublanguage and storable in the catalog, not in application programs.[5] In PostgreSQL, a foreign key constraint specifies that the values in a column or group of columns must match the values appearing in some row of another table.[6] In PostgreSQL, adding a primary key automatically creates a unique B-tree index on its columns and forces them to be marked NOT NULL.[6]

In DuckDB, a primary key or unique constraint defines a column, or set of columns, that is a unique identifier for a row in the table.[7] In DuckDB, a min-max index, also known as a zonemap, is created automatically for columns of all general-purpose data types.[8] In DuckDB, the constraint_text column of duckdb_constraints() expresses the constraint as a SQL phrase, not necessarily a complete or syntactically valid data definition language (DDL) statement.[1] In DuckDB, duckdb_dependencies() records dependencies between objects, each of them either regular or automatic.[1]