Skip to main content

uqa_sql/ast/
composites.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Composite type declarations and their bound type references.
8
9use serde::{Deserialize, Serialize};
10use uqa_core::{
11    memory::{Produced, ProductionControl},
12    ValueRetentionError,
13};
14
15use super::ColumnType;
16
17/// `CREATE TYPE name AS (attribute type [COLLATE collation], ...)` with attributes in declaration order.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct CreateCompositeType {
20    /// Rendered qualified type name, resolved against the creation namespace during execution.
21    pub name: String,
22    pub attributes: Vec<CompositeAttributeDefinition>,
23}
24
25/// One declared attribute. The type is a declaration until execution binds it through the catalog.
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct CompositeAttributeDefinition {
28    pub name: String,
29    pub ty: ColumnType,
30    /// The rendered qualified collation name of an explicit `COLLATE` clause.
31    pub collation: Option<String>,
32    /// `SETOF type`, which the grammar accepts and `BuildDescForRelation` rejects.
33    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
34    pub setof: bool,
35}
36
37/// One ADD ATTRIBUTE declaration. Unlike CREATE TYPE, ALTER applies column declaration lowering, including SERIAL.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct CompositeAttributeAddition {
40    pub attribute: CompositeAttributeDefinition,
41    pub declaration: super::ColumnDeclaration,
42}
43
44/// One DROP ATTRIBUTE action; missing-name handling and dependency behavior remain execution-time decisions.
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct CompositeAttributeRemoval {
47    pub name: String,
48    pub if_exists: bool,
49    pub cascade: bool,
50}
51
52/// Creation-time positional binding of a stored ROW constructor. Type references use the same canonical-name lifecycle as stored casts; attribute numbers survive additions and dropped slots.
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(deny_unknown_fields)]
55pub struct CompositeRowBinding {
56    pub ty: String,
57    pub attributes: Vec<i16>,
58    /// Physical width and cast syntax captured before the descriptor can change.
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub layout: Option<CompositeRowLayout>,
61    /// Types of the admitted arguments; absent only in predecessor definitions.
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub argument_types: Option<Vec<ColumnType>>,
64}
65
66/// Definition-time layout of a typed ROW, including positions that were already dropped.
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(deny_unknown_fields)]
69pub struct CompositeRowLayout {
70    pub width: i16,
71    pub implicit: bool,
72}
73
74/// A composite type bound by catalog identity: a standalone composite type or the row type of a relation. The schema and name are the catalog's current names; equality compares the type identity only.
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct CompositeTypeReference {
77    pub schema: String,
78    pub name: String,
79    /// The row type's `pg_type` OID.
80    pub oid: u32,
81    pub array_oid: u32,
82    /// The `pg_class` OID of the relation that describes the attributes (`pg_type.typrelid`).
83    pub relation_oid: u32,
84}
85
86impl PartialEq for CompositeTypeReference {
87    fn eq(&self, other: &Self) -> bool {
88        self.oid == other.oid
89    }
90}
91
92impl Eq for CompositeTypeReference {}
93
94impl CompositeTypeReference {
95    /// Copy the owned catalog names under the production reservation; the identities are inline.
96    pub fn clone_with_control(
97        &self,
98        control: &ProductionControl<'_>,
99    ) -> Result<Produced<Self>, ValueRetentionError> {
100        let schema = control.copy_text(&self.schema)?;
101        let name = control.copy_text(&self.name)?;
102        let (schema, schema_memory) = schema.into_parts();
103        let (name, name_memory) = name.into_parts();
104        control.finish(
105            Self {
106                schema,
107                name,
108                oid: self.oid,
109                array_oid: self.array_oid,
110                relation_oid: self.relation_oid,
111            },
112            control.combine(schema_memory, name_memory),
113        )
114    }
115}