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
19mod inputs;
20pub use inputs::select_common_input_type;
21pub(crate) use inputs::select_common_input_type_in_with_control;
22pub(super) use inputs::{select_common_input_type_with_control, CommonInputType};
23
24pub(super) fn unknown_input(mut expression: &ScalarExpr) -> bool {
26 while let Ok(Some(clause)) = crate::catalog::collations::expression::scalar_clause(expression) {
27 expression = clause.argument;
28 }
29 matches!(
30 expression,
31 ScalarExpr::Literal(Value::Null | Value::Str(_)) | ScalarExpr::Param(_)
32 )
33}
34
35#[doc(hidden)]
37pub type FunctionCallArgumentSignature = (Vec<Option<String>>, Vec<Option<ColumnType>>, bool);
38
39#[doc(hidden)]
41pub fn function_call_argument_signature(
42 arguments: &[ScalarExpr],
43 schema: &dyn ScalarTypeSchema,
44 params: &[SQLParam],
45 resolver: Option<&dyn FunctionTypeResolver>,
46) -> Result<FunctionCallArgumentSignature, SQLError> {
47 let call_arguments = scalar_call_arguments(arguments)?;
48 let explicit_variadic = call_arguments
49 .iter()
50 .any(|argument| argument.explicit_variadic);
51 let mut argument_names = Vec::with_capacity(call_arguments.len());
52 let mut argument_types = Vec::with_capacity(call_arguments.len());
53 for argument in call_arguments {
54 argument_names.push(argument.name.map(str::to_string));
55 let argument_type =
56 common_context_expression_type(argument.value, schema, params, resolver)?;
57 argument_types.push(effective_overload_argument_type_with_params(
58 argument.value,
59 argument_type,
60 params,
61 ));
62 }
63 Ok((argument_names, argument_types, explicit_variadic))
64}
65
66pub(super) fn local_routine_name(name: &str) -> String {
67 let lower = name.to_ascii_lowercase();
68 lower
69 .strip_prefix("pg_catalog.")
70 .unwrap_or(&lower)
71 .to_string()
72}
73
74pub(super) fn numeric_type() -> ColumnType {
75 ColumnType::Numeric {
76 precision: None,
77 scale: None,
78 }
79}
80
81pub(crate) fn base_type(mut ty: &ColumnType) -> &ColumnType {
82 while let ColumnType::Domain { base, .. } = ty {
83 ty = base;
84 }
85 ty.without_temporal_modifiers()
86}
87
88pub(crate) fn literal_input_type_with_control(
90 mut ty: &ColumnType,
91 control: &ProductionControl<'_>,
92) -> Result<Produced<ColumnType>, SQLError> {
93 fn input_type(
94 ty: &ColumnType,
95 control: &ProductionControl<'_>,
96 ) -> Result<Produced<ColumnType>, SQLError> {
97 Ok(match ty {
98 ColumnType::IntervalWithFields { .. } | ColumnType::Domain { .. } => {
99 ty.clone_with_control(control)?
100 }
101 ColumnType::Array(element) => {
102 ColumnType::array_with_control(input_type(element, control)?, control)?
103 }
104 _ => ty.without_type_modifiers_with_control(control)?,
105 })
106 }
107 while let ColumnType::Domain { base, .. } = ty {
108 ty = base;
109 }
110 input_type(ty, control)
111}
112
113pub(crate) fn array_element_type(ty: &ColumnType) -> Option<&ColumnType> {
114 match base_type(ty) {
115 ColumnType::Array(element) => {
116 let mut element = element.as_ref();
117 while let ColumnType::Array(inner) = element {
118 element = inner;
119 }
120 Some(element)
121 }
122 ColumnType::Int2Vector => Some(&ColumnType::SmallInteger),
123 ColumnType::OidVector => Some(&ColumnType::Oid),
124 _ => None,
125 }
126}
127
128pub fn values_column_types(
129 rows: &[Vec<ScalarExpr>],
130 params: &[SQLParam],
131) -> Result<Vec<Option<ColumnType>>, SQLError> {
132 let width = rows.first().map_or(0, Vec::len);
133 let empty = RowSchema::default();
134 let control = ProductionControl::uncontrolled();
135 let mut types = (0..width)
136 .map(|_| CommonInputType::new())
137 .collect::<Vec<_>>();
138 for row in rows {
139 if row.len() != width {
140 return Err(SQLError::TypeMismatch(
141 "VALUES lists must all be the same length".into(),
142 ));
143 }
144 for (position, expression) in row.iter().enumerate() {
145 let ty = common_context_expression_type_with_control(
146 expression, &empty, params, None, &control,
147 )?;
148 if ty.is_some() || unknown_input(expression) {
149 types[position].include_owned(CommonTypeContext::Values, ty, &control)?;
150 }
151 }
152 }
153 types
154 .into_iter()
155 .map(|ty| {
156 ty.finish(&control).map(|ty| {
157 Some(
158 ty.into_uncontrolled()
159 .expect("ordinary VALUES type has no reservation"),
160 )
161 })
162 })
163 .collect()
164}
165
166pub fn common_context_expression_type(
168 expression: &ScalarExpr,
169 schema: &dyn ScalarTypeSchema,
170 params: &[SQLParam],
171 resolver: Option<&dyn FunctionTypeResolver>,
172) -> Result<Option<ColumnType>, SQLError> {
173 common_context_expression_type_with_control(
174 expression,
175 schema,
176 params,
177 resolver,
178 &ProductionControl::uncontrolled(),
179 )
180 .map(|ty| {
181 ty.map(|ty| {
182 ty.into_uncontrolled()
183 .expect("ordinary common-context inference has no reservation")
184 })
185 })
186}
187
188pub(super) fn common_context_expression_type_with_control(
189 expression: &ScalarExpr,
190 schema: &dyn ScalarTypeSchema,
191 params: &[SQLParam],
192 resolver: Option<&dyn FunctionTypeResolver>,
193 control: &ProductionControl<'_>,
194) -> Result<Option<Produced<ColumnType>>, SQLError> {
195 control.check()?;
196 if let Some(ty) = schema.observed_expression_type(expression) {
197 return super::inference::copy_type(ty, control);
198 }
199 if let Some(clause) = crate::catalog::collations::expression::scalar_clause(expression)? {
200 let ty = common_context_expression_type_with_control(
201 clause.argument,
202 schema,
203 params,
204 resolver,
205 control,
206 )?;
207 crate::catalog::collations::expression::select_with_control(
208 clause.name,
209 clause.oid,
210 ty.as_deref(),
211 resolver,
212 control,
213 )?;
214 return Ok(ty);
215 }
216 if matches!(expression, ScalarExpr::Literal(Value::Str(_) | Value::Null)) {
217 return Ok(None);
218 }
219 super::scalar_type_inner_with_control(expression, schema, params, resolver, control)
220}
221
222#[doc(hidden)]
224pub fn effective_overload_argument_type(
225 expression: &ScalarExpr,
226 resolved: Option<ColumnType>,
227) -> Option<ColumnType> {
228 if effective_overload_argument_type_ref_with_params(expression, resolved.as_ref(), &[])
229 .is_some()
230 {
231 resolved
232 } else {
233 None
234 }
235}
236
237#[doc(hidden)]
239pub fn effective_overload_argument_type_with_params(
240 expression: &ScalarExpr,
241 resolved: Option<ColumnType>,
242 params: &[SQLParam],
243) -> Option<ColumnType> {
244 if effective_overload_argument_type_ref_with_params(expression, resolved.as_ref(), params)
245 .is_some()
246 {
247 resolved
248 } else {
249 None
250 }
251}
252
253pub(super) fn effective_overload_argument_type_ref_with_params<'a>(
255 expression: &ScalarExpr,
256 resolved: Option<&'a ColumnType>,
257 params: &[SQLParam],
258) -> Option<&'a ColumnType> {
259 if let ScalarExpr::Param(index) = expression {
260 if index
261 .checked_sub(1)
262 .and_then(|index| params.get(index))
263 .is_some_and(|parameter| parameter.declared_scalar_type().is_some())
264 {
265 return resolved;
266 }
267 }
268 if matches!(expression, ScalarExpr::Literal(Value::Str(_) | Value::Null))
269 || matches!(expression, ScalarExpr::Param(_)) && matches!(resolved, Some(ColumnType::Text))
270 {
271 None
272 } else {
273 resolved
274 }
275}
276
277pub(super) fn parameter_type_with_control(
278 parameter: &SQLParam,
279 control: &ProductionControl<'_>,
280) -> Result<Option<Produced<ColumnType>>, SQLError> {
281 control.check()?;
282 let scalar = match parameter {
283 SQLParam::Scalar(value) => return value_type_with_control(value, control),
284 SQLParam::TypedScalar { ty, .. } | SQLParam::DeferredError { ty, .. } => {
285 return Ok(Some(ty.clone_with_control(control)?))
286 }
287 SQLParam::Vector(values) => u32::try_from(values.len()).ok().map(ColumnType::Vector),
288 SQLParam::Tensor(values) => values
289 .first()
290 .and_then(|values| u32::try_from(values.len()).ok())
291 .map(ColumnType::Tensor),
292 };
293 scalar
294 .map(|ty| {
295 control
296 .finish(ty, control.empty_reservation())
297 .map_err(Into::into)
298 })
299 .transpose()
300}
301
302pub(crate) fn value_type(value: &Value) -> Option<ColumnType> {
303 value_type_with_control(value, &ProductionControl::uncontrolled())
304 .expect("ordinary value type inference cannot be cancelled or limited")
305 .map(|value| {
306 value
307 .into_uncontrolled()
308 .expect("ordinary value type has no reservation")
309 })
310}
311
312pub(crate) fn value_type_with_control(
313 value: &Value,
314 control: &ProductionControl<'_>,
315) -> Result<Option<Produced<ColumnType>>, SQLError> {
316 control.check()?;
317 let scalar = match value {
318 Value::Null | Value::Map(_) | Value::Enum(_) | Value::Datum(_) => None,
320 Value::Void => Some(ColumnType::Void),
321 Value::Row(_) | Value::Record(_) => Some(ColumnType::Record),
322 Value::Bool(_) => Some(ColumnType::Boolean),
323 Value::Int(value) if i32::try_from(*value).is_ok() => Some(ColumnType::Integer),
324 Value::Int(_) => Some(ColumnType::BigInteger),
325 Value::Float(_) => Some(ColumnType::DoublePrecision),
326 Value::Decimal(_) => Some(numeric_type()),
327 Value::Str(_) => Some(ColumnType::Text),
328 Value::FixedChar(value) => {
329 let mut count = 0_usize;
330 for _ in value.chars() {
331 control.check()?;
332 count += 1;
333 }
334 u32::try_from(count).ok().map(ColumnType::Character)
335 }
336 Value::Bytes(_) => Some(ColumnType::Bytea),
337 Value::Temporal(value) => Some(match value {
338 uqa_core::TemporalValue::Date { .. } => ColumnType::Date,
339 uqa_core::TemporalValue::Time { .. } => ColumnType::Time,
340 uqa_core::TemporalValue::TimeTz { .. } => ColumnType::TimeTz,
341 uqa_core::TemporalValue::Timestamp { .. } => ColumnType::Timestamp,
342 uqa_core::TemporalValue::TimestampTz { .. } => ColumnType::TimestampTz,
343 uqa_core::TemporalValue::Interval { .. } => ColumnType::Interval,
344 }),
345 Value::Json(_) => Some(ColumnType::Json),
346 Value::JsonB(_) => Some(ColumnType::JsonB),
347 Value::LegacyVector(vector) => Some(match vector.kind() {
348 uqa_core::LegacyVectorKind::SmallInteger => ColumnType::Int2Vector,
349 uqa_core::LegacyVectorKind::Oid => ColumnType::OidVector,
350 }),
351 Value::Array(array) => {
352 let mut element = None;
353 if !merge_array_element_types(array.elements(), &mut element, control)? {
354 return Ok(None);
355 }
356 return element
357 .map(|element| ColumnType::array_with_control(element, control).map_err(Into::into))
358 .transpose();
359 }
360 Value::List(values) => {
361 let mut element = None;
362 for value in values {
363 let next = value_type_with_control(value, control)?;
364 match merge_value_types(element, next, control) {
365 Ok(merged) => element = merged,
366 Err(error) if matches!(error.sqlstate(), Some("53200" | "57014")) => {
367 return Err(error)
368 }
369 Err(_) => return Ok(None),
370 }
371 }
372 return element
373 .map(|element| ColumnType::array_with_control(element, control).map_err(Into::into))
374 .transpose();
375 }
376 };
377 scalar
378 .map(|ty| {
379 control
380 .finish(ty, control.empty_reservation())
381 .map_err(Into::into)
382 })
383 .transpose()
384}
385
386fn merge_array_element_types(
387 values: &[Value],
388 element: &mut Option<Produced<ColumnType>>,
389 control: &ProductionControl<'_>,
390) -> Result<bool, SQLError> {
391 for value in values {
392 control.check()?;
393 if let Value::List(nested) = value {
394 if !merge_array_element_types(nested, element, control)? {
395 return Ok(false);
396 }
397 } else {
398 match merge_value_types(
399 element.take(),
400 value_type_with_control(value, control)?,
401 control,
402 ) {
403 Ok(merged) => *element = merged,
404 Err(error) if matches!(error.sqlstate(), Some("53200" | "57014")) => {
405 return Err(error)
406 }
407 Err(_) => return Ok(false),
408 }
409 }
410 }
411 Ok(true)
412}
413
414pub(super) fn merge_value_types(
415 left: Option<Produced<ColumnType>>,
416 right: Option<Produced<ColumnType>>,
417 control: &ProductionControl<'_>,
418) -> Result<Option<Produced<ColumnType>>, SQLError> {
419 control.check()?;
420 match (left, right) {
421 (None, other) | (other, None) => Ok(other),
422 (Some(left), Some(right)) if *left == *right => Ok(Some(left)),
423 (Some(left), Some(right)) => common_type_with_control(&left, &right, control).map(Some),
424 }
425}
426
427pub(super) fn read_unknown_literals<'a>(
429 expressions: impl IntoIterator<Item = &'a ScalarExpr>,
430 target: &ColumnType,
431) -> Result<(), SQLError> {
432 if super::catalog_input_type(target) {
433 return Ok(());
434 }
435 let target = base_type(target).without_type_modifiers().catalog_name();
437 for expression in expressions {
438 if let ScalarExpr::Literal(Value::Str(text)) = expression {
439 crate::expr::cast_value_from(&Value::Str(text.clone()), &target, None)?;
440 }
441 }
442 Ok(())
443}
444
445pub fn common_type(left: &ColumnType, right: &ColumnType) -> Result<ColumnType, SQLError> {
446 common_type_with_control(left, right, &ProductionControl::uncontrolled()).map(|value| {
447 value
448 .into_uncontrolled()
449 .expect("ordinary common type has no reservation")
450 })
451}
452
453pub fn common_type_in(
455 context: CommonTypeContext,
456 left: &ColumnType,
457 right: &ColumnType,
458) -> Result<ColumnType, SQLError> {
459 select_pair_with_control(left, right, &ProductionControl::uncontrolled())
460 .map(|value| {
461 value
462 .into_uncontrolled()
463 .expect("ordinary common type has no reservation")
464 })
465 .map_err(|failure| failure.in_context(context))
466}
467
468#[derive(Clone, Copy, Debug, PartialEq, Eq)]
470pub enum CommonTypeContext {
471 Union,
472 Intersect,
473 Except,
474 Values,
475 Case,
476 Coalesce,
477 Array,
478 Greatest,
479 Least,
480 In,
481 JoinUsing,
482 Cycle,
483}
484
485impl CommonTypeContext {
486 pub const fn label(self) -> &'static str {
487 match self {
488 Self::Union => "UNION",
489 Self::Intersect => "INTERSECT",
490 Self::Except => "EXCEPT",
491 Self::Values => "VALUES",
492 Self::Case => "CASE",
493 Self::Coalesce => "COALESCE",
494 Self::Array => "ARRAY",
495 Self::Greatest => "GREATEST",
496 Self::Least => "LEAST",
497 Self::In => "IN",
498 Self::JoinUsing => "JOIN/USING",
499 Self::Cycle => "CYCLE",
500 }
501 }
502
503 pub const fn coercion_label(self) -> &'static str {
505 match self {
506 Self::Case => "CASE/WHEN",
507 other => other.label(),
508 }
509 }
510
511 pub const fn set_operation(kind: crate::ast::SetOpKind) -> Self {
512 match kind {
513 crate::ast::SetOpKind::Union => Self::Union,
514 crate::ast::SetOpKind::Intersect => Self::Intersect,
515 crate::ast::SetOpKind::Except => Self::Except,
516 }
517 }
518
519 pub fn function(name: &str) -> Option<Self> {
521 match local_routine_name(name).as_str() {
522 "coalesce" => Some(Self::Coalesce),
523 "greatest" => Some(Self::Greatest),
524 "least" => Some(Self::Least),
525 _ => None,
526 }
527 }
528}
529
530#[derive(Debug)]
532pub(super) enum CommonTypeFailure {
533 Unmatched(Box<(ColumnType, ColumnType)>),
535 Inconvertible(Box<(ColumnType, ColumnType)>),
537 Failed(Box<SQLError>),
538}
539
540impl CommonTypeFailure {
541 fn failed(error: impl Into<SQLError>) -> Self {
542 Self::Failed(Box::new(error.into()))
543 }
544
545 fn in_context(self, context: CommonTypeContext) -> SQLError {
547 match self {
548 Self::Unmatched(types) => SQLError::Routine {
549 sqlstate: "42804".into(),
550 message: format!(
551 "{} types {} and {} cannot be matched",
552 context.label(),
553 types.0.sql_name(),
554 types.1.sql_name()
555 ),
556 },
557 Self::Inconvertible(types) => SQLError::Routine {
558 sqlstate: "42846".into(),
559 message: format!(
560 "{} could not convert type {} to {}",
561 context.coercion_label(),
562 types.0.sql_name(),
563 types.1.sql_name()
564 ),
565 },
566 Self::Failed(error) => *error,
567 }
568 }
569
570 fn without_context(self, left: &ColumnType, right: &ColumnType) -> SQLError {
572 match self {
573 Self::Failed(error) => *error,
574 Self::Unmatched(_) | Self::Inconvertible(_) => SQLError::TypeMismatch(format!(
575 "types {} and {} cannot be matched",
576 left.sql_name(),
577 right.sql_name()
578 )),
579 }
580 }
581}
582
583pub(super) fn common_type_with_control(
585 left: &ColumnType,
586 right: &ColumnType,
587 control: &ProductionControl<'_>,
588) -> Result<Produced<ColumnType>, SQLError> {
589 select_pair_with_control(left, right, control)
590 .map_err(|failure| failure.without_context(left, right))
591}
592
593fn select_pair_with_control(
595 left: &ColumnType,
596 right: &ColumnType,
597 control: &ProductionControl<'_>,
598) -> Result<Produced<ColumnType>, CommonTypeFailure> {
599 control.check().map_err(CommonTypeFailure::failed)?;
600 if left == right {
601 return left
602 .clone_with_control(control)
603 .map_err(CommonTypeFailure::failed);
604 }
605 if left != left.without_temporal_modifiers() || right != right.without_temporal_modifiers() {
606 return select_pair_with_control(
607 left.without_temporal_modifiers(),
608 right.without_temporal_modifiers(),
609 control,
610 );
611 }
612 if matches!(left, ColumnType::Domain { .. }) || matches!(right, ColumnType::Domain { .. }) {
613 return select_pair_with_control(base_type(left), base_type(right), control);
614 }
615 let scalar = if let Some(numeric) = common_numeric_type(left, right) {
616 numeric
617 } else if matches!(left, ColumnType::Oid) && is_integral_type(right)
618 || matches!(right, ColumnType::Oid) && is_integral_type(left)
619 {
620 ColumnType::Oid
621 } else if left.is_character_string() && right.is_character_string() {
622 match left {
623 ColumnType::Bpchar | ColumnType::Character(_) => ColumnType::Bpchar,
624 ColumnType::Varchar(_) => ColumnType::Varchar(None),
625 ColumnType::Name => ColumnType::Name,
626 _ => ColumnType::Text,
627 }
628 } else {
629 match (left, right) {
630 (ColumnType::Date, ColumnType::Timestamp)
631 | (ColumnType::Timestamp, ColumnType::Date) => ColumnType::Timestamp,
632 (ColumnType::Date | ColumnType::Timestamp, ColumnType::TimestampTz)
633 | (ColumnType::TimestampTz, ColumnType::Date | ColumnType::Timestamp) => {
634 ColumnType::TimestampTz
635 }
636 (ColumnType::Array(_), ColumnType::Array(_)) => {
637 let element = select_pair_with_control(
639 innermost_array_element(left),
640 innermost_array_element(right),
641 control,
642 )?;
643 return ColumnType::array_with_control(element, control)
644 .map_err(CommonTypeFailure::failed);
645 }
646 _ => same_category_common_type(left, right)?,
647 }
648 };
649 control
650 .finish(scalar, control.empty_reservation())
651 .map_err(CommonTypeFailure::failed)
652}
653
654fn same_category_common_type(
656 left: &ColumnType,
657 right: &ColumnType,
658) -> Result<ColumnType, CommonTypeFailure> {
659 use super::overload_resolution::{
660 routine_type_accepts_implicit_cast as implicit, routine_type_category,
661 routine_type_is_preferred,
662 };
663 let left_name = super::canonical_column_type_name(left);
664 let right_name = super::canonical_column_type_name(right);
665 if routine_type_category(&left_name) != routine_type_category(&right_name) {
666 return Err(CommonTypeFailure::Unmatched(Box::new((
667 left.clone(),
668 right.clone(),
669 ))));
670 }
671 let switch = !routine_type_is_preferred(&left_name)
672 && implicit(&left_name, &right_name)
673 && !implicit(&right_name, &left_name);
674 let (selected, other, selected_name, other_name) = if switch {
675 (right, left, &right_name, &left_name)
676 } else {
677 (left, right, &left_name, &right_name)
678 };
679 if implicit(other_name, selected_name) {
680 Ok(selected.clone())
681 } else {
682 Err(CommonTypeFailure::Inconvertible(Box::new((
683 other.clone(),
684 selected.clone(),
685 ))))
686 }
687}
688
689pub(super) mod case;
690
691fn innermost_array_element(mut ty: &ColumnType) -> &ColumnType {
693 while let ColumnType::Array(element) = ty {
694 ty = element;
695 }
696 ty
697}
698
699fn is_integral_type(ty: &ColumnType) -> bool {
700 matches!(
701 base_type(ty),
702 ColumnType::SmallInteger | ColumnType::Integer | ColumnType::BigInteger
703 )
704}
705
706pub(super) fn common_numeric_type(left: &ColumnType, right: &ColumnType) -> Option<ColumnType> {
707 let rank = numeric_rank(left)?.max(numeric_rank(right)?);
708 Some(match rank {
709 0 => ColumnType::SmallInteger,
710 1 => ColumnType::Integer,
711 2 => ColumnType::BigInteger,
712 3 => numeric_type(),
713 4 => ColumnType::Real,
714 _ => ColumnType::DoublePrecision,
715 })
716}
717
718pub(super) fn numeric_rank(ty: &ColumnType) -> Option<u8> {
719 match ty {
720 ColumnType::SmallInteger => Some(0),
721 ColumnType::Integer => Some(1),
722 ColumnType::BigInteger => Some(2),
723 ColumnType::Numeric { .. } => Some(3),
724 ColumnType::Real => Some(4),
725 ColumnType::DoublePrecision => Some(5),
726 _ => None,
727 }
728}
729
730pub(super) fn same_operator_type_with_control(
732 left: &ColumnType,
733 right: &ColumnType,
734 control: &ProductionControl<'_>,
735) -> Result<bool, uqa_core::ValueRetentionError> {
736 fn element(mut ty: &ColumnType) -> &ColumnType {
737 while let ColumnType::Array(inner) = ty {
738 ty = inner;
739 }
740 ty
741 }
742 let left = base_type(left);
743 let right = base_type(right);
744 let (left, right) = match (left, right) {
745 (ColumnType::Array(left), ColumnType::Array(right)) => (element(left), element(right)),
746 _ => (left, right),
747 };
748 let left = left.without_type_modifiers_with_control(control)?;
749 let right = right.without_type_modifiers_with_control(control)?;
750 Ok(*left == *right)
751}
752
753#[cfg(test)]
754mod production_tests;