Skip to main content

uqa_sql/ast/
sequence.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Durable provenance for implicit SERIAL and identity sequences.
8
9use serde::{Deserialize, Deserializer, Serialize};
10
11/// `PostgreSQL` syntax that supplies an omitted column value from an implicit sequence. `SERIAL` is a sequence-backed default, while identity columns retain a separate generation attribute and do not have a column default.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum AutoIncrementKind {
15    Serial,
16    IdentityAlways,
17    IdentityByDefault,
18    /// Catalogs written before sequence provenance was persisted cannot distinguish `SERIAL` from identity syntax. Preserve their historical table-counter behavior instead of guessing and changing stored data.
19    Legacy,
20}
21
22/// Durable owner of an implicit sequence. Inherited `SERIAL` defaults and declarative partitions copy this owner unchanged, so truncating a child cannot reset a sequence owned by its parent.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct AutoIncrementOwner {
25    pub table: String,
26    pub column: String,
27}
28
29/// Durable sequence provenance for a `SERIAL` or identity column.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub struct AutoIncrement {
32    pub kind: AutoIncrementKind,
33    /// Canonical sequence relation name. Compiler-produced column definitions leave this empty until `CREATE TABLE` resolves the destination schema.
34    #[serde(default, skip_serializing_if = "Option::is_none")]
35    pub sequence: Option<String>,
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub owner: Option<AutoIncrementOwner>,
38}
39
40impl AutoIncrement {
41    #[must_use]
42    pub const fn serial() -> Self {
43        Self {
44            kind: AutoIncrementKind::Serial,
45            sequence: None,
46            owner: None,
47        }
48    }
49
50    #[must_use]
51    pub const fn identity_always() -> Self {
52        Self {
53            kind: AutoIncrementKind::IdentityAlways,
54            sequence: None,
55            owner: None,
56        }
57    }
58
59    #[must_use]
60    pub const fn identity_by_default() -> Self {
61        Self {
62            kind: AutoIncrementKind::IdentityByDefault,
63            sequence: None,
64            owner: None,
65        }
66    }
67
68    #[must_use]
69    pub const fn legacy() -> Self {
70        Self {
71            kind: AutoIncrementKind::Legacy,
72            sequence: None,
73            owner: None,
74        }
75    }
76
77    #[must_use]
78    pub const fn is_identity(&self) -> bool {
79        matches!(
80            self.kind,
81            AutoIncrementKind::IdentityAlways | AutoIncrementKind::IdentityByDefault
82        )
83    }
84
85    #[must_use]
86    pub const fn is_legacy(&self) -> bool {
87        matches!(self.kind, AutoIncrementKind::Legacy)
88    }
89}
90
91pub(super) fn deserialize_auto_increment<'de, D>(
92    deserializer: D,
93) -> Result<Option<AutoIncrement>, D::Error>
94where
95    D: Deserializer<'de>,
96{
97    #[derive(Deserialize)]
98    #[serde(untagged)]
99    enum Representation {
100        Legacy(bool),
101        Provenance(AutoIncrement),
102    }
103
104    Ok(match Option::<Representation>::deserialize(deserializer)? {
105        Some(Representation::Legacy(true)) => Some(AutoIncrement::legacy()),
106        Some(Representation::Legacy(false)) | None => None,
107        Some(Representation::Provenance(provenance)) => Some(provenance),
108    })
109}