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
9using namespace duckdb;
10using namespace std;
11
12BoundStatement 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 &quote_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
50BoundStatement 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
102BoundStatement Binder::Bind(CopyStatement &stmt) {
103 if (stmt.select_statement) {
104 return BindCopyTo(stmt);
105 } else {
106 return BindCopyFrom(stmt);
107 }
108}
109