#include "duckdb/function/macro_function.hpp"
#include "duckdb/catalog/catalog_entry/scalar_function_catalog_entry.hpp"
#include "duckdb/catalog/catalog_entry/scalar_macro_catalog_entry.hpp"
#include "duckdb/common/string_util.hpp"
#include "duckdb/function/scalar_macro_function.hpp"
#include "duckdb/function/table_macro_function.hpp"
#include "duckdb/parser/expression/columnref_expression.hpp"
#include "duckdb/parser/expression/comparison_expression.hpp"
#include "duckdb/parser/expression/function_expression.hpp"
namespace duckdb {
MacroFunction::MacroFunction(MacroType type) : type(type) {
}
string FormatMacroFunction(MacroFunction &function, const string &name) {
string result;
result = name + "(";
string parameters;
for (auto ¶m : function.parameters) {
if (!parameters.empty()) {
parameters += ", ";
}
parameters += param->Cast().GetColumnName();
}
for (auto &named_param : function.default_parameters) {
if (!parameters.empty()) {
parameters += ", ";
}
parameters += named_param.first;
parameters += " := ";
parameters += named_param.second->ToString();
}
result += parameters + ")";
return result;
}
MacroBindResult MacroFunction::BindMacroFunction(const vector> &functions, const string &name,
FunctionExpression &function_expr,
vector> &positionals,
unordered_map> &defaults) {
// separate positional and default arguments
for (auto &arg : function_expr.children) {
if (!arg->GetAlias().empty()) {
// default argument
if (defaults.count(arg->GetAlias())) {
return MacroBindResult(StringUtil::Format("Duplicate default parameters %s!", arg->GetAlias()));
}
defaults[arg->GetAlias()] = std::move(arg);
} else if (!defaults.empty()) {
return MacroBindResult("Positional parameters cannot come after parameters with a default value!");
} else {
// positional argument
positionals.push_back(std::move(arg));
}
}
// check for each macro function if it matches the number of positional arguments
optional_idx result_idx;
for (idx_t function_idx = 0; function_idx < functions.size(); function_idx++) {
if (functions[function_idx]->parameters.size() == positionals.size()) {
// found a matching function
result_idx = function_idx;
break;
}
}
if (!result_idx.IsValid()) {
// no matching function found
string error;
if (functions.size() == 1) {
// we only have one function - print the old more detailed error message
auto ¯o_def = *functions[0];
auto ¶meters = macro_def.parameters;
error = StringUtil::Format("Macro function %s requires ", FormatMacroFunction(macro_def, name));
error += parameters.size() == 1 ? "a single positional argument"
: StringUtil::Format("%i positional arguments", parameters.size());
error += ", but ";
error += positionals.size() == 1 ? "a single positional argument was"
: StringUtil::Format("%i positional arguments were", positionals.size());
error += " provided.";
} else {
// we have multiple functions - list all candidates
error += StringUtil::Format("Macro \"%s\" does not support %llu parameters.\n", name, positionals.size());
error += "Candidate macros:";
for (auto &function : functions) {
error += "\n\t" + FormatMacroFunction(*function, name);
}
}
return MacroBindResult(error);
}
// found a matching index - check if the default values exist within the macro
auto macro_idx = result_idx.GetIndex();
auto ¯o_def = *functions[macro_idx];
for (auto &default_val : defaults) {
auto entry = macro_def.default_parameters.find(default_val.first);
if (entry == macro_def.default_parameters.end()) {
string error =
StringUtil::Format("Macro \"%s\" does not have a named parameter \"%s\"\n", name, default_val.first);
error += "\nMacro definition: " + FormatMacroFunction(macro_def, name);
return MacroBindResult(error);
}
}
// Add the default values for parameters that have defaults, that were not explicitly assigned to
for (auto it = macro_def.default_parameters.begin(); it != macro_def.default_parameters.end(); it++) {
auto ¶meter_name = it->first;
auto ¶meter_default = it->second;
if (!defaults.count(parameter_name)) {
// This parameter was not set yet, set it with the default value
defaults[parameter_name] = parameter_default->Copy();
}
}
return MacroBindResult(macro_idx);
}
void MacroFunction::CopyProperties(MacroFunction &other) const {
other.type = type;
for (auto ¶m : parameters) {
other.parameters.push_back(param->Copy());
}
for (auto &kv : default_parameters) {
other.default_parameters[kv.first] = kv.second->Copy();
}
}
string MacroFunction::ToSQL() const {
vector param_strings;
for (auto ¶m : parameters) {
param_strings.push_back(param->ToString());
}
for (auto &named_param : default_parameters) {
param_strings.push_back(StringUtil::Format("%s := %s", named_param.first, named_param.second->ToString()));
}
return StringUtil::Format("(%s) AS ", StringUtil::Join(param_strings, ", "));
}
} // namespace duckdb