| 1 | #include "duckdb/common/string_util.hpp" |
|---|---|
| 2 | #include "duckdb/common/types/value.hpp" |
| 3 | #include "duckdb/parser/expression/constant_expression.hpp" |
| 4 | #include "duckdb/parser/statement/copy_statement.hpp" |
| 5 | #include "duckdb/parser/tableref/basetableref.hpp" |
| 6 | #include "duckdb/parser/transformer.hpp" |
| 7 | |
| 8 | #include <cstring> |
| 9 | |
| 10 | namespace duckdb { |
| 11 | |
| 12 | void Transformer::TransformCopyOptions(CopyInfo &info, optional_ptr<duckdb_libpgquery::PGList> options) { |
| 13 | if (!options) { |
| 14 | return; |
| 15 | } |
| 16 | |
| 17 | // iterate over each option |
| 18 | duckdb_libpgquery::PGListCell *cell; |
| 19 | for_each_cell(cell, options->head) { |
| 20 | auto def_elem = PGPointerCast<duckdb_libpgquery::PGDefElem>(ptr: cell->data.ptr_value); |
| 21 | if (StringUtil::Lower(str: def_elem->defname) == "format") { |
| 22 | // format specifier: interpret this option |
| 23 | auto format_val = PGPointerCast<duckdb_libpgquery::PGValue>(ptr: def_elem->arg); |
| 24 | if (!format_val || format_val->type != duckdb_libpgquery::T_PGString) { |
| 25 | throw ParserException("Unsupported parameter type for FORMAT: expected e.g. FORMAT 'csv', 'parquet'"); |
| 26 | } |
| 27 | info.format = StringUtil::Lower(str: format_val->val.str); |
| 28 | continue; |
| 29 | } |
| 30 | // otherwise |
| 31 | if (info.options.find(x: def_elem->defname) != info.options.end()) { |
| 32 | throw ParserException("Unexpected duplicate option \"%s\"", def_elem->defname); |
| 33 | } |
| 34 | if (!def_elem->arg) { |
| 35 | info.options[def_elem->defname] = vector<Value>(); |
| 36 | continue; |
| 37 | } |
| 38 | switch (def_elem->arg->type) { |
| 39 | case duckdb_libpgquery::T_PGList: { |
| 40 | auto column_list = PGPointerCast<duckdb_libpgquery::PGList>(ptr: def_elem->arg); |
| 41 | for (auto c = column_list->head; c != nullptr; c = lnext(c)) { |
| 42 | auto target = PGPointerCast<duckdb_libpgquery::PGResTarget>(ptr: c->data.ptr_value); |
| 43 | info.options[def_elem->defname].push_back(x: Value(target->name)); |
| 44 | } |
| 45 | break; |
| 46 | } |
| 47 | case duckdb_libpgquery::T_PGAStar: |
| 48 | info.options[def_elem->defname].push_back(x: Value("*")); |
| 49 | break; |
| 50 | default: { |
| 51 | auto val = PGPointerCast<duckdb_libpgquery::PGValue>(ptr: def_elem->arg); |
| 52 | info.options[def_elem->defname].push_back(x: TransformValue(val: *val)->value); |
| 53 | break; |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | unique_ptr<CopyStatement> Transformer::TransformCopy(duckdb_libpgquery::PGCopyStmt &stmt) { |
| 60 | auto result = make_uniq<CopyStatement>(); |
| 61 | auto &info = *result->info; |
| 62 | |
| 63 | // get file_path and is_from |
| 64 | info.is_from = stmt.is_from; |
| 65 | if (!stmt.filename) { |
| 66 | // stdin/stdout |
| 67 | info.file_path = info.is_from ? "/dev/stdin": "/dev/stdout"; |
| 68 | } else { |
| 69 | // copy to a file |
| 70 | info.file_path = stmt.filename; |
| 71 | } |
| 72 | if (StringUtil::EndsWith(str: info.file_path, suffix: ".parquet")) { |
| 73 | info.format = "parquet"; |
| 74 | } else if (StringUtil::EndsWith(str: info.file_path, suffix: ".json") || StringUtil::EndsWith(str: info.file_path, suffix: ".ndjson")) { |
| 75 | info.format = "json"; |
| 76 | } else { |
| 77 | info.format = "csv"; |
| 78 | } |
| 79 | |
| 80 | // get select_list |
| 81 | if (stmt.attlist) { |
| 82 | for (auto n = stmt.attlist->head; n != nullptr; n = n->next) { |
| 83 | auto target = PGPointerCast<duckdb_libpgquery::PGResTarget>(ptr: n->data.ptr_value); |
| 84 | if (target->name) { |
| 85 | info.select_list.emplace_back(args&: target->name); |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | if (stmt.relation) { |
| 91 | auto ref = TransformRangeVar(root&: *stmt.relation); |
| 92 | auto &table = ref->Cast<BaseTableRef>(); |
| 93 | info.table = table.table_name; |
| 94 | info.schema = table.schema_name; |
| 95 | info.catalog = table.catalog_name; |
| 96 | } else { |
| 97 | result->select_statement = TransformSelectNode(select&: *PGPointerCast<duckdb_libpgquery::PGSelectStmt>(ptr: stmt.query)); |
| 98 | } |
| 99 | |
| 100 | // handle the different options of the COPY statement |
| 101 | TransformCopyOptions(info, options: stmt.options); |
| 102 | |
| 103 | return result; |
| 104 | } |
| 105 | |
| 106 | } // namespace duckdb |
| 107 |