Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,13 @@ void ApproxTopKImportState(AggregateImportInputData &input) {
throw InvalidInputException("Invalid approx_top_k state - the state values must be unique");
}
val.count = count_data[idx];
if (val.count == 0) {
throw InvalidInputException("Invalid approx_top_k state - the state values must have a count > 0");
}
if (value_idx > 0 && val.count > target.values[value_idx - 1].get().count) {
throw InvalidInputException(
"Invalid approx_top_k state - the state values must be ordered by descending count");
}
}
for (idx_t filter_idx = 0; filter_idx < filter_entries[i].length; filter_idx++) {
target.filter[filter_idx] = filter_data[filter_entries[i].offset + filter_idx];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace duckdb {

static unique_ptr<FunctionData> ArrayGenericBinaryBind(BindScalarFunctionInput &input) {
static void ArrayGenericBinaryResolveTypes(ResolveScalarFunctionTypesInput &input) {
auto &context = input.GetClientContext();
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
Expand All @@ -17,7 +17,7 @@ static unique_ptr<FunctionData> ArrayGenericBinaryBind(BindScalarFunctionInput &
bound_function.GetArguments()[0] = rhs_type;
bound_function.GetArguments()[1] = lhs_type;
bound_function.SetReturnType(LogicalType::UNKNOWN);
return nullptr;
return;
}

bound_function.GetArguments()[0] = lhs_type.IsUnknown() ? rhs_type : lhs_type;
Expand Down Expand Up @@ -56,8 +56,6 @@ static unique_ptr<FunctionData> ArrayGenericBinaryBind(BindScalarFunctionInput &
// The important part is just that we resolve the size of the input arrays
bound_function.GetArguments()[0] = LogicalType::ARRAY(common_type, lhs_size);
bound_function.GetArguments()[1] = LogicalType::ARRAY(common_type, rhs_size);

return nullptr;
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -255,7 +253,8 @@ static scalar_function_t GetArrayFoldFunction(const LogicalType &type) {

template <class OP>
static void AddArrayFoldFunction(ScalarFunctionSet &set, const LogicalType &type) {
ScalarFunction func({}, type, GetArrayFoldFunction<OP>(type), ArrayGenericBinaryBind, ArrayGenericFoldStats);
ScalarFunction func({}, type, GetArrayFoldFunction<OP>(type), nullptr, ArrayGenericFoldStats);
func.SetResolveTypesCallback(ArrayGenericBinaryResolveTypes);
auto array = LogicalType::ARRAY(type, optional_idx());

func.SetFallible();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void ArrayValueFunction(DataChunk &args, ExpressionState &state, Vector &result)
result.Verify();
}

unique_ptr<FunctionData> ArrayValueBind(BindScalarFunctionInput &input) {
void ArrayValueResolveTypes(ResolveScalarFunctionTypesInput &input) {
auto &context = input.GetClientContext();
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
Expand All @@ -72,7 +72,6 @@ unique_ptr<FunctionData> ArrayValueBind(BindScalarFunctionInput &input) {
}

bound_function.SetReturnType(LogicalType::ARRAY(child_type, arguments.size()));
return make_uniq<VariableReturnBindData>(bound_function.GetReturnType());
}

unique_ptr<BaseStatistics> ArrayValueStats(ClientContext &context, FunctionStatisticsInput &input) {
Expand All @@ -91,7 +90,9 @@ unique_ptr<BaseStatistics> ArrayValueStats(ClientContext &context, FunctionStati

ScalarFunction ArrayValueFun::GetFunction() {
// the arguments and return types are actually set in the binder function
ScalarFunction fun("array_value", {}, LogicalTypeId::ARRAY, ArrayValueFunction, ArrayValueBind, ArrayValueStats);
ScalarFunction fun("array_value", {}, LogicalTypeId::ARRAY, ArrayValueFunction, VariableReturnBindData::Bind,
ArrayValueStats);
fun.SetResolveTypesCallback(ArrayValueResolveTypes);
fun.GetSignature().AddArgs("args", LogicalType::ANY);
fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING);
return fun;
Expand Down
48 changes: 32 additions & 16 deletions src/duckdb/extension/core_functions/scalar/debug/index_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,31 +115,42 @@ struct IndexKeyBindData : public FunctionData {
vector<LogicalType> key_types;
};

static unique_ptr<FunctionData> IndexKeyBind(BindScalarFunctionInput &input) {
auto &context = input.GetClientContext();
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
// index_key resolves index metadata during binding.
// Register the owning catalog so cached prepared statements rebind after catalog changes.
static void RegisterIndexKeyRead(BindScalarFunctionInput &input, TableCatalogEntry &table_entry) {
if (input.HasBinder()) {
auto &binder = input.GetBinder();
binder.GetStatementProperties().RegisterDBRead(table_entry.ParentCatalog(), input.GetClientContext());
}
}

static void RegisterIndexKeyRead(ResolveScalarFunctionTypesInput &input, TableCatalogEntry &table_entry) {
}

//! Called from both resolve_types and bind - the key types and the bind data both come from the index
template <class INPUT>
static unique_ptr<FunctionData> IndexKeyBind(INPUT &input) {
ClientContext &context = input.GetClientContext();
BoundScalarFunction &bound_function = input.GetBoundFunction();
const vector<unique_ptr<Expression>> &arguments = input.GetArguments();
if (arguments.size() < INDEX_KEY_FIXED_ARGS) {
throw BinderException("index_key: requires at least two arguments - path (STRUCT), index_name");
}

auto path = EvaluateTableDescription(input.GetConstant(0));
auto index_name = GetStringArgument(input.GetConstant(1), "index_name");
const Value path_value = input.GetConstant(0);
const Value index_name_value = input.GetConstant(1);
auto path = EvaluateTableDescription(path_value);
auto index_name = GetStringArgument(index_name_value, "index_name");

auto qualified_table = path.qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA);
auto &table_entry = Catalog::GetEntry<TableCatalogEntry>(context, path.qualified_name);
if (!table_entry.IsDuckTable()) {
auto duck_table = table_entry.TryGetDuckTableEntry();
if (!duck_table) {
throw BinderException("index_key: table '%s' is not a DuckDB table", qualified_table);
}

// index_key resolves index metadata during binding.
// Register the owning catalog so cached prepared statements rebind after catalog changes.
if (input.HasBinder()) {
auto &binder = input.GetBinder();
binder.GetStatementProperties().RegisterDBRead(table_entry.ParentCatalog(), context);
}
auto &duck_table = table_entry.Cast<DuckTableEntry>();
auto &data_table = duck_table.GetStorage();
RegisterIndexKeyRead(input, table_entry);
auto &data_table = duck_table->GetStorage();
auto &data_table_info = *data_table.GetDataTableInfo();

// Note: It may come up in testing that we don't want to force binding here, e.g. if the test should explicitly
Expand Down Expand Up @@ -180,6 +191,10 @@ static unique_ptr<FunctionData> IndexKeyBind(BindScalarFunctionInput &input) {
return make_uniq<IndexKeyBindData>(index_entry, std::move(key_types));
}

static void IndexKeyResolveTypes(ResolveScalarFunctionTypesInput &input) {
IndexKeyBind(input);
}

static void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &result) {
auto &func_expr = state.expr.Cast<BoundFunctionExpression>();
auto &bind_data = func_expr.BindInfo()->Cast<IndexKeyBindData>();
Expand Down Expand Up @@ -216,9 +231,10 @@ static void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &re
} // namespace

ScalarFunction IndexKeyFun::GetFunction() {
ScalarFunction fun("index_key", {}, LogicalType::BLOB, IndexKeyFunction, IndexKeyBind);
ScalarFunction fun("index_key", {}, LogicalType::BLOB, IndexKeyFunction, IndexKeyBind<BindScalarFunctionInput>);
fun.GetSignature().AddParameter("path", LogicalTypeId::STRUCT).AddParameter("name", LogicalType::VARCHAR);
fun.GetSignature().AddArgs("args", LogicalTypeId::ANY);
fun.SetResolveTypesCallback(IndexKeyResolveTypes);
return fun;
}

Expand Down
14 changes: 7 additions & 7 deletions src/duckdb/extension/core_functions/scalar/generic/binning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,26 +414,25 @@ struct EquiWidthBinsTimestamp {
}
};

unique_ptr<FunctionData> BindEquiWidthFunction(BindScalarFunctionInput &input) {
void EquiWidthResolveTypes(ResolveScalarFunctionTypesInput &input) {
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
auto &max_type = input.GetArgumentType(1);
// while internally the bins are computed over a unified type
// the equi_width_bins function returns the same type as the input MAX
LogicalType child_type;
switch (arguments[1]->GetReturnType().id()) {
switch (max_type.id()) {
case LogicalTypeId::UNKNOWN:
case LogicalTypeId::SQLNULL:
return nullptr;
return;
case LogicalTypeId::DECIMAL:
// for decimals we promote to double because
child_type = LogicalType::DOUBLE;
break;
default:
child_type = arguments[1]->GetReturnType();
child_type = max_type;
break;
}
bound_function.SetReturnType(LogicalType::LIST(child_type));
return nullptr;
}

template <class T, class OP>
Expand Down Expand Up @@ -496,7 +495,8 @@ unique_ptr<FunctionData> EquiWidthBinDeserialize(Deserializer &deserializer, Bou

static void AddEquiWidthBinFunction(ScalarFunctionSet &functions, const LogicalType &min_max_type,
scalar_function_t function) {
ScalarFunction fun({}, LogicalType::LIST(LogicalType::ANY), std::move(function), BindEquiWidthFunction);
ScalarFunction fun({}, LogicalType::LIST(LogicalType::ANY), std::move(function));
fun.SetResolveTypesCallback(EquiWidthResolveTypes);
fun.GetSignature()
.AddParameter("min", min_max_type)
.AddParameter("max", min_max_type)
Expand Down
15 changes: 7 additions & 8 deletions src/duckdb/extension/core_functions/scalar/generic/least.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,14 @@ unique_ptr<BaseStatistics> PropagateLeastGreatestStats(ClientContext &context, F
}

template <class LEAST_GREATER_OP>
unique_ptr<FunctionData> BindLeastGreatest(BindScalarFunctionInput &input) {
void LeastGreatestResolveTypes(ResolveScalarFunctionTypesInput &input) {
auto &context = input.GetClientContext();
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
LogicalType child_type = ExpressionBinder::GetExpressionReturnType(*arguments[0]);
for (idx_t i = 1; i < arguments.size(); i++) {
auto arg_type = ExpressionBinder::GetExpressionReturnType(*arguments[i]);
LogicalType child_type = ExpressionBinder::GetExpressionReturnType(input.GetArgument(0));
for (idx_t i = 1; i < input.GetArgumentCount(); i++) {
auto arg_type = ExpressionBinder::GetExpressionReturnType(input.GetArgument(i));
if (!LogicalType::TryGetMaxLogicalType(context, child_type, arg_type, child_type)) {
throw BinderException(arguments[i]->GetQueryLocation(),
throw BinderException(input.GetArgument(i).GetQueryLocation(),
"Cannot combine types of %s and %s - an explicit cast is required",
child_type.ToString(), arg_type.ToString());
}
Expand Down Expand Up @@ -300,14 +299,14 @@ unique_ptr<FunctionData> BindLeastGreatest(BindScalarFunctionInput &input) {
arg = child_type;
}
bound_function.SetReturnType(child_type);
return nullptr;
}

template <class OP>
ScalarFunction GetLeastGreatestFunction() {
ScalarFunction fun({{"arg1", LogicalType::ANY}}, LogicalType::ANY, nullptr, BindLeastGreatest<OP>,
ScalarFunction fun({{"arg1", LogicalType::ANY}}, LogicalType::ANY, nullptr, nullptr,
PropagateLeastGreatestStats<OP>, nullptr, LogicalType::ANY, FunctionStability::CONSISTENT,
FunctionNullHandling::SPECIAL_HANDLING);
fun.SetResolveTypesCallback(LeastGreatestResolveTypes<OP>);
return fun;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ struct CurrentSchemasBindData : public FunctionData {
}
};

unique_ptr<FunctionData> CurrentSchemasBind(BindScalarFunctionInput &input) {
auto &context = input.GetClientContext();
auto &arguments = input.GetArguments();
if (arguments[0]->GetReturnType().id() != LogicalTypeId::BOOLEAN) {
void CurrentSchemasResolveTypes(ResolveScalarFunctionTypesInput &input) {
if (input.GetArgumentType(0).id() != LogicalTypeId::BOOLEAN) {
throw BinderException("current_schemas requires a boolean input");
}
}

unique_ptr<FunctionData> CurrentSchemasBind(BindScalarFunctionInput &input) {
auto &context = input.GetClientContext();
Value schema_value = input.GetConstant(0);
Value result_val;
if (schema_value.IsNull()) {
Expand Down Expand Up @@ -128,6 +130,7 @@ ScalarFunction CurrentSchemasFun::GetFunction() {
auto varchar_list_type = LogicalType::LIST(LogicalType::VARCHAR);
ScalarFunction current_schemas({}, varchar_list_type, CurrentSchemasFunction, CurrentSchemasBind);
current_schemas.GetSignature().AddParameter("include_implicit", LogicalType::BOOLEAN);
current_schemas.SetResolveTypesCallback(CurrentSchemasResolveTypes);
current_schemas.SetStability(FunctionStability::CONSISTENT_WITHIN_QUERY);
return current_schemas;
}
Expand Down
55 changes: 29 additions & 26 deletions src/duckdb/extension/core_functions/scalar/list/array_slice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,18 +300,18 @@ void ArraySliceFunction(DataChunk &args, ExpressionState &state, Vector &result)
}

//! An omitted slice bound is parsed as an empty list constructor (see OperatorExpression::EmptySliceBound)
bool CheckIfParamIsEmpty(duckdb::unique_ptr<duckdb::Expression> &param) {
if (param->GetReturnType().id() != LogicalTypeId::LIST) {
bool CheckIfParamIsEmpty(const Expression &param) {
if (param.GetReturnType().id() != LogicalTypeId::LIST) {
return false;
}
if (param->GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) {
auto &function = param->Cast<BoundFunctionExpression>();
if (param.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) {
auto &function = param.Cast<BoundFunctionExpression>();
if (function.Function().GetName() == "list_value" && function.GetChildren().empty()) {
return true;
}
}
if (param->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) {
auto &value = param->Cast<BoundConstantExpression>().GetValue();
if (param.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) {
auto &value = param.Cast<BoundConstantExpression>().GetValue();
if (!value.IsNull() && ListValue::GetChildren(value).empty()) {
return true;
}
Expand All @@ -320,24 +320,22 @@ bool CheckIfParamIsEmpty(duckdb::unique_ptr<duckdb::Expression> &param) {
throw BinderException("The upper and lower bounds of the slice must be a BIGINT");
}

unique_ptr<FunctionData> ArraySliceBind(BindScalarFunctionInput &input) {
auto &context = input.GetClientContext();
void ArraySliceResolveTypes(ResolveScalarFunctionTypesInput &input) {
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
D_ASSERT(arguments.size() == 3 || arguments.size() == 4);
D_ASSERT(input.GetArgumentCount() == 3 || input.GetArgumentCount() == 4);
D_ASSERT(bound_function.GetArguments().size() == 3 || bound_function.GetArguments().size() == 4);

switch (arguments[0]->GetReturnType().id()) {
auto &list_type = input.GetArgumentType(0);
switch (list_type.id()) {
case LogicalTypeId::ARRAY: {
// Cast to list
auto child_type = ArrayType::GetChildType(arguments[0]->GetReturnType());
auto target_type = LogicalType::LIST(child_type);
arguments[0] = BoundCastExpression::AddCastToType(context, std::move(arguments[0]), target_type);
bound_function.SetReturnType(arguments[0]->GetReturnType());
auto target_type = LogicalType::LIST(ArrayType::GetChildType(list_type));
bound_function.GetArguments()[0] = target_type;
bound_function.SetReturnType(target_type);
} break;
case LogicalTypeId::LIST:
// The result is the same type
bound_function.SetReturnType(arguments[0]->GetReturnType());
bound_function.SetReturnType(list_type);
break;
case LogicalTypeId::BLOB:
case LogicalTypeId::VARCHAR:
Expand All @@ -347,15 +345,15 @@ unique_ptr<FunctionData> ArraySliceBind(BindScalarFunctionInput &input) {
"Slice with steps has not been implemented for string types, you can consider rewriting your query as "
"follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');");
}
if (arguments[0]->GetReturnType().IsJSONType()) {
if (list_type.IsJSONType()) {
// This is needed to avoid producing invalid JSON
bound_function.GetArguments()[0] = LogicalType::VARCHAR;
bound_function.SetReturnType(LogicalType::VARCHAR);
} else {
bound_function.SetReturnType(arguments[0]->GetReturnType());
bound_function.SetReturnType(list_type);
}
for (idx_t i = 1; i < 3; i++) {
if (arguments[i]->GetReturnType().id() != LogicalTypeId::LIST) {
if (input.GetArgumentType(i).id() != LogicalTypeId::LIST) {
bound_function.GetArguments()[i] = LogicalType::BIGINT;
}
}
Expand All @@ -369,15 +367,19 @@ unique_ptr<FunctionData> ArraySliceBind(BindScalarFunctionInput &input) {
throw BinderException("ARRAY_SLICE can only operate on LISTs and VARCHARs");
}

bool begin_is_empty = CheckIfParamIsEmpty(arguments[1]);
if (!begin_is_empty) {
bound_function.GetArguments()[1] = LogicalType::BIGINT;
}
bool end_is_empty = CheckIfParamIsEmpty(arguments[2]);
if (!end_is_empty) {
bound_function.GetArguments()[2] = LogicalType::BIGINT;
// empty bounds keep their (empty list) type - the other bounds are cast to BIGINT
for (idx_t i = 1; i < 3; i++) {
if (!CheckIfParamIsEmpty(input.GetArgument(i))) {
bound_function.GetArguments()[i] = LogicalType::BIGINT;
}
}
}

unique_ptr<FunctionData> ArraySliceBind(BindScalarFunctionInput &input) {
auto &bound_function = input.GetBoundFunction();
auto &arguments = input.GetArguments();
bool begin_is_empty = arguments[1]->GetReturnType().id() == LogicalTypeId::LIST;
bool end_is_empty = arguments[2]->GetReturnType().id() == LogicalTypeId::LIST;
return make_uniq<ListSliceBindData>(bound_function.GetReturnType(), begin_is_empty, end_is_empty);
}

Expand Down Expand Up @@ -462,6 +464,7 @@ ScalarFunctionSet ListSliceFun::GetFunctions() {
.AddParameter("list", LogicalType::ANY)
.AddParameter("begin", LogicalType::ANY)
.AddParameter("end", LogicalType::ANY);
fun.SetResolveTypesCallback(ArraySliceResolveTypes);
fun.SetStatisticsCallback(ArraySlicePropagateStats);
fun.SetUnbindCallback(ArraySliceUnbind);
fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING);
Expand Down
Loading
Loading