Information Schema
information_schema is a reserved schema that exposes metadata about the tables, views, and columns registered in an Opteryx workspace's catalog. It is read from the catalog live at query time — not a fixed or generated snapshot — so it reflects whatever exists in the catalog at the moment the query runs.
Eight views are currently available: tables, columns, views, schemata, triggers, tasks, column_relationships, and grants.
SELECT *
FROM opteryx.information_schema.tables;Addressing
information_schema sits inside a workspace's own namespace, addressed the same three-part way as any other catalog table — <workspace>.information_schema.<view>:
SELECT *
FROM opteryx.information_schema.tables;
SELECT *
FROM opteryx.information_schema.columns;information_schema is only reachable through a catalog-backed workspace. It has no meaning against a plain filesystem/Parquet connection, and querying it there returns an unknown-relation error rather than an empty result.
information_schema.tables
One row per table or view in the workspace's catalog.
| Column | Type | Description |
|---|---|---|
table_catalog |
VARCHAR |
The workspace name |
table_schema |
VARCHAR |
The collection/namespace the table belongs to |
table_name |
VARCHAR |
The table or view name |
table_type |
VARCHAR |
BASE TABLE or VIEW |
table_sort_order |
VARCHAR |
The dataset's configured sort order |
snapshot_id |
INT64 |
The current snapshot's identifier |
snapshot_sequence_id |
INT64 |
The current snapshot's write sequence number |
table_updated_at |
TIMESTAMP |
When the current snapshot was committed |
table_file_count |
INT64 |
Data files held at the current snapshot |
table_bytes |
INT64 |
Total bytes held at the current snapshot |
table_record_count |
INT64 |
Records held at the current snapshot |
SELECT table_schema, table_name, table_type
FROM opteryx.information_schema.tables
ORDER BY table_schema, table_name;The statistics columns come from the table's current snapshot. Views have no snapshot, so
they report NULL for all of them — as does a table that has never been committed to.
Reading them costs one extra catalog round trip per table; project only table_type and the
name columns if you don't need them.
information_schema.columns
One row per column of every table in the workspace's catalog.
| Column | Type | Description |
|---|---|---|
table_catalog |
VARCHAR |
The workspace name |
table_schema |
VARCHAR |
The collection/namespace the table belongs to |
table_name |
VARCHAR |
The table name |
column_name |
VARCHAR |
The column name |
ordinal_position |
INTEGER |
The column's 1-based position in the table |
data_type |
VARCHAR |
The column's Opteryx type, e.g. VARCHAR, DECIMAL(10,2) |
is_nullable |
VARCHAR |
YES or NO |
SELECT column_name, data_type, is_nullable
FROM opteryx.information_schema.columns
WHERE table_schema = 'test'
AND table_name = 'planets'
ORDER BY ordinal_position;
Be Aware
information_schema.columnsonly covers tables — views are not included. A view's columns are only known once its query is planned, and that isn't done just to populate this listing. Useinformation_schema.viewsfor a view's SQL text.
information_schema.views
One row per view in the workspace's catalog, with its definition. This is a separate view
from tables, which lists views too but carries only their name and type — tables never
opens a view's document, views does.
| Column | Type | Description |
|---|---|---|
table_catalog |
VARCHAR |
The workspace name |
table_schema |
VARCHAR |
The collection/namespace the view belongs to |
table_name |
VARCHAR |
The view name |
view_definition |
VARCHAR |
The view's SQL text |
view_owner |
VARCHAR |
Who created the view |
view_updated_at |
TIMESTAMP |
When the view was last updated |
SELECT table_schema, table_name, view_definition
FROM opteryx.information_schema.views
WHERE table_schema = 'reporting';It does one catalog round trip per view found, so filter by table_schema/table_name
where you can.
information_schema.schemata
One row per collection in the workspace. Deliberately thin: information_schema is scoped
to a single workspace, so catalog_name is constant across every row, and the catalog
exposes no readable per-collection metadata beyond the name.
| Column | Type | Description |
|---|---|---|
catalog_name |
VARCHAR |
The workspace name |
schema_name |
VARCHAR |
The collection/namespace name |
SELECT schema_name
FROM opteryx.information_schema.schemata
ORDER BY schema_name;A collection is listed only if you can read at least one table or view inside it. The
per-row READ check the other views apply is per-table, so without this a collection you
have no access to anywhere would still have its existence disclosed here.
information_schema.triggers
One row per trigger in the workspace's catalog. A trigger is attached to a table and fires
when data is committed to it. It is one of two kinds: a task trigger, created with
CREATE TRIGGER or CREATE TASK ... ON,
which runs a task; or a refresh trigger,
created by
CREATE MATERIALIZED VIEW,
which re-runs a materialized view's query. Both kinds share these columns.
| Column | Type | Description |
|---|---|---|
trigger_catalog |
VARCHAR |
The workspace name |
trigger_collection |
VARCHAR |
The collection the trigger belongs to |
trigger_name |
VARCHAR |
The trigger name — as given to CREATE TRIGGER, or generated as refresh__<collection>__<view_name>__<suffix> for a refresh trigger |
event_object_table |
VARCHAR |
The source table the trigger is attached to, as collection.dataset |
action_kind |
VARCHAR |
task — the trigger executes a task; materialized_view_refresh — it refreshes a materialized view |
target |
VARCHAR |
What the trigger fires, fully qualified: the task it executes, or the materialized view it refreshes, depending on action_kind |
runs_as |
VARCHAR |
Whose identity an unattended run carries — the trigger's owner, not necessarily created_by. Moved with ALTER TRIGGER ... OWNER TO |
created_by |
VARCHAR |
Who created the trigger |
created_at |
TIMESTAMP |
When the trigger was created |
last_fired_at |
TIMESTAMP |
When the trigger last fired |
last_fired_status |
VARCHAR |
How the last firing went, including suspended and throttled for a firing that was refused — the place to look when a task has not run or a materialized view seems stale |
suspended_at |
TIMESTAMP |
When the trigger was paused with ALTER TRIGGER ... SUSPEND. NULL while it is live |
suspended_by |
VARCHAR |
Who paused it. NULL while it is live |
minimum_interval_seconds |
BIGINT |
The floor between two firings, set with ALTER TRIGGER ... SET MINIMUM INTERVAL TO. NULL for a trigger created before the setting existed, which fires on every commit; 0 where the floor was removed |
last_fired_status is the outcome of the most recent time a commit reached the
trigger, not of the work it started. enqueued means a job was submitted. throttled
means the commit landed inside the trigger's minimum interval and nothing was submitted,
which is the setting working, not a fault. suspended means the trigger is paused.
skipped-unread and superseded are deliberate skips — a view nobody has read, or a
task run already covered by a later one. egress-blocked, owner-missing and error
need someone to act.
SELECT trigger_name, event_object_table, action_kind, target, runs_as, last_fired_at, last_fired_status
FROM opteryx.information_schema.triggers;For the triggers on a single table, SHOW TRIGGERS FOR is the shorthand.
information_schema.column_relationships
One row per relationship declared with ALTER TABLE ... ADD CONSTRAINT — a record that a column holds values corresponding to a column of another table.
Nothing here is enforced. A write that breaks a relationship succeeds, and the engine never consults these rows when planning a query. They exist so that people and tools can see how the tables in a workspace fit together.
| Column | Type | Description |
|---|---|---|
constraint_catalog |
VARCHAR |
The workspace name |
constraint_collection |
VARCHAR |
The collection the declaring table belongs to |
constraint_name |
VARCHAR |
The name given to the declaration, and the handle DROP CONSTRAINT uses |
table_name |
VARCHAR |
The declaring table, as collection.dataset |
column_name |
VARCHAR |
Its column — the near end of the relationship |
referenced_table_name |
VARCHAR |
The referenced table, as collection.dataset |
referenced_column_name |
VARCHAR |
Its column — the far end |
relationship_kind |
VARCHAR |
maps — the columns hold corresponding values |
cardinality |
VARCHAR |
As declared, never derived from the data. A foreign key means many_to_one |
origin |
VARCHAR |
asserted — somebody wrote it down |
status |
VARCHAR |
active |
asserted_by |
VARCHAR |
Who declared it |
asserted_at |
TIMESTAMP |
When it was declared |
verified_at |
TIMESTAMP |
When the declaration was last checked against the data. Always NULL today — nothing checks |
SELECT table_name, column_name, referenced_table_name, referenced_column_name
FROM opteryx.information_schema.column_relationships;A row is listed against the table the constraint was declared on. To find what points
at a table, filter on referenced_table_name — that reads every declaration in the
workspace rather than one table's, so it is the slower direction.
information_schema.grants
The workspace's access, as a relation. One row per object and policy that reaches it —
for the workspace itself, each collection, and each table and view, every stored access
policy whose pattern covers it. SHOW GRANTS ON and SHOW EFFECTIVE GRANTS ON answer the
same questions one object at a time, as statements; this is both answers for the whole
workspace, so a client can read access live and narrow it with WHERE.
| Column | Type | Description |
|---|---|---|
grant_catalog |
VARCHAR |
The workspace name |
grant_collection |
VARCHAR |
The object's collection. NULL on the workspace row |
object_kind |
VARCHAR |
workspace, collection or dataset — the kinds the grant statements name |
object_name |
VARCHAR |
The object, fully qualified: w, w.c or w.c.d — what GRANT ... ON <kind> <object> takes |
grantee |
VARCHAR |
Who holds the policy |
role |
VARCHAR |
owner, writer or reader |
pattern |
VARCHAR |
The policy's own pattern — the thing to REVOKE to remove this access |
level |
VARCHAR |
The level that pattern addresses: workspace, collection or dataset |
origin |
VARCHAR |
explicit — the policy is stored at this object; inherited — it covers the object from the collection or workspace above |
origin is the column the view exists for. The explicit rows are what SHOW GRANTS ON
reports for an object, and what a GRANT or REVOKE there acts on. The inherited rows
are what SHOW EFFECTIVE GRANTS ON adds — a dataset with nothing granted on it directly is
still reachable by the workspace owner, and this says so, naming the policy that does it.
One row per covering policy, never one per person: a user who reaches a dataset through both a collection grant and a workspace grant gets two rows, because either policy alone keeps the access in place and an administrator has to know which to change.
-- Everything stored in the workspace: every policy, once, at its own object.
SELECT object_name, grantee, role
FROM opteryx.information_schema.grants
WHERE origin = 'explicit';
-- Everyone who can reach one dataset, and through which policy.
SELECT grantee, role, pattern, level, origin
FROM opteryx.information_schema.grants
WHERE object_name = 'opteryx.sales.orders';Every stored policy appears as explicit at its own pattern exactly once, whether or not
the catalog still holds what it names — a grant on a dataset that has since been dropped
is listed rather than lost, which is exactly the grant a listing needs to be complete for.
The workspace row lists the policies that cover the workspace as an object (w.*); the
whole workspace's policies are the explicit rows.
Filtering on object_name with = reads that one object without walking the catalog at
all — the read a dataset page makes. Whatever the filter, the catalog is listed once and the
policy store read once.
Permissions
information_schema.tables, information_schema.columns and information_schema.views only ever show tables and views the querying identity has read permission on. A table that isn't readable to you simply doesn't appear in the results — it isn't hidden with an error, and it isn't visible with its schema exposed. This applies row-by-row, so a query against information_schema always succeeds even if you have no access to any tables in the workspace; it just returns no rows.
information_schema.triggers follows the same rule against the trigger's source table, and information_schema.schemata against the collection's contents — a collection appears only if something inside it is readable to you.
information_schema.column_relationships requires both tables to be readable, not just the one the constraint was declared on. A row names the other table's collection, dataset and column, so showing it to someone who can read only one side would disclose the shape of data they have no access to. A relationship you can half-see does not appear at all.
A missing execution context denies everything rather than falling back to showing all rows.
information_schema.grants is gated differently, because it is not about data. A row
describes who can reach an object, which is strictly more sensitive than the object's
existence, so a row is shown only where the querying identity could administer the
object — owner authority covering it, the same gate SHOW GRANTS ON holds. A collection
owner sees their collection and what is under it; everything else is simply absent, not
refused.
Limitations
This is an early implementation. Known gaps:
tables,columns,views,schemata,triggers,tasks,column_relationshipsandgrantsare implemented.routinesis not.information_schema.columnsandinformation_schema.viewsdo one catalog round trip per table or view found. Predicate pushdown covers only equality andINon the enumeration key columns (table_catalog,table_schema,table_name, andtable_typeontables) — those are known before any round trip, so filtering on them skips the lookups entirely. Every other predicate is applied after the fact, so an unfiltered query against a workspace with a very large number of tables is proportionally slower.