1#include "duckdb/common/exception.hpp"
2#include "duckdb/parser/tableref/pivotref.hpp"
3#include "duckdb/parser/transformer.hpp"
4#include "duckdb/parser/expression/columnref_expression.hpp"
5#include "duckdb/parser/expression/constant_expression.hpp"
6#include "duckdb/parser/expression/function_expression.hpp"
7
8namespace duckdb {
9
10static void TransformPivotInList(unique_ptr<ParsedExpression> &expr, PivotColumnEntry &entry, bool root_entry = true) {
11 if (expr->type == ExpressionType::COLUMN_REF) {
12 auto &colref = expr->Cast<ColumnRefExpression>();
13 if (colref.IsQualified()) {
14 throw ParserException("PIVOT IN list cannot contain qualified column references");
15 }
16 entry.values.emplace_back(args: colref.GetColumnName());
17 } else if (expr->type == ExpressionType::VALUE_CONSTANT) {
18 auto &constant_expr = expr->Cast<ConstantExpression>();
19 entry.values.push_back(x: std::move(constant_expr.value));
20 } else if (root_entry && expr->type == ExpressionType::FUNCTION) {
21 auto &function = expr->Cast<FunctionExpression>();
22 if (function.function_name != "row") {
23 throw ParserException("PIVOT IN list must contain columns or lists of columns");
24 }
25 for (auto &child : function.children) {
26 TransformPivotInList(expr&: child, entry, root_entry: false);
27 }
28 } else if (root_entry && expr->type == ExpressionType::STAR) {
29 entry.star_expr = std::move(expr);
30 } else {
31 throw ParserException("PIVOT IN list must contain columns or lists of columns");
32 }
33}
34
35PivotColumn Transformer::TransformPivotColumn(duckdb_libpgquery::PGPivot &pivot) {
36 PivotColumn col;
37 if (pivot.pivot_columns) {
38 TransformExpressionList(list&: *pivot.pivot_columns, result&: col.pivot_expressions);
39 for (auto &expr : col.pivot_expressions) {
40 if (expr->IsScalar()) {
41 throw ParserException("Cannot pivot on constant value \"%s\"", expr->ToString());
42 }
43 if (expr->HasSubquery()) {
44 throw ParserException("Cannot pivot on subquery \"%s\"", expr->ToString());
45 }
46 }
47 } else if (pivot.unpivot_columns) {
48 col.unpivot_names = TransformStringList(list: pivot.unpivot_columns);
49 } else {
50 throw InternalException("Either pivot_columns or unpivot_columns must be defined");
51 }
52 if (pivot.pivot_value) {
53 for (auto node = pivot.pivot_value->head; node != nullptr; node = node->next) {
54 auto n = PGPointerCast<duckdb_libpgquery::PGNode>(ptr: node->data.ptr_value);
55 auto expr = TransformExpression(node: n);
56 PivotColumnEntry entry;
57 entry.alias = expr->alias;
58 TransformPivotInList(expr, entry);
59 col.entries.push_back(x: std::move(entry));
60 }
61 }
62 if (pivot.subquery) {
63 col.subquery = TransformSelectNode(select&: *PGPointerCast<duckdb_libpgquery::PGSelectStmt>(ptr: pivot.subquery));
64 }
65 if (pivot.pivot_enum) {
66 col.pivot_enum = pivot.pivot_enum;
67 }
68 return col;
69}
70
71vector<PivotColumn> Transformer::TransformPivotList(duckdb_libpgquery::PGList &list) {
72 vector<PivotColumn> result;
73 for (auto node = list.head; node != nullptr; node = node->next) {
74 auto pivot = PGPointerCast<duckdb_libpgquery::PGPivot>(ptr: node->data.ptr_value);
75 result.push_back(x: TransformPivotColumn(pivot&: *pivot));
76 }
77 return result;
78}
79
80unique_ptr<TableRef> Transformer::TransformPivot(duckdb_libpgquery::PGPivotExpr &root) {
81 auto result = make_uniq<PivotRef>();
82 result->source = TransformTableRefNode(n&: *root.source);
83 if (root.aggrs) {
84 TransformExpressionList(list&: *root.aggrs, result&: result->aggregates);
85 }
86 if (root.unpivots) {
87 result->unpivot_names = TransformStringList(list: root.unpivots);
88 }
89 result->pivots = TransformPivotList(list&: *root.pivots);
90 if (!result->unpivot_names.empty() && result->pivots.size() > 1) {
91 throw ParserException("UNPIVOT requires a single pivot element");
92 }
93 if (root.groups) {
94 result->groups = TransformStringList(list: root.groups);
95 }
96 for (auto &pivot : result->pivots) {
97 idx_t expected_size;
98 bool is_pivot = result->unpivot_names.empty();
99 if (!result->unpivot_names.empty()) {
100 // unpivot
101 if (pivot.unpivot_names.size() != 1) {
102 throw ParserException("UNPIVOT requires a single column name for the PIVOT IN clause");
103 }
104 D_ASSERT(pivot.pivot_expressions.empty());
105 expected_size = pivot.entries[0].values.size();
106 } else {
107 // pivot
108 expected_size = pivot.pivot_expressions.size();
109 D_ASSERT(pivot.unpivot_names.empty());
110 }
111 for (auto &entry : pivot.entries) {
112 if (entry.star_expr && is_pivot) {
113 throw ParserException("PIVOT IN list must contain columns or lists of columns - star expressions are "
114 "only supported for UNPIVOT");
115 }
116 if (entry.values.size() != expected_size) {
117 throw ParserException("PIVOT IN list - inconsistent amount of rows - expected %d but got %d",
118 expected_size, entry.values.size());
119 }
120 }
121 }
122 result->include_nulls = root.include_nulls;
123 result->alias = TransformAlias(root: root.alias, column_name_alias&: result->column_name_alias);
124 return std::move(result);
125}
126
127} // namespace duckdb
128