1use uqa_core::{
10 memory::{Produced, ProductionControl},
11 Value,
12};
13
14use crate::ast::{ColumnType, InternalColumnRef};
15use crate::error::{Result, SQLError};
16use crate::params::SQLParam;
17use crate::result::ResultRow;
18
19mod casting;
20mod regtype;
21pub use casting::{
22 cast_value_with_type_resolution, cast_value_with_type_resolution_with_control,
23 coercion_type_name, read_catalog_array_input, read_catalog_input, CatalogInputFunctions,
24};
25pub(crate) use casting::{requires_catalog_constant_input, requires_domain_array_input};
26pub use regtype::{
27 format_regtype_elements_with_control, format_regtype_value, format_regtype_value_with_control,
28};
29
30pub trait EngineHook {
35 fn require_builtin_execute(&self, _binding: &crate::ast::FunctionBinding) -> Result<()> {
37 Ok(())
38 }
39
40 fn transaction_timestamp_micros(&self) -> Option<i64> {
42 None
43 }
44
45 fn statement_timestamp_micros(&self) -> Option<i64> {
47 None
48 }
49
50 fn nextval(&self, name: &str) -> Result<i64>;
51 fn currval(&self, name: &str) -> Result<i64>;
52 fn lastval(&self) -> Result<i64> {
53 Err(SQLError::Unsupported(
54 "lastval requires an engine hook implementation".into(),
55 ))
56 }
57 fn setval(&self, name: &str, value: i64, is_called: bool) -> Result<i64>;
58
59 fn call_scalar_function(&self, _name: &str, _args: &[Value]) -> Option<Result<Value>> {
60 None
61 }
62
63 fn call_bound_builtin_function(
68 &self,
69 _binding: &crate::ast::FunctionBinding,
70 _args: &[(Option<String>, Value)],
71 ) -> Option<Result<Value>> {
72 None
73 }
74
75 fn has_scalar_functions(&self) -> bool {
76 true
77 }
78
79 fn enum_labels(&self) -> Option<&dyn super::enums::EnumLabelCatalog> {
81 None
82 }
83
84 fn composite_types(&self) -> Option<&dyn super::composites::CompositeTypeCatalog> {
86 None
87 }
88
89 fn resolve_type_name(&self, _name: &str) -> std::result::Result<Option<ColumnType>, String> {
91 Ok(None)
92 }
93
94 fn cast_domain(
96 &self,
97 _value: &Value,
98 _source: Option<&str>,
99 _target: &ColumnType,
100 ) -> Result<Option<Value>> {
101 Ok(None)
102 }
103
104 fn resolve_regtype_input(&self, _name: &str) -> Result<Option<i64>> {
106 Ok(None)
107 }
108
109 fn resolve_regclass(&self, _name: &str) -> std::result::Result<Option<i64>, String> {
111 Ok(None)
112 }
113
114 fn resolve_regclass_input(&self, name: &str) -> Result<Option<i64>> {
116 self.resolve_regclass(name).map_err(SQLError::Internal)
117 }
118
119 fn resolve_regproc(&self, _name: &str) -> Result<Option<i64>> {
121 Ok(None)
122 }
123
124 fn resolve_regprocedure(&self, _name: &str) -> std::result::Result<Option<i64>, String> {
126 Ok(None)
127 }
128
129 fn resolve_regprocedure_input(&self, name: &str) -> Result<Option<i64>> {
131 self.resolve_regprocedure(name).map_err(SQLError::Internal)
132 }
133
134 fn resolve_regrole(&self, _name: &str) -> Result<Option<i64>> {
136 Ok(None)
137 }
138
139 fn resolve_regnamespace(&self, name: &str) -> Result<Option<i64>> {
141 self.resolve_regobject(&ColumnType::Regnamespace, name)
142 }
143
144 fn resolve_regobject(&self, ty: &ColumnType, name: &str) -> Result<Option<i64>> {
146 match ty {
147 ColumnType::Regclass => self.resolve_regclass_input(name),
148 ColumnType::Regprocedure => self.resolve_regprocedure(name).map_err(SQLError::Internal),
149 ColumnType::Regrole => self.resolve_regrole(name),
150 ColumnType::Regproc | ColumnType::Regnamespace | ColumnType::Regtype => Ok(None),
151 _ => Err(SQLError::Internal(format!(
152 "unsupported regobject lookup type `{}`",
153 ty.sql_name()
154 ))),
155 }
156 }
157
158 fn resolve_regtype_output(
160 &self,
161 _ty: &ColumnType,
162 _oid: i64,
163 ) -> std::result::Result<Option<String>, String> {
164 Ok(None)
165 }
166
167 fn current_schema(&self) -> std::result::Result<Option<String>, String> {
169 Ok(Some("public".into()))
170 }
171
172 fn current_user(&self) -> std::result::Result<Option<String>, crate::SQLError> {
173 Ok(None)
174 }
175
176 fn session_user(&self) -> std::result::Result<Option<String>, crate::SQLError> {
177 Ok(None)
178 }
179
180 fn runtime_parameter(&self, _name: &str) -> Result<Option<String>> {
182 Err(SQLError::Unsupported(
183 "engine hook does not provide session settings".into(),
184 ))
185 }
186
187 fn sleep(&self, _duration: std::time::Duration) -> Result<()> {
189 Err(SQLError::Unsupported(
190 "engine hook does not provide session sleeps".into(),
191 ))
192 }
193
194 fn set_runtime_parameter(
196 &self,
197 _name: &str,
198 _value: Option<&str>,
199 _local: bool,
200 ) -> Result<String> {
201 Err(SQLError::Unsupported(
202 "engine hook does not provide session settings".into(),
203 ))
204 }
205
206 fn current_schemas(
208 &self,
209 _include_implicit: bool,
210 ) -> std::result::Result<Option<Vec<String>>, String> {
211 Ok(None)
212 }
213
214 fn random_value(&self) -> std::result::Result<Option<f64>, String> {
217 Ok(None)
218 }
219
220 fn random_u64(&self) -> std::result::Result<Option<u64>, String> {
224 Ok(None)
225 }
226
227 fn set_random_seed(&self, _seed: f64) -> std::result::Result<bool, String> {
230 Ok(false)
231 }
232
233 fn call_user_function(
238 &self,
239 _name: &str,
240 _args: &[(Option<String>, Value)],
241 ) -> Option<Result<Value>> {
242 None
243 }
244
245 fn call_bound_user_function(
246 &self,
247 _binding: &crate::ast::FunctionBinding,
248 _args: &[(Option<String>, Value)],
249 ) -> Option<Result<Value>> {
250 None
251 }
252}
253
254pub trait RowLookup {
258 fn column(&self, name: &str) -> Option<&Value>;
259
260 fn column_is_ambiguous(&self, _name: &str) -> bool {
264 false
265 }
266
267 fn qualified_column(&self, qualifier: &str, column: &str) -> Option<&Value>;
268
269 fn qualified_column_is_ambiguous(&self, _qualifier: &str, _column: &str) -> bool {
271 false
272 }
273
274 fn positional_column(&self, _index: usize) -> Option<&Value> {
279 None
280 }
281
282 fn internal_column(&self, _column: InternalColumnRef) -> Option<&Value> {
285 None
286 }
287
288 fn score_source(&self, _qualifier: Option<&str>) -> Option<&Value> {
290 None
291 }
292
293 fn score_source_is_ambiguous(&self, _qualifier: Option<&str>) -> bool {
295 false
296 }
297
298 fn visit_columns(&self, _visitor: &mut dyn FnMut(&str, &Value)) {}
303}
304
305impl RowLookup for ResultRow {
306 fn column(&self, name: &str) -> Option<&Value> {
307 self.get(name)
308 }
309
310 fn qualified_column(&self, _qualifier: &str, _column: &str) -> Option<&Value> {
311 None
312 }
313
314 fn visit_columns(&self, visitor: &mut dyn FnMut(&str, &Value)) {
315 for (column, value) in self {
316 visitor(column, value);
317 }
318 }
319}
320
321pub struct EvalContext<'a> {
322 pub row: Option<&'a ResultRow>,
323 row_lookup: Option<&'a dyn RowLookup>,
324 pub params: &'a [SQLParam],
325 pub engine: Option<&'a dyn EngineHook>,
326}
327
328impl<'a> EvalContext<'a> {
329 pub fn new(row: Option<&'a ResultRow>, params: &'a [SQLParam]) -> Self {
330 Self {
331 row,
332 row_lookup: row.map(|row| row as &dyn RowLookup),
333 params,
334 engine: None,
335 }
336 }
337
338 pub fn from_row_lookup(row: &'a dyn RowLookup, params: &'a [SQLParam]) -> Self {
339 Self {
340 row: None,
344 row_lookup: Some(row),
345 params,
346 engine: None,
347 }
348 }
349
350 pub fn with_engine(mut self, engine: &'a dyn EngineHook) -> Self {
351 self.engine = Some(engine);
352 self
353 }
354
355 pub(super) fn row_lookup(&self) -> Result<&'a dyn RowLookup> {
356 self.row_lookup
357 .ok_or_else(|| SQLError::Internal("column reference without row context".into()))
358 }
359
360 pub fn column_value(&self, name: &str) -> Result<Value> {
364 self.column_value_with_control(name, &ProductionControl::uncontrolled())
365 .map(|value| value.into_uncontrolled().expect("ordinary column value"))
366 }
367
368 pub fn column_value_with_control(
370 &self,
371 name: &str,
372 control: &ProductionControl<'_>,
373 ) -> Result<Produced<Value>> {
374 control.check()?;
375 let row = self.row_lookup()?;
376 if row.column_is_ambiguous(name) {
377 return Err(SQLError::AmbiguousColumn(name.to_string()));
378 }
379 Ok(control.copy_value(row.column(name).unwrap_or(&Value::Null))?)
380 }
381
382 pub fn qualified_column_value(&self, qualifier: &str, column: &str) -> Result<Value> {
384 self.qualified_column_value_with_control(
385 qualifier,
386 column,
387 &ProductionControl::uncontrolled(),
388 )
389 .map(|value| {
390 value
391 .into_uncontrolled()
392 .expect("ordinary qualified column value")
393 })
394 }
395
396 pub fn qualified_column_value_with_control(
398 &self,
399 qualifier: &str,
400 column: &str,
401 control: &ProductionControl<'_>,
402 ) -> Result<Produced<Value>> {
403 control.check()?;
404 let row = self.row_lookup()?;
405 if row.qualified_column_is_ambiguous(qualifier, column) {
406 return Err(SQLError::AmbiguousColumn(format!("{qualifier}.{column}")));
407 }
408 Ok(control.copy_value(
409 row.qualified_column(qualifier, column)
410 .unwrap_or(&Value::Null),
411 )?)
412 }
413}
414
415#[cfg(test)]
416mod production_tests;