1 | #include "duckdb/catalog/catalog.hpp" |
2 | #include "duckdb/parser/statement/copy_statement.hpp" |
3 | #include "duckdb/planner/binder.hpp" |
4 | #include "duckdb/parser/statement/insert_statement.hpp" |
5 | #include "duckdb/planner/operator/logical_copy_from_file.hpp" |
6 | #include "duckdb/planner/operator/logical_copy_to_file.hpp" |
7 | #include "duckdb/planner/operator/logical_insert.hpp" |
8 | |
9 | using namespace duckdb; |
10 | using namespace std; |
11 | |
12 | BoundStatement Binder::BindCopyTo(CopyStatement &stmt) { |
13 | // COPY TO a file |
14 | BoundStatement result; |
15 | result.types = {SQLType::BIGINT}; |
16 | result.names = {"Count" }; |
17 | |
18 | // bind the select statement |
19 | auto select_node = Bind(*stmt.select_statement); |
20 | |
21 | auto &names = select_node.names; |
22 | auto "e_list = stmt.info->force_quote_list; |
23 | |
24 | // set all columns to false |
25 | for (idx_t i = 0; i < names.size(); i++) { |
26 | stmt.info->force_quote.push_back(stmt.info->quote_all); |
27 | } |
28 | |
29 | if (!quote_list.empty()) { |
30 | // validate force_quote_list entries |
31 | for (const auto &column : quote_list) { |
32 | auto it = find(names.begin(), names.end(), column); |
33 | if (it != names.end()) { |
34 | stmt.info->force_quote[distance(names.begin(), it)] = true; |
35 | } else { |
36 | throw BinderException("Column %s in FORCE_QUOTE is not used in COPY" , column.c_str()); |
37 | } |
38 | } |
39 | } |
40 | // now create the copy information |
41 | auto copy = make_unique<LogicalCopyToFile>(move(stmt.info)); |
42 | copy->AddChild(move(select_node.plan)); |
43 | copy->names = select_node.names; |
44 | copy->sql_types = select_node.types; |
45 | result.plan = move(copy); |
46 | |
47 | return result; |
48 | } |
49 | |
50 | BoundStatement Binder::BindCopyFrom(CopyStatement &stmt) { |
51 | BoundStatement result; |
52 | result.types = {SQLType::BIGINT}; |
53 | result.names = {"Count" }; |
54 | |
55 | assert(!stmt.info->table.empty()); |
56 | // COPY FROM a file |
57 | // generate an insert statement for the the to-be-inserted table |
58 | InsertStatement insert; |
59 | insert.table = stmt.info->table; |
60 | insert.schema = stmt.info->schema; |
61 | insert.columns = stmt.info->select_list; |
62 | |
63 | // bind the insert statement to the base table |
64 | auto insert_statement = Bind(insert); |
65 | assert(insert_statement.plan->type == LogicalOperatorType::INSERT); |
66 | |
67 | auto &bound_insert = (LogicalInsert &)*insert_statement.plan; |
68 | |
69 | auto table = Catalog::GetCatalog(context).GetEntry<TableCatalogEntry>(context, stmt.info->schema, stmt.info->table); |
70 | // set all columns to false |
71 | idx_t column_count = stmt.info->select_list.empty() ? table->columns.size() : stmt.info->select_list.size(); |
72 | stmt.info->force_not_null.resize(column_count, false); |
73 | |
74 | // transform column names of force_not_null_list into force_not_null booleans |
75 | if (!stmt.info->force_not_null_list.empty()) { |
76 | // validate force_not_null_list entries |
77 | for (const auto &column : stmt.info->force_not_null_list) { |
78 | auto entry = table->name_map.find(column); |
79 | if (entry == table->name_map.end()) { |
80 | throw BinderException("Column %s not found in table %s" , column.c_str(), table->name.c_str()); |
81 | } |
82 | if (bound_insert.column_index_map.size() > 0) { |
83 | auto it = |
84 | find(bound_insert.column_index_map.begin(), bound_insert.column_index_map.end(), entry->second); |
85 | if (it != bound_insert.column_index_map.end()) { |
86 | stmt.info->force_not_null[entry->second] = true; |
87 | } else { |
88 | throw BinderException("Column %s in FORCE_NOT_NULL is not used in COPY" , column.c_str()); |
89 | } |
90 | } else { |
91 | stmt.info->force_not_null[entry->second] = true; |
92 | } |
93 | } |
94 | } |
95 | // now create the copy statement and set it as a child of the insert statement |
96 | auto copy = make_unique<LogicalCopyFromFile>(0, move(stmt.info), bound_insert.expected_types); |
97 | insert_statement.plan->children.push_back(move(copy)); |
98 | result.plan = move(insert_statement.plan); |
99 | return result; |
100 | } |
101 | |
102 | BoundStatement Binder::Bind(CopyStatement &stmt) { |
103 | if (stmt.select_statement) { |
104 | return BindCopyTo(stmt); |
105 | } else { |
106 | return BindCopyFrom(stmt); |
107 | } |
108 | } |
109 | |