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 let oid = value.array_view().and_then(ArrayValue::element_type_oid);
70 Ok(Some(control.finish(
71 Value::Array(array.with_element_type_oid(oid)),
72 memory,
73 )?))
74 }
75 _ => Ok(None),
76 }
77}
78
79fn element_kind(ty: &ColumnType) -> Option<LegacyVectorKind> {
80 match ty {
81 ColumnType::Domain { base, .. } | ColumnType::Array(base) => element_kind(base),
82 ColumnType::Int2Vector => Some(LegacyVectorKind::SmallInteger),
83 ColumnType::OidVector => Some(LegacyVectorKind::Oid),
84 _ => None,
85 }
86}
87
88fn canonical_elements(
89 values: &[Value],
90 kind: LegacyVectorKind,
91 control: &ProductionControl<'_>,
92) -> Result<bool, SQLError> {
93 for value in values {
94 control.check()?;
95 match value {
96 Value::Null => {}
97 Value::LegacyVector(vector) if vector.kind() == kind => {}
98 Value::List(nested)
99 if !nested.is_empty() && canonical_elements(nested, kind, control)? => {}
100 _ => return Ok(false),
101 }
102 }
103 Ok(true)
104}
105
106fn normalize_elements(
107 values: &[Value],
108 kind: LegacyVectorKind,
109 control: &ProductionControl<'_>,
110) -> Result<(Produced<Vec<Value>>, bool), SQLError> {
111 let mut output = ProductionVec::new(*control);
112 output.reserve(values.len())?;
113 let mut collapsed = false;
114 for value in values {
115 control.check()?;
116 let value = match value {
117 Value::Null => control.copy_value(value)?,
118 Value::List(nested) => {
119 let mut flat = true;
120 for item in nested {
121 control.check()?;
122 flat &= matches!(item, Value::Int(_));
123 }
124 if flat {
125 collapsed = true;
126 crate::expr::cast_value_from_with_control(
127 value,
128 kind.type_name(),
129 Some(kind.type_name()),
130 control,
131 )?
132 } else {
133 let (values, child_collapsed) = normalize_elements(nested, kind, control)?;
134 collapsed |= child_collapsed;
135 let (values, memory) = values.into_parts();
136 control.finish(Value::List(values), memory)?
137 }
138 }
139 _ => crate::expr::cast_value_from_with_control(
140 value,
141 kind.type_name(),
142 Some(kind.type_name()),
143 control,
144 )?,
145 };
146 output.push_produced(value)?;
147 }
148 Ok((output.finish()?, collapsed))
149}
150
151fn invalid_array() -> SQLError {
152 SQLError::TypeMismatch("invalid stored array of legacy vectors".into())
153}
154
155#[cfg(test)]
156mod tests;
157
158pub(in crate::assignment) fn normalize_existing(
159 value: Value,
160 ty: &ColumnType,
161) -> Result<Value, SQLError> {
162 normalize_legacy_vector_carrier_with_control(&value, ty, &ProductionControl::uncontrolled())?
163 .map_or(Ok(value), |value| {
164 value
165 .into_uncontrolled()
166 .map_err(|_| SQLError::Internal("ordinary carrier normalization".into()))
167 })
168}