Skip to main content

uqa_sql/catalog/
mod.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! `PostgreSQL` 18 row types for the virtual system-catalog relations implemented by the engine.
8
9use crate::ast::ColumnType;
10
11pub mod expression_text;
12pub mod oids;
13pub mod resolution;
14pub mod result_type;
15pub mod type_metadata;
16
17/// Namespace of the Apache AGE catalog relations, types, and functions.
18pub const AG_CATALOG_SCHEMA: &str = "ag_catalog";
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum VirtualRelation {
22    InformationSchemaCatalogName,
23    InformationSchemata,
24    InformationTables,
25    InformationColumns,
26    InformationColumnPrivileges,
27    InformationRoleColumnGrants,
28    InformationViews,
29    InformationRoutines,
30    InformationSequences,
31    InformationTableConstraints,
32    InformationKeyColumnUsage,
33    PgNamespace,
34    PgClass,
35    PgInherits,
36    PgPartitionedTable,
37    PgAttribute,
38    PgAttrdef,
39    PgConstraint,
40    PgIndex,
41    PgTrigger,
42    PgRewrite,
43    PgRules,
44    PgTables,
45    PgViews,
46    PgIndexes,
47    PgType,
48    PgRange,
49    PgProc,
50    PgDatabase,
51    PgAuthMembers,
52    PgRoles,
53    PgUser,
54    PgSettings,
55    PgPreparedStatements,
56    PgDescription,
57    PgMatviews,
58    PgSequences,
59    AgGraph,
60    AgLabel,
61}
62
63/// Resolve a relation reference to one of the engine's virtual catalog
64/// relations. `information_schema` and `pg_catalog` names resolve
65/// qualified or bare because `PostgreSQL` always searches `pg_catalog`;
66/// the AGE relations resolve bare only while `ag_catalog` is on the
67/// session `search_path`, exactly like the extension's schema.
68pub fn resolve_virtual_relation(search_path: &[String], name: &str) -> Option<VirtualRelation> {
69    let lower = name.to_ascii_lowercase();
70    if let Some(local) = lower.strip_prefix("ag_catalog.") {
71        return resolve_ag_catalog_relation(local);
72    }
73    if !lower.contains('.') && search_path.iter().any(|schema| schema == AG_CATALOG_SCHEMA) {
74        if let Some(relation) = resolve_ag_catalog_relation(&lower) {
75            return Some(relation);
76        }
77    }
78    let is_information_schema = lower.starts_with("information_schema.");
79    let is_pg_catalog = lower.starts_with("pg_catalog.");
80    let stripped = lower
81        .strip_prefix("information_schema.")
82        .or_else(|| lower.strip_prefix("pg_catalog."))
83        .unwrap_or(&lower);
84    match (is_information_schema, is_pg_catalog, stripped) {
85        (true, _, "information_schema_catalog_name") => {
86            Some(VirtualRelation::InformationSchemaCatalogName)
87        }
88        (true, _, "schemata") => Some(VirtualRelation::InformationSchemata),
89        (true, _, "tables") => Some(VirtualRelation::InformationTables),
90        (true, _, "columns") => Some(VirtualRelation::InformationColumns),
91        (true, _, "column_privileges") => Some(VirtualRelation::InformationColumnPrivileges),
92        (true, _, "role_column_grants") => Some(VirtualRelation::InformationRoleColumnGrants),
93        (true, _, "views") => Some(VirtualRelation::InformationViews),
94        (true, _, "routines") => Some(VirtualRelation::InformationRoutines),
95        (true, _, "sequences") => Some(VirtualRelation::InformationSequences),
96        (true, _, "table_constraints") => Some(VirtualRelation::InformationTableConstraints),
97        (true, _, "key_column_usage") => Some(VirtualRelation::InformationKeyColumnUsage),
98        (_, true, "pg_namespace") | (false, false, "pg_namespace") => {
99            Some(VirtualRelation::PgNamespace)
100        }
101        (_, true, "pg_class") | (false, false, "pg_class") => Some(VirtualRelation::PgClass),
102        (_, true, "pg_inherits") | (false, false, "pg_inherits") => {
103            Some(VirtualRelation::PgInherits)
104        }
105        (_, true, "pg_partitioned_table") | (false, false, "pg_partitioned_table") => {
106            Some(VirtualRelation::PgPartitionedTable)
107        }
108        (_, true, "pg_attribute") | (false, false, "pg_attribute") => {
109            Some(VirtualRelation::PgAttribute)
110        }
111        (_, true, "pg_attrdef") | (false, false, "pg_attrdef") => Some(VirtualRelation::PgAttrdef),
112        (_, true, "pg_constraint") | (false, false, "pg_constraint") => {
113            Some(VirtualRelation::PgConstraint)
114        }
115        (_, true, "pg_index") | (false, false, "pg_index") => Some(VirtualRelation::PgIndex),
116        (_, true, "pg_trigger") | (false, false, "pg_trigger") => Some(VirtualRelation::PgTrigger),
117        (_, true, "pg_rewrite") | (false, false, "pg_rewrite") => Some(VirtualRelation::PgRewrite),
118        (_, true, "pg_rules") | (false, false, "pg_rules") => Some(VirtualRelation::PgRules),
119        (_, true, "pg_tables") | (false, false, "pg_tables") => Some(VirtualRelation::PgTables),
120        (_, true, "pg_views") | (false, false, "pg_views") => Some(VirtualRelation::PgViews),
121        (_, true, "pg_indexes") | (false, false, "pg_indexes") => Some(VirtualRelation::PgIndexes),
122        (_, true, "pg_type") | (false, false, "pg_type") => Some(VirtualRelation::PgType),
123        (_, true, "pg_range") | (false, false, "pg_range") => Some(VirtualRelation::PgRange),
124        (_, true, "pg_proc") | (false, false, "pg_proc") => Some(VirtualRelation::PgProc),
125        (_, true, "pg_database") | (false, false, "pg_database") => {
126            Some(VirtualRelation::PgDatabase)
127        }
128        (_, true, "pg_auth_members") | (false, false, "pg_auth_members") => {
129            Some(VirtualRelation::PgAuthMembers)
130        }
131        (_, true, "pg_roles") | (false, false, "pg_roles") => Some(VirtualRelation::PgRoles),
132        (_, true, "pg_user") | (false, false, "pg_user") => Some(VirtualRelation::PgUser),
133        (_, true, "pg_settings") | (false, false, "pg_settings") => {
134            Some(VirtualRelation::PgSettings)
135        }
136        (_, true, "pg_prepared_statements") | (false, false, "pg_prepared_statements") => {
137            Some(VirtualRelation::PgPreparedStatements)
138        }
139        (_, true, "pg_description") | (false, false, "pg_description") => {
140            Some(VirtualRelation::PgDescription)
141        }
142        (_, true, "pg_matviews") | (false, false, "pg_matviews") => {
143            Some(VirtualRelation::PgMatviews)
144        }
145        (_, true, "pg_sequences") | (false, false, "pg_sequences") => {
146            Some(VirtualRelation::PgSequences)
147        }
148        _ => None,
149    }
150}
151
152fn resolve_ag_catalog_relation(local: &str) -> Option<VirtualRelation> {
153    match local {
154        "ag_graph" => Some(VirtualRelation::AgGraph),
155        "ag_label" => Some(VirtualRelation::AgLabel),
156        _ => None,
157    }
158}
159
160impl VirtualRelation {
161    pub const fn accepts_row_lock(self) -> bool {
162        matches!(
163            self,
164            Self::PgNamespace
165                | Self::PgClass
166                | Self::PgInherits
167                | Self::PgPartitionedTable
168                | Self::PgAttribute
169                | Self::PgAttrdef
170                | Self::PgConstraint
171                | Self::PgIndex
172                | Self::PgTrigger
173                | Self::PgRewrite
174                | Self::PgType
175                | Self::PgProc
176                | Self::PgDatabase
177                | Self::PgAuthMembers
178                | Self::PgDescription
179                | Self::AgGraph
180                | Self::AgLabel
181        )
182    }
183
184    #[expect(
185        clippy::too_many_lines,
186        reason = "preserves catalog column and OID order"
187    )]
188    pub fn schema(self) -> Vec<(String, ColumnType)> {
189        macro_rules! columns {
190            ($($name:literal => $ty:expr),* $(,)?) => {
191                vec![$(($name.to_string(), $ty)),*]
192            };
193        }
194
195        match self {
196            Self::InformationSchemaCatalogName => columns![
197                "catalog_name" => sql_identifier(),
198            ],
199            Self::InformationSchemata => columns![
200                "catalog_name" => sql_identifier(),
201                "schema_name" => sql_identifier(),
202                "schema_owner" => sql_identifier(),
203                "default_character_set_catalog" => sql_identifier(),
204                "default_character_set_schema" => sql_identifier(),
205                "default_character_set_name" => sql_identifier(),
206                "sql_path" => character_data(),
207            ],
208            Self::InformationTables => columns![
209                "table_catalog" => sql_identifier(),
210                "table_schema" => sql_identifier(),
211                "table_name" => sql_identifier(),
212                "table_type" => character_data(),
213                "self_referencing_column_name" => sql_identifier(),
214                "reference_generation" => character_data(),
215                "user_defined_type_catalog" => sql_identifier(),
216                "user_defined_type_schema" => sql_identifier(),
217                "user_defined_type_name" => sql_identifier(),
218                "is_insertable_into" => yes_or_no(),
219                "is_typed" => yes_or_no(),
220                "commit_action" => character_data(),
221            ],
222            Self::InformationColumns => columns![
223                "table_catalog" => sql_identifier(),
224                "table_schema" => sql_identifier(),
225                "table_name" => sql_identifier(),
226                "column_name" => sql_identifier(),
227                "ordinal_position" => cardinal_number(),
228                "column_default" => character_data(),
229                "is_nullable" => yes_or_no(),
230                "data_type" => character_data(),
231                "character_maximum_length" => cardinal_number(),
232                "character_octet_length" => cardinal_number(),
233                "numeric_precision" => cardinal_number(),
234                "numeric_precision_radix" => cardinal_number(),
235                "numeric_scale" => cardinal_number(),
236                "datetime_precision" => cardinal_number(),
237                "interval_type" => character_data(),
238                "interval_precision" => cardinal_number(),
239                "character_set_catalog" => sql_identifier(),
240                "character_set_schema" => sql_identifier(),
241                "character_set_name" => sql_identifier(),
242                "collation_catalog" => sql_identifier(),
243                "collation_schema" => sql_identifier(),
244                "collation_name" => sql_identifier(),
245                "domain_catalog" => sql_identifier(),
246                "domain_schema" => sql_identifier(),
247                "domain_name" => sql_identifier(),
248                "udt_catalog" => sql_identifier(),
249                "udt_schema" => sql_identifier(),
250                "udt_name" => sql_identifier(),
251                "scope_catalog" => sql_identifier(),
252                "scope_schema" => sql_identifier(),
253                "scope_name" => sql_identifier(),
254                "maximum_cardinality" => cardinal_number(),
255                "dtd_identifier" => sql_identifier(),
256                "is_self_referencing" => yes_or_no(),
257                "is_identity" => yes_or_no(),
258                "identity_generation" => character_data(),
259                "identity_start" => character_data(),
260                "identity_increment" => character_data(),
261                "identity_maximum" => character_data(),
262                "identity_minimum" => character_data(),
263                "identity_cycle" => yes_or_no(),
264                "is_generated" => character_data(),
265                "generation_expression" => character_data(),
266                "is_updatable" => yes_or_no(),
267            ],
268            Self::InformationColumnPrivileges | Self::InformationRoleColumnGrants => columns![
269                "grantor" => sql_identifier(),
270                "grantee" => sql_identifier(),
271                "table_catalog" => sql_identifier(),
272                "table_schema" => sql_identifier(),
273                "table_name" => sql_identifier(),
274                "column_name" => sql_identifier(),
275                "privilege_type" => character_data(),
276                "is_grantable" => yes_or_no(),
277            ],
278            Self::InformationViews => columns![
279                "table_catalog" => sql_identifier(),
280                "table_schema" => sql_identifier(),
281                "table_name" => sql_identifier(),
282                "view_definition" => character_data(),
283                "check_option" => character_data(),
284                "is_updatable" => yes_or_no(),
285                "is_insertable_into" => yes_or_no(),
286                "is_trigger_updatable" => yes_or_no(),
287                "is_trigger_deletable" => yes_or_no(),
288                "is_trigger_insertable_into" => yes_or_no(),
289            ],
290            Self::InformationRoutines => information_routines_schema(),
291            Self::InformationSequences => columns![
292                "sequence_catalog" => sql_identifier(),
293                "sequence_schema" => sql_identifier(),
294                "sequence_name" => sql_identifier(),
295                "data_type" => character_data(),
296                "numeric_precision" => cardinal_number(),
297                "numeric_precision_radix" => cardinal_number(),
298                "numeric_scale" => cardinal_number(),
299                "start_value" => character_data(),
300                "minimum_value" => character_data(),
301                "maximum_value" => character_data(),
302                "increment" => character_data(),
303                "cycle_option" => yes_or_no(),
304            ],
305            Self::InformationTableConstraints => columns![
306                "constraint_catalog" => sql_identifier(),
307                "constraint_schema" => sql_identifier(),
308                "constraint_name" => sql_identifier(),
309                "table_catalog" => sql_identifier(),
310                "table_schema" => sql_identifier(),
311                "table_name" => sql_identifier(),
312                "constraint_type" => character_data(),
313                "is_deferrable" => yes_or_no(),
314                "initially_deferred" => yes_or_no(),
315                "enforced" => yes_or_no(),
316                "nulls_distinct" => yes_or_no(),
317            ],
318            Self::InformationKeyColumnUsage => columns![
319                "constraint_catalog" => sql_identifier(),
320                "constraint_schema" => sql_identifier(),
321                "constraint_name" => sql_identifier(),
322                "table_catalog" => sql_identifier(),
323                "table_schema" => sql_identifier(),
324                "table_name" => sql_identifier(),
325                "column_name" => sql_identifier(),
326                "ordinal_position" => cardinal_number(),
327                "position_in_unique_constraint" => cardinal_number(),
328            ],
329            Self::PgNamespace => columns![
330                "oid" => ColumnType::Oid,
331                "nspname" => ColumnType::Name,
332                "nspowner" => ColumnType::Oid,
333                "nspacl" => array(ColumnType::AclItem),
334            ],
335            Self::PgClass => columns![
336                "oid" => ColumnType::Oid,
337                "relname" => ColumnType::Name,
338                "relnamespace" => ColumnType::Oid,
339                "reltype" => ColumnType::Oid,
340                "reloftype" => ColumnType::Oid,
341                "relowner" => ColumnType::Oid,
342                "relam" => ColumnType::Oid,
343                "relfilenode" => ColumnType::Oid,
344                "reltablespace" => ColumnType::Oid,
345                "relpages" => ColumnType::Integer,
346                "reltuples" => ColumnType::Real,
347                "relallvisible" => ColumnType::Integer,
348                "relallfrozen" => ColumnType::Integer,
349                "reltoastrelid" => ColumnType::Oid,
350                "relhasindex" => ColumnType::Boolean,
351                "relisshared" => ColumnType::Boolean,
352                "relpersistence" => ColumnType::InternalChar,
353                "relkind" => ColumnType::InternalChar,
354                "relnatts" => ColumnType::SmallInteger,
355                "relchecks" => ColumnType::SmallInteger,
356                "relhasrules" => ColumnType::Boolean,
357                "relhastriggers" => ColumnType::Boolean,
358                "relhassubclass" => ColumnType::Boolean,
359                "relrowsecurity" => ColumnType::Boolean,
360                "relforcerowsecurity" => ColumnType::Boolean,
361                "relispopulated" => ColumnType::Boolean,
362                "relreplident" => ColumnType::InternalChar,
363                "relispartition" => ColumnType::Boolean,
364                "relrewrite" => ColumnType::Oid,
365                "relfrozenxid" => ColumnType::Xid,
366                "relminmxid" => ColumnType::Xid,
367                "relacl" => array(ColumnType::AclItem),
368                "reloptions" => array(ColumnType::Text),
369                "relpartbound" => ColumnType::PgNodeTree,
370            ],
371            Self::PgInherits => columns![
372                "inhrelid" => ColumnType::Oid,
373                "inhparent" => ColumnType::Oid,
374                "inhseqno" => ColumnType::Integer,
375                "inhdetachpending" => ColumnType::Boolean,
376            ],
377            Self::PgPartitionedTable => columns![
378                "partrelid" => ColumnType::Oid,
379                "partstrat" => ColumnType::InternalChar,
380                "partnatts" => ColumnType::SmallInteger,
381                "partdefid" => ColumnType::Oid,
382                "partattrs" => ColumnType::Int2Vector,
383                "partclass" => ColumnType::OidVector,
384                "partcollation" => ColumnType::OidVector,
385                "partexprs" => ColumnType::PgNodeTree,
386            ],
387            Self::PgAttribute => columns![
388                "attrelid" => ColumnType::Oid,
389                "attname" => ColumnType::Name,
390                "atttypid" => ColumnType::Oid,
391                "attlen" => ColumnType::SmallInteger,
392                "attnum" => ColumnType::SmallInteger,
393                "atttypmod" => ColumnType::Integer,
394                "attndims" => ColumnType::SmallInteger,
395                "attbyval" => ColumnType::Boolean,
396                "attalign" => ColumnType::InternalChar,
397                "attstorage" => ColumnType::InternalChar,
398                "attcompression" => ColumnType::InternalChar,
399                "attnotnull" => ColumnType::Boolean,
400                "atthasdef" => ColumnType::Boolean,
401                "atthasmissing" => ColumnType::Boolean,
402                "attidentity" => ColumnType::InternalChar,
403                "attgenerated" => ColumnType::InternalChar,
404                "attisdropped" => ColumnType::Boolean,
405                "attislocal" => ColumnType::Boolean,
406                "attinhcount" => ColumnType::SmallInteger,
407                "attcollation" => ColumnType::Oid,
408                "attstattarget" => ColumnType::SmallInteger,
409                "attacl" => array(ColumnType::AclItem),
410                "attoptions" => array(ColumnType::Text),
411                "attfdwoptions" => array(ColumnType::Text),
412                "attmissingval" => ColumnType::AnyArray,
413            ],
414            Self::PgAttrdef => columns![
415                "oid" => ColumnType::Oid,
416                "adrelid" => ColumnType::Oid,
417                "adnum" => ColumnType::SmallInteger,
418                "adbin" => ColumnType::PgNodeTree,
419            ],
420            Self::PgConstraint => columns![
421                "oid" => ColumnType::Oid,
422                "conname" => ColumnType::Name,
423                "connamespace" => ColumnType::Oid,
424                "contype" => ColumnType::InternalChar,
425                "condeferrable" => ColumnType::Boolean,
426                "condeferred" => ColumnType::Boolean,
427                "conenforced" => ColumnType::Boolean,
428                "convalidated" => ColumnType::Boolean,
429                "conrelid" => ColumnType::Oid,
430                "contypid" => ColumnType::Oid,
431                "conindid" => ColumnType::Oid,
432                "conparentid" => ColumnType::Oid,
433                "confrelid" => ColumnType::Oid,
434                "confupdtype" => ColumnType::InternalChar,
435                "confdeltype" => ColumnType::InternalChar,
436                "confmatchtype" => ColumnType::InternalChar,
437                "conislocal" => ColumnType::Boolean,
438                "coninhcount" => ColumnType::SmallInteger,
439                "connoinherit" => ColumnType::Boolean,
440                "conperiod" => ColumnType::Boolean,
441                "conkey" => array(ColumnType::SmallInteger),
442                "confkey" => array(ColumnType::SmallInteger),
443                "conpfeqop" => array(ColumnType::Oid),
444                "conppeqop" => array(ColumnType::Oid),
445                "conffeqop" => array(ColumnType::Oid),
446                "confdelsetcols" => array(ColumnType::SmallInteger),
447                "conexclop" => array(ColumnType::Oid),
448                "conbin" => ColumnType::PgNodeTree,
449            ],
450            Self::PgIndex => columns![
451                "indexrelid" => ColumnType::Oid,
452                "indrelid" => ColumnType::Oid,
453                "indnatts" => ColumnType::SmallInteger,
454                "indnkeyatts" => ColumnType::SmallInteger,
455                "indisunique" => ColumnType::Boolean,
456                "indnullsnotdistinct" => ColumnType::Boolean,
457                "indisprimary" => ColumnType::Boolean,
458                "indisexclusion" => ColumnType::Boolean,
459                "indimmediate" => ColumnType::Boolean,
460                "indisclustered" => ColumnType::Boolean,
461                "indisvalid" => ColumnType::Boolean,
462                "indcheckxmin" => ColumnType::Boolean,
463                "indisready" => ColumnType::Boolean,
464                "indislive" => ColumnType::Boolean,
465                "indisreplident" => ColumnType::Boolean,
466                "indkey" => ColumnType::Int2Vector,
467                "indcollation" => ColumnType::OidVector,
468                "indclass" => ColumnType::OidVector,
469                "indoption" => ColumnType::Int2Vector,
470                "indexprs" => ColumnType::PgNodeTree,
471                "indpred" => ColumnType::PgNodeTree,
472            ],
473            Self::PgTrigger => columns![
474                "oid" => ColumnType::Oid,
475                "tgrelid" => ColumnType::Oid,
476                "tgparentid" => ColumnType::Oid,
477                "tgname" => ColumnType::Name,
478                "tgfoid" => ColumnType::Oid,
479                "tgtype" => ColumnType::SmallInteger,
480                "tgenabled" => ColumnType::InternalChar,
481                "tgisinternal" => ColumnType::Boolean,
482                "tgconstrrelid" => ColumnType::Oid,
483                "tgconstrindid" => ColumnType::Oid,
484                "tgconstraint" => ColumnType::Oid,
485                "tgdeferrable" => ColumnType::Boolean,
486                "tginitdeferred" => ColumnType::Boolean,
487                "tgnargs" => ColumnType::SmallInteger,
488                "tgattr" => ColumnType::Int2Vector,
489                "tgargs" => ColumnType::Bytea,
490                "tgqual" => ColumnType::PgNodeTree,
491                "tgoldtable" => ColumnType::Name,
492                "tgnewtable" => ColumnType::Name,
493            ],
494            Self::PgRewrite => columns![
495                "oid" => ColumnType::Oid,
496                "rulename" => ColumnType::Name,
497                "ev_class" => ColumnType::Oid,
498                "ev_type" => ColumnType::InternalChar,
499                "ev_enabled" => ColumnType::InternalChar,
500                "is_instead" => ColumnType::Boolean,
501                "ev_qual" => ColumnType::PgNodeTree,
502                "ev_action" => ColumnType::PgNodeTree,
503            ],
504            Self::PgRules => columns![
505                "schemaname" => ColumnType::Name,
506                "tablename" => ColumnType::Name,
507                "rulename" => ColumnType::Name,
508                "definition" => ColumnType::Text,
509            ],
510            Self::PgTables => columns![
511                "schemaname" => ColumnType::Name,
512                "tablename" => ColumnType::Name,
513                "tableowner" => ColumnType::Name,
514                "tablespace" => ColumnType::Name,
515                "hasindexes" => ColumnType::Boolean,
516                "hasrules" => ColumnType::Boolean,
517                "hastriggers" => ColumnType::Boolean,
518                "rowsecurity" => ColumnType::Boolean,
519            ],
520            Self::PgViews => columns![
521                "schemaname" => ColumnType::Name,
522                "viewname" => ColumnType::Name,
523                "viewowner" => ColumnType::Name,
524                "definition" => ColumnType::Text,
525            ],
526            Self::PgIndexes => columns![
527                "schemaname" => ColumnType::Name,
528                "tablename" => ColumnType::Name,
529                "indexname" => ColumnType::Name,
530                "tablespace" => ColumnType::Name,
531                "indexdef" => ColumnType::Text,
532            ],
533            Self::PgType => columns![
534                "oid" => ColumnType::Oid,
535                "typname" => ColumnType::Name,
536                "typnamespace" => ColumnType::Oid,
537                "typowner" => ColumnType::Oid,
538                "typlen" => ColumnType::SmallInteger,
539                "typbyval" => ColumnType::Boolean,
540                "typtype" => ColumnType::InternalChar,
541                "typcategory" => ColumnType::InternalChar,
542                "typispreferred" => ColumnType::Boolean,
543                "typisdefined" => ColumnType::Boolean,
544                "typdelim" => ColumnType::InternalChar,
545                "typrelid" => ColumnType::Oid,
546                "typsubscript" => ColumnType::Regproc,
547                "typelem" => ColumnType::Oid,
548                "typarray" => ColumnType::Oid,
549                "typinput" => ColumnType::Regproc,
550                "typoutput" => ColumnType::Regproc,
551                "typreceive" => ColumnType::Regproc,
552                "typsend" => ColumnType::Regproc,
553                "typmodin" => ColumnType::Regproc,
554                "typmodout" => ColumnType::Regproc,
555                "typanalyze" => ColumnType::Regproc,
556                "typalign" => ColumnType::InternalChar,
557                "typstorage" => ColumnType::InternalChar,
558                "typnotnull" => ColumnType::Boolean,
559                "typbasetype" => ColumnType::Oid,
560                "typtypmod" => ColumnType::Integer,
561                "typndims" => ColumnType::Integer,
562                "typcollation" => ColumnType::Oid,
563                "typdefaultbin" => ColumnType::PgNodeTree,
564                "typdefault" => ColumnType::Text,
565                "typacl" => array(ColumnType::AclItem),
566            ],
567            Self::PgRange => columns![
568                "rngtypid" => ColumnType::Oid,
569                "rngsubtype" => ColumnType::Oid,
570                "rngmultitypid" => ColumnType::Oid,
571                "rngcollation" => ColumnType::Oid,
572                "rngsubopc" => ColumnType::Oid,
573                "rngcanonical" => ColumnType::Regproc,
574                "rngsubdiff" => ColumnType::Regproc,
575            ],
576            Self::PgProc => columns![
577                "oid" => ColumnType::Oid,
578                "proname" => ColumnType::Name,
579                "pronamespace" => ColumnType::Oid,
580                "proowner" => ColumnType::Oid,
581                "prolang" => ColumnType::Oid,
582                "procost" => ColumnType::Real,
583                "prorows" => ColumnType::Real,
584                "provariadic" => ColumnType::Oid,
585                "prosupport" => ColumnType::Regproc,
586                "prokind" => ColumnType::InternalChar,
587                "prosecdef" => ColumnType::Boolean,
588                "proleakproof" => ColumnType::Boolean,
589                "proisstrict" => ColumnType::Boolean,
590                "proretset" => ColumnType::Boolean,
591                "provolatile" => ColumnType::InternalChar,
592                "proparallel" => ColumnType::InternalChar,
593                "pronargs" => ColumnType::SmallInteger,
594                "pronargdefaults" => ColumnType::SmallInteger,
595                "prorettype" => ColumnType::Oid,
596                "proargtypes" => ColumnType::OidVector,
597                "proallargtypes" => array(ColumnType::Oid),
598                "proargmodes" => array(ColumnType::InternalChar),
599                "proargnames" => array(ColumnType::Text),
600                "proargdefaults" => ColumnType::PgNodeTree,
601                "protrftypes" => array(ColumnType::Oid),
602                "prosrc" => ColumnType::Text,
603                "probin" => ColumnType::Text,
604                "prosqlbody" => ColumnType::PgNodeTree,
605                "proconfig" => array(ColumnType::Text),
606                "proacl" => array(ColumnType::AclItem),
607            ],
608            Self::PgDatabase => columns![
609                "oid" => ColumnType::Oid,
610                "datname" => ColumnType::Name,
611                "datdba" => ColumnType::Oid,
612                "encoding" => ColumnType::Integer,
613                "datlocprovider" => ColumnType::InternalChar,
614                "datistemplate" => ColumnType::Boolean,
615                "datallowconn" => ColumnType::Boolean,
616                "dathasloginevt" => ColumnType::Boolean,
617                "datconnlimit" => ColumnType::Integer,
618                "datfrozenxid" => ColumnType::Xid,
619                "datminmxid" => ColumnType::Xid,
620                "dattablespace" => ColumnType::Oid,
621                "datcollate" => ColumnType::Text,
622                "datctype" => ColumnType::Text,
623                "datlocale" => ColumnType::Text,
624                "daticurules" => ColumnType::Text,
625                "datcollversion" => ColumnType::Text,
626                "datacl" => array(ColumnType::AclItem),
627            ],
628            Self::PgAuthMembers => columns![
629                "oid" => ColumnType::Oid,
630                "roleid" => ColumnType::Oid,
631                "member" => ColumnType::Oid,
632                "grantor" => ColumnType::Oid,
633                "admin_option" => ColumnType::Boolean,
634                "inherit_option" => ColumnType::Boolean,
635                "set_option" => ColumnType::Boolean,
636            ],
637            Self::PgRoles => columns![
638                "rolname" => ColumnType::Name,
639                "rolsuper" => ColumnType::Boolean,
640                "rolinherit" => ColumnType::Boolean,
641                "rolcreaterole" => ColumnType::Boolean,
642                "rolcreatedb" => ColumnType::Boolean,
643                "rolcanlogin" => ColumnType::Boolean,
644                "rolreplication" => ColumnType::Boolean,
645                "rolconnlimit" => ColumnType::Integer,
646                "rolpassword" => ColumnType::Text,
647                "rolvaliduntil" => ColumnType::TimestampTz,
648                "rolbypassrls" => ColumnType::Boolean,
649                "rolconfig" => array(ColumnType::Text),
650                "oid" => ColumnType::Oid,
651            ],
652            Self::PgUser => columns![
653                "usename" => ColumnType::Name,
654                "usesysid" => ColumnType::Oid,
655                "usecreatedb" => ColumnType::Boolean,
656                "usesuper" => ColumnType::Boolean,
657                "userepl" => ColumnType::Boolean,
658                "usebypassrls" => ColumnType::Boolean,
659                "passwd" => ColumnType::Text,
660                "valuntil" => ColumnType::TimestampTz,
661                "useconfig" => array(ColumnType::Text),
662            ],
663            Self::PgPreparedStatements => columns![
664                "name" => ColumnType::Text,
665                "statement" => ColumnType::Text,
666                "prepare_time" => ColumnType::TimestampTz,
667                "parameter_types" => array(ColumnType::Regtype),
668                "result_types" => array(ColumnType::Regtype),
669                "from_sql" => ColumnType::Boolean,
670                "generic_plans" => ColumnType::BigInteger,
671                "custom_plans" => ColumnType::BigInteger,
672            ],
673            Self::PgSettings => columns![
674                "name" => ColumnType::Text,
675                "setting" => ColumnType::Text,
676                "unit" => ColumnType::Text,
677                "category" => ColumnType::Text,
678                "short_desc" => ColumnType::Text,
679                "extra_desc" => ColumnType::Text,
680                "context" => ColumnType::Text,
681                "vartype" => ColumnType::Text,
682                "source" => ColumnType::Text,
683                "min_val" => ColumnType::Text,
684                "max_val" => ColumnType::Text,
685                "enumvals" => array(ColumnType::Text),
686                "boot_val" => ColumnType::Text,
687                "reset_val" => ColumnType::Text,
688                "sourcefile" => ColumnType::Text,
689                "sourceline" => ColumnType::Integer,
690                "pending_restart" => ColumnType::Boolean,
691            ],
692            Self::PgDescription => columns![
693                "objoid" => ColumnType::Oid,
694                "classoid" => ColumnType::Oid,
695                "objsubid" => ColumnType::Integer,
696                "description" => ColumnType::Text,
697            ],
698            Self::PgMatviews => columns![
699                "schemaname" => ColumnType::Name,
700                "matviewname" => ColumnType::Name,
701                "matviewowner" => ColumnType::Name,
702                "tablespace" => ColumnType::Name,
703                "hasindexes" => ColumnType::Boolean,
704                "ispopulated" => ColumnType::Boolean,
705                "definition" => ColumnType::Text,
706            ],
707            Self::PgSequences => columns![
708                "schemaname" => ColumnType::Name,
709                "sequencename" => ColumnType::Name,
710                "sequenceowner" => ColumnType::Name,
711                "data_type" => ColumnType::Regtype,
712                "start_value" => ColumnType::BigInteger,
713                "min_value" => ColumnType::BigInteger,
714                "max_value" => ColumnType::BigInteger,
715                "increment_by" => ColumnType::BigInteger,
716                "cycle" => ColumnType::Boolean,
717                "cache_size" => ColumnType::BigInteger,
718                "last_value" => ColumnType::BigInteger,
719            ],
720            Self::AgGraph => columns![
721                "graphid" => ColumnType::Oid,
722                "name" => ColumnType::Name,
723                "namespace" => ColumnType::Regnamespace,
724            ],
725            Self::AgLabel => columns![
726                "name" => ColumnType::Name,
727                "graph" => ColumnType::Oid,
728                "id" => ag_label_id(),
729                "kind" => ag_label_kind(),
730                "relation" => ColumnType::Regclass,
731                "seq_name" => ColumnType::Name,
732            ],
733        }
734    }
735}
736
737fn ag_catalog_domain(name: &str, base: ColumnType) -> ColumnType {
738    ColumnType::Domain {
739        schema: AG_CATALOG_SCHEMA.into(),
740        name: name.into(),
741        oid: ag_catalog_type_oid(name),
742        base: Box::new(base),
743    }
744}
745
746/// Stable OID of an `ag_catalog` type; the extension assigns these from
747/// the OID counter, so any collision-free assignment is faithful.
748pub fn ag_catalog_type_oid(name: &str) -> u32 {
749    let oid = oids::stable_oid("type", &format!("{AG_CATALOG_SCHEMA}.{name}"));
750    u32::try_from(oid).unwrap_or(0)
751}
752
753/// AGE `label_id`: `int NOT NULL CHECK (VALUE > 0 AND VALUE <= 65535)`.
754pub fn ag_label_id() -> ColumnType {
755    ag_catalog_domain("label_id", ColumnType::Integer)
756}
757
758/// AGE `label_kind`: `"char" NOT NULL CHECK (VALUE = 'v' OR VALUE = 'e')`.
759pub fn ag_label_kind() -> ColumnType {
760    ag_catalog_domain("label_kind", ColumnType::InternalChar)
761}
762
763/// Internal type carrier for AGE's fixed-width `graphid` base type.
764pub fn age_graphid() -> ColumnType {
765    ag_catalog_domain("graphid", ColumnType::BigInteger)
766}
767
768/// Internal type carrier for AGE's JSONB-shaped `agtype` base type.
769pub fn age_agtype() -> ColumnType {
770    ag_catalog_domain("agtype", ColumnType::JsonB)
771}
772
773pub fn ag_catalog_domains() -> Vec<ColumnType> {
774    vec![ag_label_id(), ag_label_kind()]
775}
776
777fn array(element: ColumnType) -> ColumnType {
778    ColumnType::Array(Box::new(element))
779}
780
781fn information_schema_domain(name: &str, oid: u32, base: ColumnType) -> ColumnType {
782    ColumnType::Domain {
783        schema: "information_schema".into(),
784        name: name.into(),
785        oid,
786        base: Box::new(base),
787    }
788}
789
790fn cardinal_number() -> ColumnType {
791    information_schema_domain("cardinal_number", 13_307, ColumnType::Integer)
792}
793
794fn character_data() -> ColumnType {
795    information_schema_domain("character_data", 13_310, ColumnType::Varchar(None))
796}
797
798fn sql_identifier() -> ColumnType {
799    information_schema_domain("sql_identifier", 13_312, ColumnType::Name)
800}
801
802fn time_stamp() -> ColumnType {
803    information_schema_domain("time_stamp", 13_318, ColumnType::TimestampTz)
804}
805
806fn yes_or_no() -> ColumnType {
807    information_schema_domain("yes_or_no", 13_320, ColumnType::Varchar(Some(3)))
808}
809
810pub fn information_schema_domains() -> Vec<ColumnType> {
811    vec![
812        cardinal_number(),
813        character_data(),
814        sql_identifier(),
815        time_stamp(),
816        yes_or_no(),
817    ]
818}
819
820fn information_routines_schema() -> Vec<(String, ColumnType)> {
821    macro_rules! columns {
822        ($($name:literal => $ty:expr),* $(,)?) => {
823            vec![$(($name.to_string(), $ty)),*]
824        };
825    }
826
827    columns![
828        "specific_catalog" => sql_identifier(),
829        "specific_schema" => sql_identifier(),
830        "specific_name" => sql_identifier(),
831        "routine_catalog" => sql_identifier(),
832        "routine_schema" => sql_identifier(),
833        "routine_name" => sql_identifier(),
834        "routine_type" => character_data(),
835        "module_catalog" => sql_identifier(),
836        "module_schema" => sql_identifier(),
837        "module_name" => sql_identifier(),
838        "udt_catalog" => sql_identifier(),
839        "udt_schema" => sql_identifier(),
840        "udt_name" => sql_identifier(),
841        "data_type" => character_data(),
842        "character_maximum_length" => cardinal_number(),
843        "character_octet_length" => cardinal_number(),
844        "character_set_catalog" => sql_identifier(),
845        "character_set_schema" => sql_identifier(),
846        "character_set_name" => sql_identifier(),
847        "collation_catalog" => sql_identifier(),
848        "collation_schema" => sql_identifier(),
849        "collation_name" => sql_identifier(),
850        "numeric_precision" => cardinal_number(),
851        "numeric_precision_radix" => cardinal_number(),
852        "numeric_scale" => cardinal_number(),
853        "datetime_precision" => cardinal_number(),
854        "interval_type" => character_data(),
855        "interval_precision" => cardinal_number(),
856        "type_udt_catalog" => sql_identifier(),
857        "type_udt_schema" => sql_identifier(),
858        "type_udt_name" => sql_identifier(),
859        "scope_catalog" => sql_identifier(),
860        "scope_schema" => sql_identifier(),
861        "scope_name" => sql_identifier(),
862        "maximum_cardinality" => cardinal_number(),
863        "dtd_identifier" => sql_identifier(),
864        "routine_body" => character_data(),
865        "routine_definition" => character_data(),
866        "external_name" => character_data(),
867        "external_language" => character_data(),
868        "parameter_style" => character_data(),
869        "is_deterministic" => yes_or_no(),
870        "sql_data_access" => character_data(),
871        "is_null_call" => yes_or_no(),
872        "sql_path" => character_data(),
873        "schema_level_routine" => yes_or_no(),
874        "max_dynamic_result_sets" => cardinal_number(),
875        "is_user_defined_cast" => yes_or_no(),
876        "is_implicitly_invocable" => yes_or_no(),
877        "security_type" => character_data(),
878        "to_sql_specific_catalog" => sql_identifier(),
879        "to_sql_specific_schema" => sql_identifier(),
880        "to_sql_specific_name" => sql_identifier(),
881        "as_locator" => yes_or_no(),
882        "created" => time_stamp(),
883        "last_altered" => time_stamp(),
884        "new_savepoint_level" => yes_or_no(),
885        "is_udt_dependent" => yes_or_no(),
886        "result_cast_from_data_type" => character_data(),
887        "result_cast_as_locator" => yes_or_no(),
888        "result_cast_char_max_length" => cardinal_number(),
889        "result_cast_char_octet_length" => cardinal_number(),
890        "result_cast_char_set_catalog" => sql_identifier(),
891        "result_cast_char_set_schema" => sql_identifier(),
892        "result_cast_char_set_name" => sql_identifier(),
893        "result_cast_collation_catalog" => sql_identifier(),
894        "result_cast_collation_schema" => sql_identifier(),
895        "result_cast_collation_name" => sql_identifier(),
896        "result_cast_numeric_precision" => cardinal_number(),
897        "result_cast_numeric_precision_radix" => cardinal_number(),
898        "result_cast_numeric_scale" => cardinal_number(),
899        "result_cast_datetime_precision" => cardinal_number(),
900        "result_cast_interval_type" => character_data(),
901        "result_cast_interval_precision" => cardinal_number(),
902        "result_cast_type_udt_catalog" => sql_identifier(),
903        "result_cast_type_udt_schema" => sql_identifier(),
904        "result_cast_type_udt_name" => sql_identifier(),
905        "result_cast_scope_catalog" => sql_identifier(),
906        "result_cast_scope_schema" => sql_identifier(),
907        "result_cast_scope_name" => sql_identifier(),
908        "result_cast_maximum_cardinality" => cardinal_number(),
909        "result_cast_dtd_identifier" => sql_identifier(),
910    ]
911}
912
913#[cfg(test)]
914mod tests;
915
916pub mod analysis;
917
918pub mod domain;
919pub mod events;
920pub mod index;
921pub mod roles;
922
923/// Namespaces the engine implements without a durable schema row: the
924/// `PostgreSQL` system schemas and the Apache AGE catalog schema.
925pub fn is_virtual_system_schema(name: &str) -> bool {
926    matches!(name, "pg_catalog" | "information_schema" | "ag_catalog")
927}
928
929pub mod view;
930
931pub mod session;
932
933pub mod constraints;
934
935pub const DATABASE_NAME: &str = "uqa";
936pub const DATABASE_OID: i64 = 5;
937
938pub mod stored_ast;
939
940pub mod regrole_dependencies;
941
942pub mod security;
943
944pub mod stored_view;
945
946pub mod errors;
947
948pub mod sequence_functions;