| 435 | } |
| 436 | |
| 437 | static void InferTemplateType(ClientContext &context, const LogicalType &source, const LogicalType &target, |
| 438 | case_insensitive_map_t<vector<LogicalType>> &bindings, const Expression ¤t_expr, |
| 439 | const BaseScalarFunction &function) { |
| 440 | if (target.id() == LogicalTypeId::UNKNOWN || target.id() == LogicalTypeId::SQLNULL) { |
| 441 | // If the actual type is unknown, we cannot infer anything more. |
| 442 | // Therefore, we map all remaining templates in the source to UNKNOWN or SQLNULL, if not already inferred to |
| 443 | // something else |
| 444 | |
| 445 | // This might seem a bit strange, why not just not set the binding and error out later when we try to substitute |
| 446 | // all templates? Well, this is how bindings for most nested functions already work, they simply propagate the |
| 447 | // UNKNOWN/SQLNULL. The binder will later check for UNKNOWN/SQLNULL in return types and if it finds one, insert |
| 448 | // a dummy cast to INT32 so that the function can be executed without errors (and just return NULLs). |
| 449 | |
| 450 | TypeVisitor::Contains(source, [&](const LogicalType &child) { |
| 451 | if (child.id() == LogicalTypeId::TEMPLATE) { |
| 452 | const auto index = TemplateType::GetName(child); |
| 453 | if (bindings.find(index) == bindings.end()) { |
| 454 | // not found, add the binding |
| 455 | bindings[index] = {target.id()}; |
| 456 | } |
| 457 | } |
| 458 | return false; // continue visiting |
| 459 | }); |
| 460 | return; |
| 461 | } |
| 462 | |
| 463 | // If the source is a template type, we bind it, or try to unify its existing binding with the target type. |
| 464 | if (source.id() == LogicalTypeId::TEMPLATE) { |
| 465 | const auto &index = TemplateType::GetName(source); |
| 466 | auto it = bindings.find(index); |
| 467 | if (it == bindings.end()) { |
| 468 | // not found, add the binding |
| 469 | bindings[index] = {target}; |
| 470 | return; |
| 471 | } |
| 472 | if (it->second.back() == target) { |
| 473 | // already bound to the same type |
| 474 | return; |
| 475 | } |
| 476 | |
| 477 | // Try to unify (promote) the type candidates |
| 478 | LogicalType result; |
| 479 | if (LogicalType::TryGetMaxLogicalType(context, it->second.back(), target, result)) { |
| 480 | // Type unification was successful |
| 481 | if (it->second.back() != result) { |
| 482 | // update the binding |
| 483 | it->second.push_back(target); |
| 484 | it->second.push_back(std::move(result)); // Push the new promoted type |
| 485 | } |
| 486 | return; |
| 487 | } |
| 488 | |
| 489 | // If we reach here, it means the types are incompatible |
| 490 | string msg = |
| 491 | StringUtil::Format("Cannot deduce template type '%s' in function: '%s'\nType '%s' was inferred to be:\n", |
| 492 | TemplateType::GetName(source), function.ToString(), TemplateType::GetName(source)); |
| 493 | const auto &steps = it->second; |
| 494 |
no test coverage detected