| 1 | #include "duckdb/planner/expression/bound_function_expression.hpp" |
| 2 | #include "duckdb/common/string_util.hpp" |
| 3 | #include "duckdb/parser/expression/bound_expression.hpp" |
| 4 | #include "duckdb/function/scalar/nested_functions.hpp" |
| 5 | |
| 6 | #include <set> |
| 7 | |
| 8 | using namespace std; |
| 9 | |
| 10 | namespace duckdb { |
| 11 | |
| 12 | static void struct_pack_fun(DataChunk &args, ExpressionState &state, Vector &result) { |
| 13 | auto &func_expr = (BoundFunctionExpression &)state.expr; |
| 14 | auto &info = (VariableReturnBindData &)*func_expr.bind_info; |
| 15 | |
| 16 | // this should never happen if the binder below is sane |
| 17 | assert(args.column_count() == info.stype.child_type.size()); |
| 18 | |
| 19 | bool all_const = true; |
| 20 | for (size_t i = 0; i < args.column_count(); i++) { |
| 21 | if (args.data[i].vector_type != VectorType::CONSTANT_VECTOR) { |
| 22 | all_const = false; |
| 23 | } |
| 24 | // same holds for this |
| 25 | assert(args.data[i].type == GetInternalType(info.stype.child_type[i].second)); |
| 26 | auto new_child = make_unique<Vector>(); |
| 27 | new_child->Reference(args.data[i]); |
| 28 | StructVector::AddEntry(result, info.stype.child_type[i].first, move(new_child)); |
| 29 | } |
| 30 | result.vector_type = all_const ? VectorType::CONSTANT_VECTOR : VectorType::FLAT_VECTOR; |
| 31 | |
| 32 | result.Verify(args.size()); |
| 33 | } |
| 34 | |
| 35 | static unique_ptr<FunctionData> struct_pack_bind(BoundFunctionExpression &expr, ClientContext &context) { |
| 36 | SQLType stype(SQLTypeId::STRUCT); |
| 37 | set<string> name_collision_set; |
| 38 | |
| 39 | // collect names and deconflict, construct return type |
| 40 | assert(expr.arguments.size() == expr.children.size()); |
| 41 | |
| 42 | if (expr.arguments.size() == 0) { |
| 43 | throw Exception("Can't pack nothing into a struct" ); |
| 44 | } |
| 45 | for (idx_t i = 0; i < expr.children.size(); i++) { |
| 46 | auto &child = expr.children[i]; |
| 47 | if (child->alias.size() == 0) { |
| 48 | throw Exception("Need named argument for struct pack, e.g. STRUCT_PACK(a := b)" ); |
| 49 | } |
| 50 | if (name_collision_set.find(child->alias) != name_collision_set.end()) { |
| 51 | throw Exception("Duplicate struct entry name" ); |
| 52 | } |
| 53 | name_collision_set.insert(child->alias); |
| 54 | stype.child_type.push_back(make_pair(child->alias, expr.arguments[i])); |
| 55 | } |
| 56 | |
| 57 | // this is more for completeness reasons |
| 58 | expr.sql_return_type = stype; |
| 59 | return make_unique<VariableReturnBindData>(stype); |
| 60 | } |
| 61 | |
| 62 | void StructPackFun::RegisterFunction(BuiltinFunctions &set) { |
| 63 | // the arguments and return types are actually set in the binder function |
| 64 | ScalarFunction fun("struct_pack" , {}, SQLType::STRUCT, struct_pack_fun, false, struct_pack_bind); |
| 65 | fun.varargs = SQLType::ANY; |
| 66 | set.AddFunction(fun); |
| 67 | } |
| 68 | |
| 69 | } // namespace duckdb |
| 70 | |