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
8using namespace std;
9
10namespace duckdb {
11
12static 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
35static 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
62void 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