uqa-sql 0.5.2

PostgreSQL-compatible SQL compiler built on libpg_query
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

//! Column and table constraint nodes shared by CREATE and ALTER TABLE.

use serde::{Deserialize, Serialize};

use super::{
    deserialize_auto_increment, AutoIncrement, ColumnType, Expr, GeneratedColumn, OnCommitAction,
    PartitionBound, PartitionSpec, RelationPersistence, TableHierarchy,
};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct ColumnDef {
    pub name: String,
    pub ty: ColumnType,
    /// Durable identity of this catalog column. Logical names can change while a fixed transaction snapshot continues to address the same column.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub object_id: Option<[u8; 16]>,
    /// Stable relation attribute number. Parsed declarations have no number until publication; renaming and dropping other columns never change it.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub attribute_number: Option<i16>,
    /// Value exposed for physical rows captured before this column was added. This is the catalog equivalent of `PostgreSQL`'s `attmissingval`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub missing_value: Option<uqa_core::Value>,
    pub primary_key: bool,
    pub not_null: bool,
    /// Whether `NOT NULL` was declared as its own constraint instead of being
    /// implied by `PRIMARY KEY` or an auto-incrementing identity.
    #[serde(default)]
    pub not_null_explicit: bool,
    /// Durable `PostgreSQL` 18 `NOT NULL` constraint name. Parsing leaves an
    /// unnamed declaration as `None`; table registration assigns and persists
    /// `PostgreSQL`'s generated name before the constraint becomes visible.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub not_null_name: Option<String>,
    /// Independent NOT NULL lifetime and public catalog OID, retained through column, relation, and constraint renames.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub not_null_identity: Option<ConstraintCatalogIdentity>,
    /// Whether the named `NOT NULL` constraint has been validated against
    /// every pre-existing row. `NOT VALID` still enforces future writes.
    #[serde(default = "default_true")]
    pub not_null_validated: bool,
    /// Durable `NO INHERIT` state for `PostgreSQL` 18 named `NOT NULL`
    /// constraints.
    #[serde(default)]
    pub not_null_no_inherit: bool,
    /// Whether this relation declares its NOT NULL constraint locally, independently from inherited parent constraints. Older serialized definitions retain their original local catalog projection.
    #[serde(default = "default_true", skip_serializing_if = "is_true")]
    pub not_null_is_local: bool,
    /// Sequence provenance for `SERIAL` / `BIGSERIAL` and identity columns. The custom decoder accepts the legacy boolean representation written by releases that merged both SQL features into one table counter.
    #[serde(
        default,
        deserialize_with = "deserialize_auto_increment",
        skip_serializing_if = "Option::is_none"
    )]
    pub auto_increment: Option<AutoIncrement>,
    /// `UNIQUE` column constraint -- the engine rejects an INSERT
    /// whose value for this column already exists in another row.
    #[serde(default)]
    pub unique: bool,
    /// `DEFAULT <expr>`. Evaluated at INSERT time when the column is
    /// not present in the row tuple. Persisted in catalog metadata so
    /// reopened engines keep the same INSERT semantics.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub default: Option<Expr>,
    /// `PostgreSQL` 18 generated-column definition. Stored values are refreshed
    /// on every row write; virtual values are evaluated from the physical row
    /// only when a logical row is read.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generated: Option<GeneratedColumn>,
    /// The `pg_attrdef` OID of the default or generation expression, allocated when the expression was set; an expression set before OIDs were recorded derives it from the table and column names.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub default_catalog_oid: Option<i64>,
    /// `CHECK (<expr>)` column-level constraint. Evaluated at INSERT
    /// (and UPDATE-replace) time against the row being written.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub check: Option<Expr>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub check_name: Option<String>,
    #[serde(default = "default_true")]
    pub check_enforced: bool,
    #[serde(default = "default_true")]
    pub check_validated: bool,
    #[serde(default)]
    pub check_no_inherit: bool,
    /// Whether this relation declares its column CHECK locally, independently from inherited copies. Missing legacy origin retains the historical local projection.
    #[serde(default = "default_true", skip_serializing_if = "is_true")]
    pub check_is_local: bool,
    /// Durable identity of the column CHECK, preserved across constraint and relation renames.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub check_object_id: Option<[u8; 16]>,
    /// Public CHECK address allocated separately from its durable incarnation.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub check_catalog_oid: Option<i64>,
    /// Column-level `REFERENCES parent[(col)]` foreign key. An omitted column is resolved to the referenced primary key before publication.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub references: Option<ForeignKeyRef>,
}

impl ColumnDef {
    /// A nullable column without a default, generation expression, identity or constraint.
    #[must_use]
    pub fn nullable(name: impl Into<String>, ty: ColumnType) -> Self {
        Self {
            name: name.into(),
            ty,
            object_id: None,
            attribute_number: None,
            missing_value: None,
            primary_key: false,
            not_null: false,
            not_null_explicit: false,
            not_null_name: None,
            not_null_identity: None,
            not_null_validated: true,
            not_null_no_inherit: false,
            not_null_is_local: true,
            auto_increment: None,
            unique: false,
            default: None,
            generated: None,
            check: None,
            check_name: None,
            check_enforced: true,
            check_validated: true,
            check_no_inherit: false,
            check_is_local: true,
            check_object_id: None,
            check_catalog_oid: None,
            default_catalog_oid: None,
            references: None,
        }
    }
}

pub use uqa_core::catalog_identity::CatalogObjectIdentity as ConstraintCatalogIdentity;

/// `REFERENCES table[(column)]` reference target.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct ForeignKeyRef {
    /// Incarnation of the selected unique index; names are retained only for diagnostics and legacy conversion.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub referenced_index: Option<[u8; 16]>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub referenced_key: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    /// Logical foreign-key identity shared by a partition family for enforcement and deferred events.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub object_id: Option<[u8; 16]>,
    /// Independent catalog row lifetime and OID, preserved through renames and distinct in each partition.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub catalog_identity: Option<ConstraintCatalogIdentity>,
    pub table: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub column: Option<String>,
    #[serde(default)]
    pub on_update: ForeignKeyAction,
    #[serde(default)]
    pub on_delete: ForeignKeyAction,
    #[serde(default)]
    pub match_type: ForeignKeyMatch,
    #[serde(default = "default_true")]
    pub enforced: bool,
    #[serde(default = "default_true")]
    pub validated: bool,
    #[serde(default)]
    pub deferrable: bool,
    #[serde(default)]
    pub initially_deferred: bool,
    /// `REFERENCES table (..., PERIOD column)` temporal coverage semantics.
    #[serde(default)]
    pub period: bool,
    /// The constraints derived on the partitions of a partitioned referenced table, as [`ForeignKey::referenced_partitions`] holds them.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub referenced_partitions: Vec<super::ReferencedPartitionConstraint>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateTable {
    pub name: String,
    /// Local SQL relation identifier used while binding expressions declared inside the table definition.
    pub qualifier: String,
    pub columns: Vec<ColumnDef>,
    /// `CREATE TABLE IF NOT EXISTS` - silently ignore the statement
    /// when a table with this name already exists.
    pub if_not_exists: bool,
    /// Table-level `CHECK (...)` constraints. Each entry is an
    /// expression that must evaluate truthy against every row.
    #[allow(dead_code)]
    pub checks: Vec<TableCheck>,
    /// Table-level `FOREIGN KEY (col, ...) REFERENCES parent(col, ...)`.
    pub foreign_keys: Vec<ForeignKey>,
    /// Foreign keys in the statement's written order, before inheritance adds any constraints. Only the statement carries this order.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub foreign_key_order: Vec<DeclaredForeignKey>,
    /// Every declared `PRIMARY KEY` / `UNIQUE` constraint, including
    /// column-level declarations. Keeping the typed key (rather than only
    /// setting per-column flags) preserves composite-key and `NULLS NOT
    /// DISTINCT` semantics through planning and catalog persistence.
    #[serde(default)]
    pub key_constraints: Vec<TableKeyConstraint>,
    /// `PostgreSQL` relation persistence selected by `TEMPORARY` or `UNLOGGED`.
    #[serde(default)]
    pub persistence: RelationPersistence,
    /// Transaction-end behavior for temporary tables.
    #[serde(default)]
    pub on_commit: OnCommitAction,
    /// Direct inheritance and declarative-partitioning metadata. The engine
    /// resolves parent names and merges their row types atomically at create
    /// time, then persists the canonical hierarchy with the table schema.
    #[serde(default)]
    pub hierarchy: TableHierarchy,
    /// Each CHECK the statement declares, in written order, which `DefineRelation` adds in that order. Only the statement carries it.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub check_order: Vec<DeclaredCheck>,
    /// The columns, NOT NULL table constraints and table PRIMARY KEY constraints the statement declares, in written order, as `transformCreateStmt` examines them. Only the statement carries it.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub element_order: Vec<DeclaredElement>,
    /// The NOT NULL constraints the statement declares, in the order `AddRelationNotNullConstraints` takes them once the relation's columns exist: each column's clause or implied constraint and each table constraint in written order, then the ones a table PRIMARY KEY adds. Only the statement carries it.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub not_null_declarations: Vec<NotNullDeclaration>,
    /// The columns a `PARTITION OF` statement declares without a type, which are options on the parent's columns and take their types when the columns merge. Only the statement carries it.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub untyped_columns: Vec<String>,
}

/// An element of a CREATE TABLE statement that `transformCreateStmt` examines in written order.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeclaredElement {
    /// The statement's next column, with the clauses it writes.
    Column(ColumnDeclaration),
    /// A table constraint `NOT NULL column`.
    NotNull(NotNullDeclaration),
    /// A table PRIMARY KEY constraint, whose columns `transformIndexConstraints` makes NOT NULL after every element is examined.
    PrimaryKey { columns: Vec<String> },
    /// A table PRIMARY KEY or UNIQUE constraint that is DEFERRABLE or INITIALLY DEFERRED.
    DeferrableKey,
}

/// A foreign key written as a column clause or a table constraint.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeclaredForeignKey {
    Column(String),
    Table(usize),
}

/// A NOT NULL constraint a CREATE TABLE statement declares or implies, which `AddRelationNotNullConstraints` resolves against the relation's columns once they exist.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NotNullDeclaration {
    pub column: String,
    pub name: Option<String>,
    pub no_inherit: bool,
    /// Whether the statement wrote the constraint, as a column clause or a table constraint, rather than implying it with a PRIMARY KEY, SERIAL or identity.
    pub explicit: bool,
}

/// The clauses a column definition writes, in written order, which `transformColumnDefinition` checks against each other.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ColumnDeclaration {
    /// The column's type is SERIAL, SMALLSERIAL or BIGSERIAL, which adds a default after the written clauses.
    pub serial: bool,
    /// The column's type is an array of a SERIAL type.
    pub serial_array: bool,
    pub clauses: Vec<ColumnClause>,
}

/// One clause of a column definition.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ColumnClause {
    pub kind: ColumnClauseKind,
    /// The name a `CONSTRAINT` clause gives it.
    pub name: Option<String>,
    pub no_inherit: bool,
}

/// The kind of a column definition's clause.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ColumnClauseKind {
    Null,
    NotNull,
    Default,
    Identity,
    Generated,
    Check,
    PrimaryKey,
    Unique,
    ForeignKey,
    Deferrable,
    NotDeferrable,
    InitiallyDeferred,
    InitiallyImmediate,
    Enforced,
    NotEnforced,
}

/// Where a CHECK that a CREATE TABLE statement declares is held.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeclaredCheck {
    /// The CHECK the named column holds as its own.
    Column(String),
    /// The entry at this position among the statement's own entries of `CreateTable::checks`.
    Table(usize),
}

/// A syntactically valid `CREATE TABLE IF NOT EXISTS` whose definition must be analyzed only after execution has established that the target relation does not already exist.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeferredCreateTable {
    pub name: String,
    pub persistence: RelationPersistence,
    pub definition_sql: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TableKeyConstraintKind {
    PrimaryKey,
    Unique,
}

impl TableKeyConstraintKind {
    /// The constraint type as SQL spells it, which `PostgreSQL` diagnostics name.
    #[must_use]
    pub const fn sql_label(self) -> &'static str {
        match self {
            Self::PrimaryKey => "PRIMARY KEY",
            Self::Unique => "UNIQUE",
        }
    }
}

/// A table key whose columns are compared as one tuple.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TableKeyConstraint {
    /// Independent catalog row lifetime, retained while the owning index changes its name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub catalog_identity: Option<ConstraintCatalogIdentity>,
    /// The identity of the index that enforces the key (`conindid`), reserved before the constraint's own as `index_create` creates the index before `index_constraint_create`. Keys created before it was recorded bind their index when it is registered.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub index_identity: Option<ConstraintCatalogIdentity>,
    pub name: Option<String>,
    pub kind: TableKeyConstraintKind,
    pub columns: Vec<String>,
    /// Columns that the supporting index carries beside the key, in declaration order. They take no part in uniqueness, and `PostgreSQL` lets them repeat and name key columns.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub included_columns: Vec<String>,
    /// `PostgreSQL` UNIQUE keys normally treat every NULL-containing tuple as
    /// distinct. `UNIQUE NULLS NOT DISTINCT` opts into NULL equality.
    #[serde(default)]
    pub nulls_not_distinct: bool,
    /// The final key column is a range or multirange compared by overlap.
    #[serde(default)]
    pub without_overlaps: bool,
}

/// Durable table-level constraints that do not fit in `ColumnDef`.
///
/// `serde(default)` on the catalog field containing this structure keeps
/// databases written before constraint persistence backward compatible.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TableConstraintSet {
    /// Physical attribute metadata retained after a column is removed from the live schema.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub dropped_attributes: Vec<crate::catalog::relation_attributes::DroppedAttribute>,
    /// Distinguish a declared zero-column SQL relation from a schema-free document table. Missing legacy metadata retains inference from existing columns.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub columns_declared: Option<bool>,
    #[serde(default)]
    pub checks: Vec<TableCheck>,
    #[serde(default)]
    pub foreign_keys: Vec<ForeignKey>,
    #[serde(default)]
    pub key_constraints: Vec<TableKeyConstraint>,
    /// Stored alongside the table definition so reopen preserves `pg_class.relpersistence` for unlogged tables.
    #[serde(default)]
    pub persistence: RelationPersistence,
    /// Permanent and unlogged tables always use the default. Temporary tables are session-local and therefore never write this field to disk.
    #[serde(default)]
    pub on_commit: OnCommitAction,
    /// Durable relation hierarchy and partition-bound metadata.
    #[serde(default)]
    pub hierarchy: TableHierarchy,
    /// The relation's public OIDs, allocated when it was created. Tables created before OIDs were recorded derive them from their identity.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub catalog_oids: Option<crate::catalog::relation_oids::RelationCatalogOids>,
    /// The generated row-array type name, which can move independently when an explicit type takes its name.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub row_type_array_name: Option<String>,
}

/// `CHECK (expr)` constraint with an optional name (`CONSTRAINT <name>
/// CHECK (...)`).
#[derive(Debug, Clone, Serialize, Deserialize)]
#[expect(
    clippy::struct_excessive_bools,
    reason = "CHECK catalog flags are independent PostgreSQL properties"
)]
pub struct TableCheck {
    pub name: Option<String>,
    /// Durable identity of this CHECK, assigned when its definition is published.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub object_id: Option<[u8; 16]>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub catalog_oid: Option<i64>,
    /// Whether this relation declares this CHECK locally, independently from inherited copies. Missing legacy origin retains the historical local projection.
    #[serde(default = "default_true", skip_serializing_if = "is_true")]
    pub is_local: bool,
    pub expr: Expr,
    #[serde(default = "default_true")]
    pub enforced: bool,
    #[serde(default = "default_true")]
    pub validated: bool,
    #[serde(default)]
    pub no_inherit: bool,
    /// Runtime form of the bound CHECK retained after `DETACH PARTITION ... CONCURRENTLY`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub partition_constraint: Option<DetachedPartitionConstraint>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetachedPartitionConstraint {
    pub spec: PartitionSpec,
    pub bound: PartitionBound,
}

/// Table-level foreign key. Compilation preserves an omitted referenced column list as empty; validation fills it from the primary key before publication.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct ForeignKey {
    /// Incarnation of the selected unique index, retained across index and constraint renames.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub referenced_index: Option<[u8; 16]>,
    /// Name of the selected unique index in the referenced relation namespace.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub referenced_key: Option<String>,
    pub name: Option<String>,
    /// Logical foreign-key identity shared by a partition family for enforcement and deferred events.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub object_id: Option<[u8; 16]>,
    /// Independent catalog row lifetime and OID, preserved through renames and distinct in each partition.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub catalog_identity: Option<ConstraintCatalogIdentity>,
    pub local_columns: Vec<String>,
    pub ref_table: String,
    pub ref_columns: Vec<String>,
    #[serde(default)]
    pub on_update: ForeignKeyAction,
    #[serde(default)]
    pub on_delete: ForeignKeyAction,
    /// Optional column subset for `ON DELETE SET NULL (...)` and
    /// `ON DELETE SET DEFAULT (...)`. Empty means every local FK
    /// column participates.
    #[serde(default)]
    pub on_delete_set_columns: Vec<String>,
    #[serde(default)]
    pub match_type: ForeignKeyMatch,
    #[serde(default = "default_true")]
    pub enforced: bool,
    #[serde(default = "default_true")]
    pub validated: bool,
    #[serde(default)]
    pub deferrable: bool,
    #[serde(default)]
    pub initially_deferred: bool,
    /// The final local and referenced columns use `PostgreSQL` PERIOD coverage.
    #[serde(default)]
    pub period: bool,
    /// The constraints derived on the partitions of a partitioned referenced table, parents before their partitions, in the order `PostgreSQL` creates them. Only the foreign key a referencing relation declares holds them; the copies on its partitions hold none.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub referenced_partitions: Vec<super::ReferencedPartitionConstraint>,
}

const fn default_true() -> bool {
    true
}

#[expect(
    clippy::trivially_copy_pass_by_ref,
    reason = "serde skip_serializing_if requires a borrowed field"
)]
const fn is_true(value: &bool) -> bool {
    *value
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ForeignKeyAction {
    #[default]
    NoAction,
    Restrict,
    Cascade,
    SetNull,
    SetDefault,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ForeignKeyMatch {
    #[default]
    Simple,
    Full,
}