1use crate::ast::ColumnType;
8use crate::{SQLError, SQLParam};
9use uqa_core::{
10 memory::{Produced, ProductionControl},
11 Value,
12};
13
14use crate::schema::ScalarTypeSchema;
15use crate::{scalar_call_arguments, RowSchema, ScalarExpr};
16
17use super::FunctionTypeResolver;
18
19#[doc(hidden)]
21pub type FunctionCallArgumentSignature = (Vec<Option<String>>, Vec<Option<ColumnType>>, bool);
22
23#[doc(hidden)]
25pub fn function_call_argument_signature(
26 arguments: &[ScalarExpr],
27 schema: &dyn ScalarTypeSchema,
28 params: &[SQLParam],
29 resolver: Option<&dyn FunctionTypeResolver>,
30) -> Result<FunctionCallArgumentSignature, SQLError> {
31 let call_arguments = scalar_call_arguments(arguments)?;
32 let explicit_variadic = call_arguments
33 .iter()
34 .any(|argument| argument.explicit_variadic);
35 let mut argument_names = Vec::with_capacity(call_arguments.len());
36 let mut argument_types = Vec::with_capacity(call_arguments.len());
37 for argument in call_arguments {
38 argument_names.push(argument.name.map(str::to_string));
39 let argument_type =
40 common_context_expression_type(argument.value, schema, params, resolver)?;
41 argument_types.push(effective_overload_argument_type_with_params(
42 argument.value,
43 argument_type,
44 params,
45 ));
46 }
47 Ok((argument_names, argument_types, explicit_variadic))
48}
49
50pub(super) fn local_routine_name(name: &str) -> String {
51 let lower = name.to_ascii_lowercase();
52 lower
53 .strip_prefix("pg_catalog.")
54 .unwrap_or(&lower)
55 .to_string()
56}
57
58pub(super) fn numeric_type() -> ColumnType {
59 ColumnType::Numeric {
60 precision: None,
61 scale: None,
62 }
63}
64
65pub(super) fn base_type(mut ty: &ColumnType) -> &ColumnType {
66 while let ColumnType::Domain { base, .. } = ty {
67 ty = base;
68 }
69 ty.without_temporal_modifiers()
70}
71
72pub(crate) fn array_element_type(ty: &ColumnType) -> Option<&ColumnType> {
73 match base_type(ty) {
74 ColumnType::Array(element) => Some(element),
75 ColumnType::Int2Vector => Some(&ColumnType::SmallInteger),
76 ColumnType::OidVector => Some(&ColumnType::Oid),
77 _ => None,
78 }
79}
80
81pub fn values_column_types(
82 rows: &[Vec<ScalarExpr>],
83 params: &[SQLParam],
84) -> Result<Vec<Option<ColumnType>>, SQLError> {
85 let width = rows.first().map_or(0, Vec::len);
86 let empty = RowSchema::default();
87 let mut types = vec![None; width];
88 for row in rows {
89 if row.len() != width {
90 return Err(SQLError::TypeMismatch(
91 "VALUES lists must all be the same length".into(),
92 ));
93 }
94 for (position, expression) in row.iter().enumerate() {
95 types[position] = merge_optional_types(
96 types[position].take(),
97 common_context_expression_type(expression, &empty, params, None)?,
98 )?;
99 }
100 }
101 Ok(types
102 .into_iter()
103 .map(|ty| ty.or(Some(ColumnType::Text)))
104 .collect())
105}
106
107pub fn common_context_expression_type(
109 expression: &ScalarExpr,
110 schema: &dyn ScalarTypeSchema,
111 params: &[SQLParam],
112 resolver: Option<&dyn FunctionTypeResolver>,
113) -> Result<Option<ColumnType>, SQLError> {
114 common_context_expression_type_with_control(
115 expression,
116 schema,
117 params,
118 resolver,
119 &ProductionControl::uncontrolled(),
120 )
121 .map(|ty| {
122 ty.map(|ty| {
123 ty.into_uncontrolled()
124 .expect("ordinary common-context inference has no reservation")
125 })
126 })
127}
128
129pub(super) fn common_context_expression_type_with_control(
130 expression: &ScalarExpr,
131 schema: &dyn ScalarTypeSchema,
132 params: &[SQLParam],
133 resolver: Option<&dyn FunctionTypeResolver>,
134 control: &ProductionControl<'_>,
135) -> Result<Option<Produced<ColumnType>>, SQLError> {
136 control.check()?;
137 if matches!(expression, ScalarExpr::Literal(Value::Str(_) | Value::Null)) {
138 return Ok(None);
139 }
140 super::scalar_type_inner_with_control(expression, schema, params, resolver, control)
141}
142
143#[doc(hidden)]
145pub fn effective_overload_argument_type(
146 expression: &ScalarExpr,
147 resolved: Option<ColumnType>,
148) -> Option<ColumnType> {
149 if effective_overload_argument_type_ref_with_params(expression, resolved.as_ref(), &[])
150 .is_some()
151 {
152 resolved
153 } else {
154 None
155 }
156}
157
158#[doc(hidden)]
160pub fn effective_overload_argument_type_with_params(
161 expression: &ScalarExpr,
162 resolved: Option<ColumnType>,
163 params: &[SQLParam],
164) -> Option<ColumnType> {
165 if effective_overload_argument_type_ref_with_params(expression, resolved.as_ref(), params)
166 .is_some()
167 {
168 resolved
169 } else {
170 None
171 }
172}
173
174pub(super) fn effective_overload_argument_type_ref_with_params<'a>(
176 expression: &ScalarExpr,
177 resolved: Option<&'a ColumnType>,
178 params: &[SQLParam],
179) -> Option<&'a ColumnType> {
180 if let ScalarExpr::Param(index) = expression {
181 if index
182 .checked_sub(1)
183 .and_then(|index| params.get(index))
184 .is_some_and(|parameter| parameter.declared_scalar_type().is_some())
185 {
186 return resolved;
187 }
188 }
189 if matches!(expression, ScalarExpr::Literal(Value::Str(_) | Value::Null))
190 || matches!(expression, ScalarExpr::Param(_)) && matches!(resolved, Some(ColumnType::Text))
191 {
192 None
193 } else {
194 resolved
195 }
196}
197
198pub(super) fn parameter_type_with_control(
199 parameter: &SQLParam,
200 control: &ProductionControl<'_>,
201) -> Result<Option<Produced<ColumnType>>, SQLError> {
202 control.check()?;
203 let scalar = match parameter {
204 SQLParam::Scalar(value) => return value_type_with_control(value, control),
205 SQLParam::TypedScalar { ty, .. } => return Ok(Some(ty.clone_with_control(control)?)),
206 SQLParam::Vector(values) => u32::try_from(values.len()).ok().map(ColumnType::Vector),
207 SQLParam::Tensor(values) => values
208 .first()
209 .and_then(|values| u32::try_from(values.len()).ok())
210 .map(ColumnType::Tensor),
211 };
212 scalar
213 .map(|ty| {
214 control
215 .finish(ty, control.empty_reservation())
216 .map_err(Into::into)
217 })
218 .transpose()
219}
220
221pub(crate) fn value_type(value: &Value) -> Option<ColumnType> {
222 value_type_with_control(value, &ProductionControl::uncontrolled())
223 .expect("ordinary value type inference cannot be cancelled or limited")
224 .map(|value| {
225 value
226 .into_uncontrolled()
227 .expect("ordinary value type has no reservation")
228 })
229}
230
231pub(crate) fn value_type_with_control(
232 value: &Value,
233 control: &ProductionControl<'_>,
234) -> Result<Option<Produced<ColumnType>>, SQLError> {
235 control.check()?;
236 let scalar = match value {
237 Value::Null | Value::Map(_) => None,
238 Value::Void => Some(ColumnType::Void),
239 Value::Row(_) | Value::Record(_) => Some(ColumnType::Record),
240 Value::Bool(_) => Some(ColumnType::Boolean),
241 Value::Int(value) if i32::try_from(*value).is_ok() => Some(ColumnType::Integer),
242 Value::Int(_) => Some(ColumnType::BigInteger),
243 Value::Float(_) => Some(ColumnType::DoublePrecision),
244 Value::Decimal(_) => Some(numeric_type()),
245 Value::Str(_) => Some(ColumnType::Text),
246 Value::FixedChar(value) => {
247 let mut count = 0_usize;
248 for _ in value.chars() {
249 control.check()?;
250 count += 1;
251 }
252 u32::try_from(count).ok().map(ColumnType::Character)
253 }
254 Value::Bytes(_) => Some(ColumnType::Bytea),
255 Value::Temporal(value) => Some(match value {
256 uqa_core::TemporalValue::Date { .. } => ColumnType::Date,
257 uqa_core::TemporalValue::Time { .. } => ColumnType::Time,
258 uqa_core::TemporalValue::TimeTz { .. } => ColumnType::TimeTz,
259 uqa_core::TemporalValue::Timestamp { .. } => ColumnType::Timestamp,
260 uqa_core::TemporalValue::TimestampTz { .. } => ColumnType::TimestampTz,
261 uqa_core::TemporalValue::Interval { .. } => ColumnType::Interval,
262 }),
263 Value::Json(_) => Some(ColumnType::Json),
264 Value::JsonB(_) => Some(ColumnType::JsonB),
265 Value::LegacyVector(vector) => Some(match vector.kind() {
266 uqa_core::LegacyVectorKind::SmallInteger => ColumnType::Int2Vector,
267 uqa_core::LegacyVectorKind::Oid => ColumnType::OidVector,
268 }),
269 Value::Array(array) => {
270 let mut element = None;
271 if !merge_array_element_types(array.elements(), &mut element, control)? {
272 return Ok(None);
273 }
274 return element
275 .map(|element| ColumnType::array_with_control(element, control).map_err(Into::into))
276 .transpose();
277 }
278 Value::List(values) => {
279 let mut element = None;
280 for value in values {
281 let next = value_type_with_control(value, control)?;
282 match merge_value_types(element, next, control) {
283 Ok(merged) => element = merged,
284 Err(error) if matches!(error.sqlstate(), Some("53200" | "57014")) => {
285 return Err(error)
286 }
287 Err(_) => return Ok(None),
288 }
289 }
290 return element
291 .map(|element| ColumnType::array_with_control(element, control).map_err(Into::into))
292 .transpose();
293 }
294 };
295 scalar
296 .map(|ty| {
297 control
298 .finish(ty, control.empty_reservation())
299 .map_err(Into::into)
300 })
301 .transpose()
302}
303
304fn merge_array_element_types(
305 values: &[Value],
306 element: &mut Option<Produced<ColumnType>>,
307 control: &ProductionControl<'_>,
308) -> Result<bool, SQLError> {
309 for value in values {
310 control.check()?;
311 if let Value::List(nested) = value {
312 if !merge_array_element_types(nested, element, control)? {
313 return Ok(false);
314 }
315 } else {
316 match merge_value_types(
317 element.take(),
318 value_type_with_control(value, control)?,
319 control,
320 ) {
321 Ok(merged) => *element = merged,
322 Err(error) if matches!(error.sqlstate(), Some("53200" | "57014")) => {
323 return Err(error)
324 }
325 Err(_) => return Ok(false),
326 }
327 }
328 }
329 Ok(true)
330}
331
332pub(super) fn merge_value_types(
333 left: Option<Produced<ColumnType>>,
334 right: Option<Produced<ColumnType>>,
335 control: &ProductionControl<'_>,
336) -> Result<Option<Produced<ColumnType>>, SQLError> {
337 control.check()?;
338 match (left, right) {
339 (None, other) | (other, None) => Ok(other),
340 (Some(left), Some(right)) if *left == *right => Ok(Some(left)),
341 (Some(left), Some(right)) => common_type_with_control(&left, &right, control).map(Some),
342 }
343}
344
345pub(super) fn merge_optional_types(
346 left: Option<ColumnType>,
347 right: Option<ColumnType>,
348) -> Result<Option<ColumnType>, SQLError> {
349 match (left, right) {
350 (None, other) | (other, None) => Ok(other),
351 (Some(left), Some(right)) => common_type(&left, &right).map(Some),
352 }
353}
354
355pub fn common_type(left: &ColumnType, right: &ColumnType) -> Result<ColumnType, SQLError> {
356 common_type_with_control(left, right, &ProductionControl::uncontrolled()).map(|value| {
357 value
358 .into_uncontrolled()
359 .expect("ordinary common type has no reservation")
360 })
361}
362
363pub(super) fn common_type_with_control(
365 left: &ColumnType,
366 right: &ColumnType,
367 control: &ProductionControl<'_>,
368) -> Result<Produced<ColumnType>, SQLError> {
369 control.check()?;
370 if left == right {
371 return left.clone_with_control(control).map_err(Into::into);
372 }
373 if left != left.without_temporal_modifiers() || right != right.without_temporal_modifiers() {
374 return common_type_with_control(
375 left.without_temporal_modifiers(),
376 right.without_temporal_modifiers(),
377 control,
378 );
379 }
380 if matches!(left, ColumnType::Domain { .. }) || matches!(right, ColumnType::Domain { .. }) {
381 return common_type_with_control(base_type(left), base_type(right), control);
382 }
383 let scalar = if let Some(numeric) = common_numeric_type(left, right) {
384 numeric
385 } else if matches!(left, ColumnType::Oid) && is_integral_type(right)
386 || matches!(right, ColumnType::Oid) && is_integral_type(left)
387 {
388 ColumnType::Oid
389 } else if left.is_character_string() && right.is_character_string() {
390 match left {
391 ColumnType::Bpchar | ColumnType::Character(_) => ColumnType::Bpchar,
392 ColumnType::Varchar(_) => ColumnType::Varchar(None),
393 ColumnType::Name => ColumnType::Name,
394 _ => ColumnType::Text,
395 }
396 } else {
397 match (left, right) {
398 (ColumnType::Date, ColumnType::Timestamp)
399 | (ColumnType::Timestamp, ColumnType::Date) => ColumnType::Timestamp,
400 (ColumnType::Date | ColumnType::Timestamp, ColumnType::TimestampTz)
401 | (ColumnType::TimestampTz, ColumnType::Date | ColumnType::Timestamp) => {
402 ColumnType::TimestampTz
403 }
404 (ColumnType::Array(left), ColumnType::Array(right)) => {
405 return ColumnType::array_with_control(
406 common_type_with_control(left, right, control)?,
407 control,
408 )
409 .map_err(Into::into)
410 }
411 _ => {
412 return Err(SQLError::TypeMismatch(format!(
413 "types {} and {} cannot be matched",
414 left.sql_name(),
415 right.sql_name()
416 )))
417 }
418 }
419 };
420 control
421 .finish(scalar, control.empty_reservation())
422 .map_err(Into::into)
423}
424
425pub(super) mod case;
426
427fn is_integral_type(ty: &ColumnType) -> bool {
428 matches!(
429 base_type(ty),
430 ColumnType::SmallInteger | ColumnType::Integer | ColumnType::BigInteger
431 )
432}
433
434pub(super) fn common_numeric_type(left: &ColumnType, right: &ColumnType) -> Option<ColumnType> {
435 let rank = numeric_rank(left)?.max(numeric_rank(right)?);
436 Some(match rank {
437 0 => ColumnType::SmallInteger,
438 1 => ColumnType::Integer,
439 2 => ColumnType::BigInteger,
440 3 => numeric_type(),
441 4 => ColumnType::Real,
442 _ => ColumnType::DoublePrecision,
443 })
444}
445
446pub(super) fn numeric_rank(ty: &ColumnType) -> Option<u8> {
447 match ty {
448 ColumnType::SmallInteger => Some(0),
449 ColumnType::Integer => Some(1),
450 ColumnType::BigInteger => Some(2),
451 ColumnType::Numeric { .. } => Some(3),
452 ColumnType::Real => Some(4),
453 ColumnType::DoublePrecision => Some(5),
454 _ => None,
455 }
456}
457
458pub(super) fn same_operator_type_with_control(
460 left: &ColumnType,
461 right: &ColumnType,
462 control: &ProductionControl<'_>,
463) -> Result<bool, uqa_core::ValueRetentionError> {
464 fn element(mut ty: &ColumnType) -> &ColumnType {
465 while let ColumnType::Array(inner) = ty {
466 ty = inner;
467 }
468 ty
469 }
470 let left = base_type(left);
471 let right = base_type(right);
472 let (left, right) = match (left, right) {
473 (ColumnType::Array(left), ColumnType::Array(right)) => (element(left), element(right)),
474 _ => (left, right),
475 };
476 let left = left.without_type_modifiers_with_control(control)?;
477 let right = right.without_type_modifiers_with_control(control)?;
478 Ok(*left == *right)
479}
480
481#[cfg(test)]
482mod production_tests;