uqa-engine 0.2.3

Engine: schema-aware table store, catalog restore, transactions
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

//! `information_schema` and `pg_catalog` virtual row dispatch.

use uqa_sql::{ResultRow, SQLError};

use crate::engine_capabilities::{CatalogReadView, RelationNameResolution, SessionExecutionView};
use crate::{ConstraintIdentity, Engine, RelationIdentity};

pub(super) fn build_info_schema_rows(
    engine: &Engine,
    catalog: &CatalogReadView,
    resolution: &RelationNameResolution,
    session: SessionExecutionView<'_>,
    name: &str,
) -> Result<Option<Vec<ResultRow>>, SQLError> {
    let Some(relation) = resolve_virtual_relation(resolution, name) else {
        return ag_catalog::build_age_label_relation_rows(catalog, resolution, name);
    };
    let mut catalog_resolution = resolution.clone();
    catalog_resolution.set_lookup_mode(crate::engine_capabilities::RelationLookupMode::Bound);
    let resolution = &catalog_resolution;
    Ok(Some(match relation {
        VirtualRelation::InformationSchemaCatalogName => build_info_catalog_name(),
        VirtualRelation::InformationSchemata => build_info_schemata(catalog, resolution)?,
        VirtualRelation::InformationTables => build_info_tables(engine, catalog, resolution)?,
        VirtualRelation::InformationColumns => build_info_columns(engine, catalog, resolution)?,
        VirtualRelation::InformationColumnPrivileges => {
            build_info_column_privileges(engine, catalog, resolution, false)?
        }
        VirtualRelation::InformationRoleColumnGrants => {
            build_info_column_privileges(engine, catalog, resolution, true)?
        }
        VirtualRelation::InformationViews => build_info_views(engine, catalog, resolution)?,
        VirtualRelation::InformationRoutines => build_info_routines(catalog)?,
        VirtualRelation::InformationSequences => build_info_sequences(catalog, session),
        VirtualRelation::InformationTableConstraints => {
            build_info_table_constraints(catalog, resolution)?
        }
        VirtualRelation::InformationKeyColumnUsage => {
            build_info_key_column_usage(catalog, resolution)?
        }
        VirtualRelation::PgNamespace => build_pg_namespace(catalog, resolution)?,
        VirtualRelation::PgClass => build_pg_class(engine, catalog, resolution)?,
        VirtualRelation::PgInherits => build_pg_inherits(catalog, resolution)?,
        VirtualRelation::PgPartitionedTable => {
            build_pg_partitioned_table(engine, catalog, resolution)?
        }
        VirtualRelation::PgAttribute => build_pg_attribute(engine, catalog, resolution)?,
        VirtualRelation::PgAttrdef => build_pg_attrdef(catalog, resolution)?,
        VirtualRelation::PgConstraint => build_pg_constraint(catalog, resolution)?,
        VirtualRelation::PgIndex => build_pg_index(catalog, resolution)?,
        VirtualRelation::PgTrigger => build_pg_trigger(engine, catalog, resolution)?,
        VirtualRelation::PgRewrite => build_pg_rewrite(catalog, resolution)?,
        VirtualRelation::PgRules => build_pg_rules(catalog, resolution)?,
        VirtualRelation::PgTables => build_pg_tables(catalog, resolution)?,
        VirtualRelation::PgViews => build_pg_views(catalog, resolution)?,
        VirtualRelation::PgIndexes => build_pg_indexes(catalog, resolution)?,
        VirtualRelation::PgType => build_pg_type(),
        VirtualRelation::PgRange => build_pg_range(),
        VirtualRelation::PgProc => build_pg_proc(catalog)?,
        VirtualRelation::PgDatabase => build_pg_database(catalog)?,
        VirtualRelation::PgAuthMembers => build_pg_auth_members(catalog),
        VirtualRelation::PgRoles => build_pg_roles(catalog),
        VirtualRelation::PgUser => build_pg_user(catalog),
        VirtualRelation::PgSettings => build_pg_settings(session)?,
        VirtualRelation::PgDescription => Vec::new(),
        VirtualRelation::PgMatviews => build_pg_matviews(catalog, resolution)?,
        VirtualRelation::PgSequences => build_pg_sequences(catalog, session)?,
        VirtualRelation::AgGraph => build_ag_graph(catalog)?,
        VirtualRelation::AgLabel => build_ag_label(catalog)?,
    }))
}

mod ag_catalog;
mod builtin_routines;
mod events;
mod expression_text;
mod index_definition;
pub(in crate::sql) use index_definition::pg_get_indexdef_value;
pub(in crate::sql) use regtypes::format_type_value;
mod view_definition;
pub(in crate::sql) use view_definition::pg_get_viewdef_value;
pub(crate) use view_definition::rename_view_column_query;
mod helpers;
mod information_schema;
mod partitioning;
mod pg_catalog;
mod pg_namespace;
mod pg_proc;
mod pg_settings;
mod regtypes;
mod relation_catalog;
mod schema;

#[derive(Debug, Clone)]
pub(crate) struct RuntimeConstraint {
    pub(crate) identity: ConstraintIdentity,
    pub(crate) deferrable: bool,
}

pub(crate) fn runtime_constraints(engine: &Engine) -> Result<Vec<RuntimeConstraint>, SQLError> {
    let catalog = engine.catalog_read_view();
    let resolution = engine.session_execution_view().relation_name_resolution();
    let mut constraints = helpers::constraints::constraint_catalog_rows(&catalog, &resolution)?
        .into_iter()
        .map(|constraint| {
            Ok(RuntimeConstraint {
                identity: ConstraintIdentity {
                    relation: RelationIdentity::new(constraint.schema, constraint.table),
                    name: constraint.name,
                    object_id: constraint.object_id,
                },
                deferrable: constraint.state.deferrable(),
            })
        })
        .collect::<Result<Vec<_>, SQLError>>()?;
    for (trigger, _) in events::catalog_triggers(&catalog, &resolution)? {
        if !trigger.definition.constraint {
            continue;
        }
        let relation =
            RelationIdentity::from_legacy_name(&trigger.definition.table).map_err(|error| {
                SQLError::Internal(format!(
                    "decode constraint-trigger relation `{}`: {error}",
                    trigger.definition.table
                ))
            })?;
        constraints.push(RuntimeConstraint {
            identity: ConstraintIdentity {
                relation,
                name: trigger
                    .constraint_name
                    .clone()
                    .unwrap_or_else(|| trigger.definition.name.clone()),
                object_id: trigger.object_id,
            },
            deferrable: trigger.definition.deferrability.is_deferrable(),
        });
    }
    Ok(constraints)
}

pub(crate) fn schema_object_oid(name: &str) -> i64 {
    helpers::oids::schema_oid(name)
}

pub(crate) fn resolve_age_label_relation_name(
    engine: &Engine,
    name: &str,
) -> Result<Option<String>, SQLError> {
    let catalog = engine.catalog_read_view();
    let resolution = engine.session_execution_view().relation_name_resolution();
    ag_catalog::resolve_age_label_relation_name(&catalog, &resolution, name)
}

pub(crate) fn query_source_column_names(
    engine: &Engine,
    name: &str,
    relations_bound: bool,
) -> Result<Option<Vec<String>>, SQLError> {
    let catalog = engine.catalog_read_view();
    let mut resolution = engine.session_execution_view().relation_name_resolution();
    if relations_bound {
        resolution.set_lookup_mode(crate::engine_capabilities::RelationLookupMode::Bound);
    }
    if catalog.sequence_resolved(&resolution, name)?.is_some() {
        return Ok(Some(vec![
            "last_value".into(),
            "log_cnt".into(),
            "is_called".into(),
        ]));
    }
    if let Some(view) = catalog.view_resolved(&resolution, name)? {
        let schema =
            engine.stored_view_schema_with_catalog(view, catalog.clone(), resolution.clone())?;
        return Ok(Some(
            schema
                .columns()
                .iter()
                .enumerate()
                .map(|(position, name)| schema.public_name(position).unwrap_or(name).to_string())
                .collect(),
        ));
    }
    if let Some(table) = catalog.table_resolved(&resolution, name)? {
        return Ok(Some(
            table
                .columns
                .iter()
                .map(|column| column.name.clone())
                .collect(),
        ));
    }
    if let Some(table) = catalog.foreign_table_resolved(&resolution, name)? {
        return Ok(Some(
            table
                .columns
                .iter()
                .map(|column| column.name.clone())
                .collect(),
        ));
    }
    Ok(virtual_relation_schema(&catalog, &resolution, name)?
        .map(|columns| columns.into_iter().map(|(name, _)| name).collect()))
}

use ag_catalog::{build_ag_graph, build_ag_label};
use events::{build_pg_rewrite, build_pg_rules, build_pg_trigger};
pub(in crate::sql) use events::{
    event_relation_oid, pg_get_ruledef_value, pg_get_triggerdef_value,
};
use information_schema::{
    build_info_catalog_name, build_info_column_privileges, build_info_columns,
    build_info_key_column_usage, build_info_routines, build_info_schemata, build_info_sequences,
    build_info_table_constraints, build_info_tables, build_info_views,
};
use partitioning::build_pg_partitioned_table;
pub(in crate::sql) use partitioning::{pg_get_expr_value, pg_get_partkeydef_value};
pub(in crate::sql) fn table_relation_oid(engine: &Engine, table: &str) -> Result<i64, SQLError> {
    let catalog = engine.catalog_read_view();
    let mut resolution = engine.session_execution_view().relation_name_resolution();
    resolution.set_lookup_mode(crate::engine_capabilities::RelationLookupMode::Bound);
    snapshot_table_relation_oid(&catalog, &resolution, table)
}
pub(crate) fn sequence_relation_oid(object_id: [u8; 16]) -> i64 {
    helpers::oids::stable_object_oid("relation", &object_id)
}
pub(crate) fn view_relation_oid(view: &crate::StoredView) -> i64 {
    helpers::oids::stable_object_oid("relation", &view.object_id)
}

pub(crate) fn view_rowtype_oid(view: &crate::StoredView) -> i64 {
    helpers::oids::stable_object_oid("rowtype", &view.object_id)
}

pub(crate) fn foreign_table_relation_oid(table: &crate::engine_fdw::StoredForeignTable) -> i64 {
    helpers::oids::stable_object_oid("relation", &table.object_id)
}

pub(crate) fn foreign_table_rowtype_oid(table: &crate::engine_fdw::StoredForeignTable) -> i64 {
    helpers::oids::stable_object_oid("rowtype", &table.object_id)
}
pub(crate) fn snapshot_table_relation_oid(
    catalog: &CatalogReadView,
    resolution: &RelationNameResolution,
    table: &str,
) -> Result<i64, SQLError> {
    pg_catalog::table_relation_oid_from(catalog, resolution, table)
}
use pg_catalog::{
    build_pg_attrdef, build_pg_attribute, build_pg_auth_members, build_pg_constraint,
    build_pg_database, build_pg_index, build_pg_indexes, build_pg_matviews, build_pg_range,
    build_pg_roles, build_pg_sequences, build_pg_tables, build_pg_type, build_pg_user,
    build_pg_views,
};
use pg_namespace::build_pg_namespace;
use pg_proc::build_pg_proc;
use pg_settings::build_pg_settings;
pub(crate) use regtypes::{
    resolve_catalog_column_type, resolve_regclass_kind_by_oid, resolve_regclass_oid,
    resolve_regnamespace_oid, resolve_regobject_oid, resolve_regprocedure_oid, resolve_regrole_oid,
    resolve_regtype_output, RegtypeOutputCatalog,
};

pub(crate) fn resolve_catalog_column_type_name(
    engine: &Engine,
    type_name: &str,
) -> Result<uqa_sql::ast::ColumnType, SQLError> {
    resolve_catalog_column_type(engine, type_name).ok_or_else(|| SQLError::Routine {
        sqlstate: "42704".into(),
        message: format!("type \"{}\" does not exist", type_name.trim_matches('"')),
    })
}

use relation_catalog::{build_pg_class, build_pg_inherits};
use schema::{resolve_virtual_relation, VirtualRelation};
pub(in crate::sql) use schema::{virtual_relation_accepts_row_lock, virtual_relation_schema};