uqa_sql/expr/composites/
literal.rs1use crate::{expr::EngineHook, ColumnType, SQLError};
10use uqa_core::{
11 memory::{Produced, ProductionControl, ProductionVec},
12 ArrayValue, Value,
13};
14
15pub fn contains_records(value: &Value) -> bool {
16 inspect_records(value, &mut || Ok(())).expect("uncontrolled value inspection")
17}
18
19fn inspect_records(
20 value: &Value,
21 check: &mut impl FnMut() -> Result<(), SQLError>,
22) -> Result<bool, SQLError> {
23 check()?;
24 let values = match value {
25 Value::Record(_) => return Ok(true),
26 Value::Array(array) => array.elements(),
27 Value::List(values) => values.as_slice(),
28 _ => return Ok(false),
29 };
30 for value in values {
31 if inspect_records(value, check)? {
32 return Ok(true);
33 }
34 }
35 Ok(false)
36}
37
38pub fn evaluate_with_control(
39 value: &Value,
40 ty: &str,
41 engine: Option<&dyn EngineHook>,
42 control: &ProductionControl<'_>,
43) -> Result<Produced<Value>, SQLError> {
44 control.check()?;
45 if !inspect_records(value, &mut || control.check().map_err(Into::into))? {
46 return control.copy_value(value).map_err(Into::into);
47 }
48 let Some(engine) = engine else {
49 return control.copy_value(value).map_err(Into::into);
50 };
51 let ty = engine
52 .resolve_type_name(ty)
53 .map_err(SQLError::Internal)?
54 .map(|ty| ty.retain_external_with_control(control))
55 .transpose()?;
56 control.check()?;
57 match ty.as_deref() {
58 Some(ty) => materialize(value, ty, engine, control),
59 None => control.copy_value(value).map_err(Into::into),
60 }
61}
62
63fn materialize(
64 value: &Value,
65 ty: &ColumnType,
66 engine: &dyn EngineHook,
67 control: &ProductionControl<'_>,
68) -> Result<Produced<Value>, SQLError> {
69 control.check()?;
70 match (ty, value) {
71 (ColumnType::Domain { base, .. }, _) => materialize(value, base, engine, control),
72 (ColumnType::Composite(reference), Value::Record(fields)) => {
73 let descriptor = super::descriptor(engine.composite_types(), reference.oid)?;
74 control.check()?;
75 let mut output = ProductionVec::new(*control);
76 output.reserve(descriptor.attributes.len())?;
77 for (index, attribute) in descriptor.attributes.iter().enumerate() {
78 let value = fields
79 .get(index)
80 .filter(|(name, _)| *name == attribute.name)
81 .or_else(|| fields.iter().find(|(name, _)| *name == attribute.name))
82 .map_or(&Value::Null, |(_, value)| value);
83 let value = materialize(value, &attribute.ty, engine, control)?;
84 let name = control.copy_text(&attribute.name)?;
85 let (value, memory) = value.into_parts();
86 let (name, name_memory) = name.into_parts();
87 output.push_produced(
88 control.finish((name, value), control.combine(memory, name_memory))?,
89 )?;
90 }
91 let (fields, memory) = output.finish()?.into_parts();
92 control
93 .finish(Value::Record(fields), memory)
94 .map_err(Into::into)
95 }
96 (ColumnType::Array(element), Value::Array(array)) => {
97 let elements = array_elements(array.elements(), element, engine, control)?;
98 let mut bounds = ProductionVec::new(*control);
99 for bound in array.lower_bounds() {
100 bounds.push_copy(*bound)?;
101 }
102 let array =
103 ArrayValue::with_lower_bounds_with_control(elements, bounds.finish()?, control)?
104 .ok_or_else(|| {
105 SQLError::Internal(
106 "retained composite constant changed array dimensions".into(),
107 )
108 })?;
109 let (array, memory) = array.into_parts();
110 control
111 .finish(Value::Array(array), memory)
112 .map_err(Into::into)
113 }
114 _ => control.copy_value(value).map_err(Into::into),
115 }
116}
117
118fn array_elements(
119 values: &[Value],
120 element: &ColumnType,
121 engine: &dyn EngineHook,
122 control: &ProductionControl<'_>,
123) -> Result<Produced<Vec<Value>>, SQLError> {
124 let mut element = element;
125 while let ColumnType::Array(inner) = element {
126 element = inner;
127 }
128 let mut output = ProductionVec::new(*control);
129 output.reserve(values.len())?;
130 for value in values {
131 let value = if let Value::List(values) = value {
132 let (values, memory) = array_elements(values, element, engine, control)?.into_parts();
133 control.finish(Value::List(values), memory)?
134 } else {
135 materialize(value, element, engine, control)?
136 };
137 output.push_produced(value)?;
138 }
139 output.finish().map_err(Into::into)
140}
141
142#[cfg(test)]
143mod tests;