1#![forbid(unsafe_code)]
9
10use bitcode::{Decode, Encode};
11
12use crate::types::{FieldId, RowId, TableId};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, Default)]
16pub enum ConstraintEnforcementMode {
17 #[default]
19 Immediate,
20 Deferred,
22}
23
24impl ConstraintEnforcementMode {
25 #[inline]
26 pub const fn is_immediate(self) -> bool {
27 matches!(self, ConstraintEnforcementMode::Immediate)
28 }
29
30 #[inline]
31 pub const fn is_deferred(self) -> bool {
32 matches!(self, ConstraintEnforcementMode::Deferred)
33 }
34}
35
36pub type ConstraintId = u32;
41
42const TABLE_ID_BITS: u32 = (std::mem::size_of::<TableId>() * 8) as u32;
43const CONSTRAINT_ID_BITS: u32 = 64 - TABLE_ID_BITS;
44const CONSTRAINT_ID_MASK: u64 = (1u64 << CONSTRAINT_ID_BITS) - 1;
45
46const _: () = assert!(
47 std::mem::size_of::<ConstraintId>() * 8 <= CONSTRAINT_ID_BITS as usize,
48 "ConstraintId does not fit within allocated row id bits"
49);
50
51#[inline]
53pub fn encode_constraint_row_id(table_id: TableId, constraint_id: ConstraintId) -> RowId {
54 ((table_id as u64) << CONSTRAINT_ID_BITS) | (constraint_id as u64 & CONSTRAINT_ID_MASK)
55}
56
57#[inline]
59pub fn decode_constraint_row_id(row_id: RowId) -> (TableId, ConstraintId) {
60 let constraint_id = (row_id & CONSTRAINT_ID_MASK) as ConstraintId;
61 let table_id = (row_id >> CONSTRAINT_ID_BITS) as TableId;
62 (table_id, constraint_id)
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode)]
67pub enum ConstraintState {
68 Active,
69 Dropped,
70}
71
72impl ConstraintState {
73 #[inline]
74 pub fn is_active(self) -> bool {
75 matches!(self, ConstraintState::Active)
76 }
77}
78
79#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
81pub struct ConstraintRecord {
82 pub constraint_id: ConstraintId,
83 pub kind: ConstraintKind,
84 pub state: ConstraintState,
85 pub revision: u64,
87 pub last_modified_micros: u64,
89}
90
91impl ConstraintRecord {
92 pub fn is_active(&self) -> bool {
93 self.state.is_active()
94 }
95}
96
97#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
99pub enum ConstraintKind {
100 PrimaryKey(PrimaryKeyConstraint),
101 Unique(UniqueConstraint),
102 ForeignKey(ForeignKeyConstraint),
103 Check(CheckConstraint),
104}
105
106impl ConstraintKind {
107 pub fn field_ids(&self) -> &[FieldId] {
109 match self {
110 ConstraintKind::PrimaryKey(payload) => &payload.field_ids,
111 ConstraintKind::Unique(payload) => &payload.field_ids,
112 ConstraintKind::ForeignKey(payload) => &payload.referencing_field_ids,
113 ConstraintKind::Check(payload) => &payload.field_ids,
114 }
115 }
116}
117
118#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
120pub struct PrimaryKeyConstraint {
121 pub field_ids: Vec<FieldId>,
122}
123
124#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
126pub struct UniqueConstraint {
127 pub field_ids: Vec<FieldId>,
128}
129
130#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
132pub struct ForeignKeyConstraint {
133 pub referencing_field_ids: Vec<FieldId>,
134 pub referenced_table: TableId,
135 pub referenced_field_ids: Vec<FieldId>,
136 pub on_delete: ForeignKeyAction,
137 pub on_update: ForeignKeyAction,
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode)]
142pub enum ForeignKeyAction {
143 NoAction,
144 Restrict,
145}
146
147#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
152pub struct CheckConstraint {
153 pub field_ids: Vec<FieldId>,
154 pub expression_ref: ConstraintExpressionRef,
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encode, Decode)]
159pub struct ConstraintExpressionRef(pub u64);
160
161impl ConstraintExpressionRef {
162 pub const NONE: ConstraintExpressionRef = ConstraintExpressionRef(0);
163
164 #[inline]
165 pub fn is_none(self) -> bool {
166 self.0 == 0
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn constraint_row_id_round_trip() {
176 let samples: &[(TableId, ConstraintId)] = &[
177 (1, 1),
178 (42, 7),
179 (TableId::MAX, ConstraintId::MAX),
180 (123, 987_654),
181 ];
182
183 for &(table_id, constraint_id) in samples {
184 let row_id = encode_constraint_row_id(table_id, constraint_id);
185 let (decoded_table, decoded_constraint) = decode_constraint_row_id(row_id);
186 assert_eq!(decoded_table, table_id);
187 assert_eq!(decoded_constraint, constraint_id);
188 }
189 }
190
191 #[test]
192 fn constraint_record_active_state() {
193 let record = ConstraintRecord {
194 constraint_id: 1,
195 kind: ConstraintKind::PrimaryKey(PrimaryKeyConstraint {
196 field_ids: vec![1, 2],
197 }),
198 state: ConstraintState::Active,
199 revision: 5,
200 last_modified_micros: 123,
201 };
202 assert!(record.is_active());
203 }
204}