Skip to main content

uqa_sql/schema/sequences/
definition.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Normalize and validate SQL sequence declarations independently of allocation state.
8use crate::ast::{AlterSequence, CreateSequence, SequenceBound, SequenceDataType};
9use crate::SQLError;
10
11/// Fully specified SQL sequence options after defaults and ALTER actions are applied.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct SequenceDefinition {
14    pub start: i64,
15    pub increment: i64,
16    pub data_type: SequenceDataType,
17    pub min_value: i64,
18    pub max_value: i64,
19    pub cycle: bool,
20    pub cache_size: i64,
21}
22
23impl SequenceDefinition {
24    #[must_use]
25    pub fn initial(start: i64, increment: i64, data_type: SequenceDataType) -> Self {
26        let (type_min, type_max) = data_type.bounds();
27        Self {
28            start,
29            increment,
30            data_type,
31            min_value: if increment > 0 { 1 } else { type_min },
32            max_value: if increment > 0 { type_max } else { -1 },
33            cycle: false,
34            cache_size: 1,
35        }
36    }
37
38    #[must_use]
39    pub fn from_create(sequence: &CreateSequence) -> Self {
40        let mut definition = Self::initial(sequence.start, sequence.increment, sequence.data_type);
41        definition.min_value = sequence.min_value.unwrap_or(definition.min_value);
42        definition.max_value = sequence.max_value.unwrap_or(definition.max_value);
43        definition.cycle = sequence.cycle;
44        definition.cache_size = sequence.cache_size;
45        definition
46    }
47}
48
49#[must_use]
50pub fn altered_sequence_definition(
51    mut state: SequenceDefinition,
52    alter: &AlterSequence,
53) -> SequenceDefinition {
54    if let Some(data_type) = alter.data_type {
55        let (old_type_min, old_type_max) = state.data_type.bounds();
56        let (new_type_min, new_type_max) = data_type.bounds();
57        if state.min_value == old_type_min {
58            state.min_value = new_type_min;
59        }
60        if state.max_value == old_type_max {
61            state.max_value = new_type_max;
62        }
63        state.data_type = data_type;
64    }
65    if let Some(increment) = alter.increment {
66        state.increment = increment;
67    }
68    let (type_min, type_max) = state.data_type.bounds();
69    match alter.min_value {
70        SequenceBound::Unchanged => {}
71        SequenceBound::Default => {
72            state.min_value = if state.increment > 0 { 1 } else { type_min };
73        }
74        SequenceBound::Value(value) => state.min_value = value,
75    }
76    match alter.max_value {
77        SequenceBound::Unchanged => {}
78        SequenceBound::Default => {
79            state.max_value = if state.increment > 0 { type_max } else { -1 };
80        }
81        SequenceBound::Value(value) => state.max_value = value,
82    }
83    if let Some(start_val) = alter.start {
84        state.start = start_val;
85    }
86    if let Some(cycle) = alter.cycle {
87        state.cycle = cycle;
88    }
89    if let Some(cache_size) = alter.cache_size {
90        state.cache_size = cache_size;
91    }
92    state
93}
94
95pub fn validate_sequence_definition(
96    state: &SequenceDefinition,
97    current: Option<i64>,
98) -> Result<(), SQLError> {
99    let validate_current = current.is_some();
100    let current = current.unwrap_or_default();
101    let invalid = |message| SQLError::Routine {
102        sqlstate: "22023".into(),
103        message,
104    };
105    if state.increment == 0 {
106        return Err(invalid("INCREMENT must not be zero".into()));
107    }
108    if state.cache_size <= 0 {
109        return Err(invalid(format!(
110            "CACHE ({}) must be greater than zero",
111            state.cache_size
112        )));
113    }
114    let (type_min, type_max) = state.data_type.bounds();
115    if !(type_min..=type_max).contains(&state.max_value) {
116        return Err(invalid(format!(
117            "MAXVALUE ({}) is out of range for sequence data type {}",
118            state.max_value,
119            state.data_type.sql_name()
120        )));
121    }
122    if !(type_min..=type_max).contains(&state.min_value) {
123        return Err(invalid(format!(
124            "MINVALUE ({}) is out of range for sequence data type {}",
125            state.min_value,
126            state.data_type.sql_name()
127        )));
128    }
129    if state.min_value >= state.max_value {
130        return Err(invalid(format!(
131            "MINVALUE ({}) must be less than MAXVALUE ({})",
132            state.min_value, state.max_value
133        )));
134    }
135    if state.start < state.min_value {
136        return Err(invalid(format!(
137            "START value ({}) cannot be less than MINVALUE ({})",
138            state.start, state.min_value
139        )));
140    }
141    if state.start > state.max_value {
142        return Err(invalid(format!(
143            "START value ({}) cannot be greater than MAXVALUE ({})",
144            state.start, state.max_value
145        )));
146    }
147    if validate_current && current < state.min_value {
148        return Err(invalid(format!(
149            "RESTART value ({}) cannot be less than MINVALUE ({})",
150            current, state.min_value
151        )));
152    }
153    if validate_current && current > state.max_value {
154        return Err(invalid(format!(
155            "RESTART value ({}) cannot be greater than MAXVALUE ({})",
156            current, state.max_value
157        )));
158    }
159    Ok(())
160}