| 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 | |