Skip to main content

uqa_sql/schema/sequences/
owner_migration.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Infer stable sequence owners from legacy table and foreign-column declarations.
8use super::implicit::stored_owner_names_current;
9use uqa_core::{
10    catalog_sequence::{SequenceOwner, SequenceOwnerDependency},
11    RelationIdentity,
12};
13pub fn resolve_migrated_sequence_reference(
14    reference: &str,
15    sequences: &[RelationIdentity],
16) -> Result<RelationIdentity, String> {
17    let (schema, name) = RelationIdentity::parse_reference(reference)?;
18    let candidates = sequences
19        .iter()
20        .filter(|candidate| {
21            candidate.name == name
22                && schema
23                    .as_ref()
24                    .is_none_or(|schema| candidate.schema == *schema)
25        })
26        .cloned()
27        .collect::<Vec<_>>();
28    match candidates.as_slice() {
29        [target] => Ok(target.clone()),
30        [] => Err(format!(
31            "implicit sequence owner references missing sequence `{reference}`"
32        )),
33        _ => Err(format!(
34            "implicit sequence owner reference `{reference}` is ambiguous"
35        )),
36    }
37}
38pub fn collect_migrated_sequence_owner(
39    relation: &RelationIdentity,
40    table_object_id: [u8; 16],
41    column: &crate::ast::ColumnDef,
42    sequence_relations: &[RelationIdentity],
43    valid_owners: &mut std::collections::BTreeSet<([u8; 16], [u8; 16])>,
44    inferred: &mut std::collections::BTreeMap<RelationIdentity, SequenceOwner>,
45) -> Result<(), String> {
46    let relation_name = relation.qualified_name();
47    let column_object_id = column.object_id.ok_or_else(|| {
48        format!(
49            "column `{relation_name}`.`{}` has no object identity during sequence-owner migration",
50            column.name
51        )
52    })?;
53    valid_owners.insert((table_object_id, column_object_id));
54    let Some(provenance) = column.auto_increment.as_ref() else {
55        return Ok(());
56    };
57    let Some(named_owner) = provenance.owner.as_ref() else {
58        return Ok(());
59    };
60    if !stored_owner_names_current(relation, column, named_owner) {
61        return Ok(());
62    }
63    let Some(sequence) = provenance.sequence.as_deref() else {
64        return Ok(());
65    };
66    let sequence = resolve_migrated_sequence_reference(sequence, sequence_relations)?;
67    let owner = SequenceOwner {
68        table_object_id,
69        column_object_id,
70        dependency: if provenance.is_identity() {
71            SequenceOwnerDependency::Internal
72        } else {
73            SequenceOwnerDependency::Automatic
74        },
75    };
76    if inferred
77        .insert(sequence.clone(), owner)
78        .is_some_and(|old| old != owner)
79    {
80        return Err(format!(
81            "sequence `{}` has conflicting implicit owners",
82            sequence.qualified_name()
83        ));
84    }
85    Ok(())
86}
87
88#[cfg(test)]
89mod tests;