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
10namespace duckdb {
11
12void 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
59unique_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