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 type_oid = fields.type_oid().unwrap_or(reference.oid);
74 let descriptor = super::descriptor(engine.composite_types(), type_oid)?;
75 control.check()?;
76 let mut output = ProductionVec::new(*control);
77 output.reserve(descriptor.attributes.len())?;
78 for (index, attribute) in descriptor.attributes.iter().enumerate() {
79 let value = fields
80 .get(index)
81 .filter(|(name, _)| *name == attribute.name)
82 .or_else(|| fields.iter().find(|(name, _)| *name == attribute.name))
83 .map_or(&Value::Null, |(_, value)| value);
84 let value = materialize(value, &attribute.ty, engine, control)?;
85 let name = control.copy_text(&attribute.name)?;
86 let (value, memory) = value.into_parts();
87 let (name, name_memory) = name.into_parts();
88 output.push_produced(
89 control.finish((name, value), control.combine(memory, name_memory))?,
90 )?;
91 }
92 let (fields, memory) =
93 uqa_core::RecordValue::with_control(output.finish()?, Some(type_oid), control)?
94 .into_parts();
95 control
96 .finish(Value::Record(fields), memory)
97 .map_err(Into::into)
98 }
99 (ColumnType::Array(element), Value::Array(array)) => {
100 let element_type_oid = array.element_type_oid();
101 let elements = array_elements(array.elements(), element, engine, control)?;
102 let mut bounds = ProductionVec::new(*control);
103 for bound in array.lower_bounds() {
104 bounds.push_copy(*bound)?;
105 }
106 let array =
107 ArrayValue::with_lower_bounds_with_control(elements, bounds.finish()?, control)?
108 .ok_or_else(|| {
109 SQLError::Internal(
110 "retained composite constant changed array dimensions".into(),
111 )
112 })?;
113 let (array, memory) = array.into_parts();
114 control
115 .finish(
116 Value::Array(array.with_element_type_oid(element_type_oid)),
117 memory,
118 )
119 .map_err(Into::into)
120 }
121 _ => control.copy_value(value).map_err(Into::into),
122 }
123}
124
125fn array_elements(
126 values: &[Value],
127 element: &ColumnType,
128 engine: &dyn EngineHook,
129 control: &ProductionControl<'_>,
130) -> Result<Produced<Vec<Value>>, SQLError> {
131 let mut element = element;
132 while let ColumnType::Array(inner) = element {
133 element = inner;
134 }
135 let mut output = ProductionVec::new(*control);
136 output.reserve(values.len())?;
137 for value in values {
138 let value = if let Value::List(values) = value {
139 let (values, memory) = array_elements(values, element, engine, control)?.into_parts();
140 control.finish(Value::List(values), memory)?
141 } else {
142 materialize(value, element, engine, control)?
143 };
144 output.push_produced(value)?;
145 }
146 output.finish().map_err(Into::into)
147}
148
149#[cfg(test)]
150mod tests;