Skip to main content

uqa_sql/semantics/partition/
identity.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use super::PartitionCatalog;
8use crate::SQLError;
9use std::collections::BTreeSet;
10pub fn partition_hierarchy_root(
11    catalog: &dyn PartitionCatalog,
12    table: &str,
13) -> Result<Option<String>, SQLError> {
14    let mut current = catalog
15        .try_resolve_table_name(table)
16        .map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
17        .ok_or_else(|| SQLError::UnknownTable(table.to_string()))?;
18    let mut visited = BTreeSet::new();
19    let mut participates = false;
20    loop {
21        if !visited.insert(current.clone()) {
22            return Err(SQLError::Internal(format!(
23                "partition hierarchy cycle reaches `{current}`"
24            )));
25        }
26        let hierarchy = catalog
27            .try_table_hierarchy(&current)
28            .map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
29        participates |= hierarchy.partition_spec.is_some() || hierarchy.is_partition();
30        if !hierarchy.is_partition() {
31            return Ok(participates.then_some(current));
32        }
33        current = hierarchy
34            .parents
35            .first()
36            .cloned()
37            .ok_or_else(|| SQLError::Internal("partition has no parent relation".into()))?;
38    }
39}
40
41/// Return the physical counter owner for legacy auto-increment metadata. Declarative partitions share the top partitioned parent's counter; newly created SERIAL and identity columns use their durable sequence binding instead.
42pub fn partition_identity_owner(
43    catalog: &dyn PartitionCatalog,
44    table: &str,
45) -> Result<String, SQLError> {
46    if let Some(root) = partition_hierarchy_root(catalog, table)? {
47        return Ok(root);
48    }
49    catalog
50        .try_resolve_table_name(table)
51        .map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
52        .ok_or_else(|| SQLError::UnknownTable(table.to_string()))
53}