Skip to content
Discord Get Started

System Catalog

DB9 implements PostgreSQL-compatible system catalog views for introspecting database objects.

ViewDescription
pg_tablesUser tables
pg_viewsUser views
pg_classTables, indexes, sequences, views
pg_attributeTable columns
pg_attrdefColumn default values
pg_namespaceSchemas
pg_typeData types
pg_indexIndex metadata
pg_indexesIndex definitions (indexdef)
pg_constraintConstraints (PK, FK, CHECK, UNIQUE, NOT NULL)
pg_procFunctions and procedures
pg_triggerTriggers — no tgtype column, and pg_get_triggerdef() does not exist; read the declared timing and row/statement level from information_schema.triggers (action_timing, action_orientation) instead
pg_enumEnum type values
pg_sequenceSequence metadata
pg_extensionInstalled extensions
pg_collationCollations — built-ins only; collations you create work but never appear
pg_roles / pg_userUser and role definitions
pg_auth_membersRole membership: built-in rows making pg_monitor a member of pg_read_all_settings, pg_read_all_stats and pg_stat_scan_tables, plus any GRANT <role> TO <role> you issue. REVOKE removes the row again
pg_db_role_settingPer-role parameter defaults written by ALTER ROLE … [IN DATABASE …] SET (also shown in pg_roles.rolconfig); applied to new sessions for that role
pg_databaseDatabases
pg_descriptionObject descriptions/comments
pg_dependDependency tracking
pg_amAccess methods
pg_stat_user_tablesTable statistics
pg_inheritsTable inheritance
pg_rangeRange type metadata
pg_opclassOperator classes
pg_policyRow-level security policies
pg_policiesReadable RLS policy view (qual, with_check)
pg_settingsRuntime parameters, including DB9’s db9.* tuning settings
pg_timezone_namesTime zone names, abbreviations and UTC offsets (name, abbrev, utc_offset, is_dst)
pg_rewriteRewrite rules — populated with the _RETURN rule backing each view
pg_aggregateAggregate functions. Exposes only aggfnoid; the transition-function columns are absent
pg_publication_relPublication table membership, written by ALTER PUBLICATION ... ADD/DROP/SET TABLE. The rows persist but nothing is replicated

The following relations resolve but are always empty — they exist so that catalog-introspecting tools and ORMs do not error, not because the feature behind them is implemented:

RelationNote
pg_castNo user-defined casts
pg_default_aclNo default privilege rules
pg_shdescriptionNo shared-object comments
pg_publication_namespaceALTER PUBLICATION ... ADD TABLES IN SCHEMA is rejected, so this is never populated
pg_replication_slots19 columns, including conflicting plus the PostgreSQL 17 additions failover/synced. pg_create_logical_replication_slot() exists in the FROM clause only — as SELECT pg_create_logical_replication_slot(...) it reports 42883 does not exist. Where it does resolve it still cannot create a slot: wal2json ends at 55000 logical replication is not provisioned for this database, other plugins at 22023. See the full call table. This view stays empty either way
pg_statistic_extNo extended statistics objects
ViewDescription
tablesTables
columnsTable columns (includes is_generated, generation_expression)
schemataSchemas
sequencesSequences
routinesFunctions and procedures
table_constraintsPRIMARY KEY, FOREIGN KEY, CHECK and UNIQUE constraints (never NOT NULL — see below)
key_column_usagePRIMARY/FOREIGN KEY columns
referential_constraintsForeign key constraints
check_constraintsCHECK constraints
constraint_column_usageColumn constraint usage
table_privilegesTable access privileges
triggersTriggers, with the declared action_timing (BEFORE / AFTER) and action_orientation (ROW / STATEMENT)

These twelve views are the complete information_schema surface. Notably, information_schema.views is not implemented — it fails with relation "views" does not exist (42P01). List views through pg_views, or filter information_schema.tables on table_type = 'VIEW'.

ViewDescription
cron.jobScheduled cron jobs
cron.job_run_detailsJob execution history
cron.running_jobsCurrently executing jobs (superuser only) — see pg_cron

Practical queries for introspecting your database. Replace 'public' and 'my_table' with your actual schema and table names.

Returns all user-defined tables in a schema with their owner and row estimate.

SQL
SELECT
tablename AS table,
tableowner AS owner,
hasindexes AS indexed,
hasrules AS has_rules,
hastriggers AS has_triggers
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY tablename;
-- Alternative using information_schema (more portable):
SELECT
table_name,
table_type
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_type = 'BASE TABLE'
ORDER BY table_name;

Returns all columns with their data type, nullability, and default value.

SQL
SELECT
column_name,
data_type,
character_maximum_length,
is_nullable,
column_default,
ordinal_position
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'my_table'
ORDER BY ordinal_position;
-- Using pg_attribute for lower-level detail (includes system columns):
SELECT
a.attname AS column,
pg_catalog.format_type(a.atttypid, a.atttypmod) AS type,
a.attnotnull AS not_null,
a.attnum AS position
FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public'
AND c.relname = 'my_table'
AND a.attnum > 0 -- exclude system columns
AND NOT a.attisdropped
ORDER BY a.attnum;

Lists every index on a table, including the columns covered and whether it is unique or primary.

SQL
SELECT
indexname AS index,
indexdef AS definition
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename = 'my_table'
ORDER BY indexname;
-- More detailed view via pg_index + pg_class:
SELECT
i.relname AS index,
ix.indisunique AS unique,
ix.indisprimary AS primary,
array_agg(a.attname ORDER BY a.attnum) AS columns
FROM pg_index ix
JOIN pg_class t ON t.oid = ix.indrelid
JOIN pg_class i ON i.oid = ix.indexrelid
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'my_table'
GROUP BY i.relname, ix.indisunique, ix.indisprimary
ORDER BY i.relname;

The information_schema query returns the PRIMARY KEY, UNIQUE, CHECK and FOREIGN KEY constraints on a table; the pg_constraint variant additionally returns NOT NULL.

SQL
SELECT
tc.constraint_name,
tc.constraint_type,
kcu.column_name,
cc.check_clause
FROM information_schema.table_constraints tc
LEFT JOIN information_schema.key_column_usage kcu
ON kcu.constraint_name = tc.constraint_name
AND kcu.table_schema = tc.table_schema
LEFT JOIN information_schema.check_constraints cc
ON cc.constraint_name = tc.constraint_name
AND cc.constraint_schema = tc.constraint_schema
WHERE tc.table_schema = 'public'
AND tc.table_name = 'my_table'
ORDER BY tc.constraint_type, tc.constraint_name;
-- Concise version using pg_constraint:
SELECT
conname AS constraint,
contype AS type, -- p=PK, u=UNIQUE, c=CHECK, f=FK, n=NOT NULL
pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE conrelid = 'public.my_table'::regclass
ORDER BY contype, conname;

Lists all row-level security policies on a table, including which roles they apply to and their USING / WITH CHECK expressions.

SQL
SELECT
policyname AS policy,
cmd AS command, -- ALL, SELECT, INSERT, UPDATE, DELETE
roles AS applies_to,
qual AS using_expr,
with_check AS check_expr
FROM pg_policies
WHERE schemaname = 'public'
AND tablename = 'my_table'
ORDER BY policyname;
-- Check if RLS is enabled on the table:
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class
WHERE oid = 'public.my_table'::regclass;

Returns all foreign key relationships — both outbound (this table references another) and inbound (other tables reference this one).

SQL
-- Outbound: FK constraints on my_table
SELECT
tc.constraint_name,
kcu.column_name AS fk_column,
ccu.table_name AS references_table,
ccu.column_name AS references_column,
rc.update_rule,
rc.delete_rule
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON kcu.constraint_name = tc.constraint_name
AND kcu.table_schema = tc.table_schema
JOIN information_schema.referential_constraints rc ON rc.constraint_name = tc.constraint_name
AND rc.constraint_schema = tc.constraint_schema
JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name = rc.unique_constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_schema = 'public'
AND tc.table_name = 'my_table'
ORDER BY tc.constraint_name;
-- All FK constraints in the schema (both directions):
SELECT
c.conrelid::regclass::text AS from_table,
c.confrelid::regclass::text AS to_table,
c.conname AS constraint,
pg_get_constraintdef(c.oid) AS definition
FROM pg_constraint c
JOIN pg_class t ON t.oid = c.conrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE c.contype = 'f'
AND n.nspname = 'public'
ORDER BY from_table, c.conname;

Returns all user-defined functions in a schema with their return types.

SQL
SELECT
routine_name AS function,
routine_type AS type,
data_type AS return_type
FROM information_schema.routines
WHERE routine_schema = 'public'
ORDER BY routine_name;
-- More detail via pg_proc:
SELECT
p.proname AS function,
format_type(p.prorettype, NULL) AS return_type,
p.prokind AS kind -- 'f' = function, 'p' = procedure, 'w' = window
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname = 'public'
ORDER BY p.proname;

Returns disk usage for every table, largest first. DB9 reports sizes through the storage accounting virtual tables rather than the PostgreSQL size functions.

SQL
SELECT
table_name,
data_bytes,
index_bytes,
total_bytes,
round(total_bytes::numeric / 1024 / 1024, 2) AS total_mb
FROM _DB9_SYS_TABLE_STORAGE_STATS
ORDER BY total_bytes DESC;
-- Database-level totals:
SELECT
database_name,
total_bytes,
round(total_bytes::numeric / 1024 / 1024, 2) AS total_mb,
scanned_at
FROM _DB9_SYS_STORAGE_STATS;