uqa_sql/assignment/conversion/
carrier.rs1use crate::{ColumnType, SQLError};
10use uqa_core::{
11 memory::{Produced, ProductionControl, ProductionVec},
12 ArrayValue, LegacyVectorKind, Value,
13};
14
15pub fn contains_legacy_vectors(ty: &ColumnType) -> bool {
17 element_kind(ty).is_some()
18}
19
20pub fn normalize_legacy_vector_carrier_with_control(
22 value: &Value,
23 ty: &ColumnType,
24 control: &ProductionControl<'_>,
25) -> Result<Option<Produced<Value>>, SQLError> {
26 control.check()?;
27 if matches!(value, Value::Null) {
28 return Ok(None);
29 }
30 match ty {
31 ColumnType::Domain { base, .. } => {
32 normalize_legacy_vector_carrier_with_control(value, base, control)
33 }
34 ColumnType::Int2Vector | ColumnType::OidVector => {
35 let name = super::column_type_name(ty);
36 if matches!(value, Value::LegacyVector(vector) if vector.kind().type_name() == name) {
37 return Ok(None);
38 }
39 crate::expr::cast_value_from_with_control(value, name, Some(name), control).map(Some)
40 }
41 ColumnType::Array(element) => {
42 let Some(kind) = element_kind(element) else {
43 return Ok(None);
44 };
45 let (elements, bounds) = match value {
46 Value::Array(array) => (array.elements(), Some(array.lower_bounds())),
47 Value::List(elements) => (elements.as_slice(), None),
48 _ => return Err(invalid_array()),
49 };
50 if bounds.is_some() && canonical_elements(elements, kind, control)? {
51 return Ok(None);
52 }
53 let (elements, collapsed) = normalize_elements(elements, kind, control)?;
54 let array = if let Some(bounds) = bounds {
55 let count = bounds
56 .len()
57 .checked_sub(usize::from(collapsed))
58 .ok_or_else(invalid_array)?;
59 let mut output = ProductionVec::new(*control);
60 for bound in &bounds[..count] {
61 output.push_copy(*bound)?;
62 }
63 ArrayValue::with_lower_bounds_with_control(elements, output.finish()?, control)?
64 } else {
65 ArrayValue::try_new_with_control(elements, control)?
66 }
67 .ok_or_else(invalid_array)?;
68 let (array, memory) = array.into_parts();
69 Ok(Some(control.finish(Value::Array(array), memory)?))
70 }
71 _ => Ok(None),
72 }
73}
74
75fn element_kind(ty: &ColumnType) -> Option<LegacyVectorKind> {
76 match ty {
77 ColumnType::Domain { base, .. } | ColumnType::Array(base) => element_kind(base),
78 ColumnType::Int2Vector => Some(LegacyVectorKind::SmallInteger),
79 ColumnType::OidVector => Some(LegacyVectorKind::Oid),
80 _ => None,
81 }
82}
83
84fn canonical_elements(
85 values: &[Value],
86 kind: LegacyVectorKind,
87 control: &ProductionControl<'_>,
88) -> Result<bool, SQLError> {
89 for value in values {
90 control.check()?;
91 match value {
92 Value::Null => {}
93 Value::LegacyVector(vector) if vector.kind() == kind => {}
94 Value::List(nested)
95 if !nested.is_empty() && canonical_elements(nested, kind, control)? => {}
96 _ => return Ok(false),
97 }
98 }
99 Ok(true)
100}
101
102fn normalize_elements(
103 values: &[Value],
104 kind: LegacyVectorKind,
105 control: &ProductionControl<'_>,
106) -> Result<(Produced<Vec<Value>>, bool), SQLError> {
107 let mut output = ProductionVec::new(*control);
108 output.reserve(values.len())?;
109 let mut collapsed = false;
110 for value in values {
111 control.check()?;
112 let value = match value {
113 Value::Null => control.copy_value(value)?,
114 Value::List(nested) => {
115 let mut flat = true;
116 for item in nested {
117 control.check()?;
118 flat &= matches!(item, Value::Int(_));
119 }
120 if flat {
121 collapsed = true;
122 crate::expr::cast_value_from_with_control(
123 value,
124 kind.type_name(),
125 Some(kind.type_name()),
126 control,
127 )?
128 } else {
129 let (values, child_collapsed) = normalize_elements(nested, kind, control)?;
130 collapsed |= child_collapsed;
131 let (values, memory) = values.into_parts();
132 control.finish(Value::List(values), memory)?
133 }
134 }
135 _ => crate::expr::cast_value_from_with_control(
136 value,
137 kind.type_name(),
138 Some(kind.type_name()),
139 control,
140 )?,
141 };
142 output.push_produced(value)?;
143 }
144 Ok((output.finish()?, collapsed))
145}
146
147fn invalid_array() -> SQLError {
148 SQLError::TypeMismatch("invalid stored array of legacy vectors".into())
149}
150
151#[cfg(test)]
152mod tests;
153
154pub(in crate::assignment) fn normalize_existing(
155 value: Value,
156 ty: &ColumnType,
157) -> Result<Value, SQLError> {
158 normalize_legacy_vector_carrier_with_control(&value, ty, &ProductionControl::uncontrolled())?
159 .map_or(Ok(value), |value| {
160 value
161 .into_uncontrolled()
162 .map_err(|_| SQLError::Internal("ordinary carrier normalization".into()))
163 })
164}