1 | //===----------------------------------------------------------------------===// |
2 | // DuckDB |
3 | // |
4 | // duckdb/parser/expression/window_expression.hpp |
5 | // |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #pragma once |
10 | |
11 | #include "duckdb/parser/parsed_expression.hpp" |
12 | #include "duckdb/parser/query_node.hpp" |
13 | |
14 | namespace duckdb { |
15 | |
16 | enum class WindowBoundary : uint8_t { |
17 | INVALID = 0, |
18 | UNBOUNDED_PRECEDING = 1, |
19 | UNBOUNDED_FOLLOWING = 2, |
20 | CURRENT_ROW_RANGE = 3, |
21 | CURRENT_ROW_ROWS = 4, |
22 | EXPR_PRECEDING_ROWS = 5, |
23 | EXPR_FOLLOWING_ROWS = 6, |
24 | EXPR_PRECEDING_RANGE = 7, |
25 | EXPR_FOLLOWING_RANGE = 8 |
26 | }; |
27 | |
28 | const char *ToString(WindowBoundary value); |
29 | |
30 | //! The WindowExpression represents a window function in the query. They are a special case of aggregates which is why |
31 | //! they inherit from them. |
32 | class WindowExpression : public ParsedExpression { |
33 | public: |
34 | static constexpr const ExpressionClass TYPE = ExpressionClass::WINDOW; |
35 | |
36 | public: |
37 | WindowExpression(ExpressionType type, string catalog_name, string schema_name, const string &function_name); |
38 | |
39 | //! Catalog of the aggregate function |
40 | string catalog; |
41 | //! Schema of the aggregate function |
42 | string schema; |
43 | //! Name of the aggregate function |
44 | string function_name; |
45 | //! The child expression of the main window function |
46 | vector<unique_ptr<ParsedExpression>> children; |
47 | //! The set of expressions to partition by |
48 | vector<unique_ptr<ParsedExpression>> partitions; |
49 | //! The set of ordering clauses |
50 | vector<OrderByNode> orders; |
51 | //! Expression representing a filter, only used for aggregates |
52 | unique_ptr<ParsedExpression> filter_expr; |
53 | //! True to ignore NULL values |
54 | bool ignore_nulls; |
55 | //! The window boundaries |
56 | WindowBoundary start = WindowBoundary::INVALID; |
57 | WindowBoundary end = WindowBoundary::INVALID; |
58 | |
59 | unique_ptr<ParsedExpression> start_expr; |
60 | unique_ptr<ParsedExpression> end_expr; |
61 | //! Offset and default expressions for WINDOW_LEAD and WINDOW_LAG functions |
62 | unique_ptr<ParsedExpression> offset_expr; |
63 | unique_ptr<ParsedExpression> default_expr; |
64 | |
65 | public: |
66 | bool IsWindow() const override { |
67 | return true; |
68 | } |
69 | |
70 | //! Convert the Expression to a String |
71 | string ToString() const override; |
72 | |
73 | static bool Equal(const WindowExpression &a, const WindowExpression &b); |
74 | |
75 | unique_ptr<ParsedExpression> Copy() const override; |
76 | |
77 | void Serialize(FieldWriter &writer) const override; |
78 | static unique_ptr<ParsedExpression> Deserialize(ExpressionType type, FieldReader &source); |
79 | void FormatSerialize(FormatSerializer &serializer) const override; |
80 | static unique_ptr<ParsedExpression> FormatDeserialize(ExpressionType type, FormatDeserializer &deserializer); |
81 | |
82 | static ExpressionType WindowToExpressionType(string &fun_name); |
83 | |
84 | public: |
85 | template <class T, class BASE, class ORDER_NODE> |
86 | static string ToString(const T &entry, const string &schema, const string &function_name) { |
87 | // Start with function call |
88 | string result = schema.empty() ? function_name : schema + "." + function_name; |
89 | result += "(" ; |
90 | if (entry.children.size()) { |
91 | result += StringUtil::Join(entry.children, entry.children.size(), ", " , |
92 | [](const unique_ptr<BASE> &child) { return child->ToString(); }); |
93 | } |
94 | // Lead/Lag extra arguments |
95 | if (entry.offset_expr.get()) { |
96 | result += ", " ; |
97 | result += entry.offset_expr->ToString(); |
98 | } |
99 | if (entry.default_expr.get()) { |
100 | result += ", " ; |
101 | result += entry.default_expr->ToString(); |
102 | } |
103 | // IGNORE NULLS |
104 | if (entry.ignore_nulls) { |
105 | result += " IGNORE NULLS" ; |
106 | } |
107 | // FILTER |
108 | if (entry.filter_expr) { |
109 | result += ") FILTER (WHERE " + entry.filter_expr->ToString(); |
110 | } |
111 | |
112 | // Over clause |
113 | result += ") OVER (" ; |
114 | string sep; |
115 | |
116 | // Partitions |
117 | if (!entry.partitions.empty()) { |
118 | result += "PARTITION BY " ; |
119 | result += StringUtil::Join(entry.partitions, entry.partitions.size(), ", " , |
120 | [](const unique_ptr<BASE> &partition) { return partition->ToString(); }); |
121 | sep = " " ; |
122 | } |
123 | |
124 | // Orders |
125 | if (!entry.orders.empty()) { |
126 | result += sep; |
127 | result += "ORDER BY " ; |
128 | result += StringUtil::Join(entry.orders, entry.orders.size(), ", " , |
129 | [](const ORDER_NODE &order) { return order.ToString(); }); |
130 | sep = " " ; |
131 | } |
132 | |
133 | // Rows/Range |
134 | string units = "ROWS" ; |
135 | string from; |
136 | switch (entry.start) { |
137 | case WindowBoundary::CURRENT_ROW_RANGE: |
138 | case WindowBoundary::CURRENT_ROW_ROWS: |
139 | from = "CURRENT ROW" ; |
140 | units = (entry.start == WindowBoundary::CURRENT_ROW_RANGE) ? "RANGE" : "ROWS" ; |
141 | break; |
142 | case WindowBoundary::UNBOUNDED_PRECEDING: |
143 | if (entry.end != WindowBoundary::CURRENT_ROW_RANGE) { |
144 | from = "UNBOUNDED PRECEDING" ; |
145 | } |
146 | break; |
147 | case WindowBoundary::EXPR_PRECEDING_ROWS: |
148 | case WindowBoundary::EXPR_PRECEDING_RANGE: |
149 | from = entry.start_expr->ToString() + " PRECEDING" ; |
150 | units = (entry.start == WindowBoundary::EXPR_PRECEDING_RANGE) ? "RANGE" : "ROWS" ; |
151 | break; |
152 | case WindowBoundary::EXPR_FOLLOWING_ROWS: |
153 | case WindowBoundary::EXPR_FOLLOWING_RANGE: |
154 | from = entry.start_expr->ToString() + " FOLLOWING" ; |
155 | units = (entry.start == WindowBoundary::EXPR_FOLLOWING_RANGE) ? "RANGE" : "ROWS" ; |
156 | break; |
157 | default: |
158 | throw InternalException("Unrecognized FROM in WindowExpression" ); |
159 | } |
160 | |
161 | string to; |
162 | switch (entry.end) { |
163 | case WindowBoundary::CURRENT_ROW_RANGE: |
164 | if (entry.start != WindowBoundary::UNBOUNDED_PRECEDING) { |
165 | to = "CURRENT ROW" ; |
166 | units = "RANGE" ; |
167 | } |
168 | break; |
169 | case WindowBoundary::CURRENT_ROW_ROWS: |
170 | to = "CURRENT ROW" ; |
171 | units = "ROWS" ; |
172 | break; |
173 | case WindowBoundary::UNBOUNDED_PRECEDING: |
174 | to = "UNBOUNDED PRECEDING" ; |
175 | break; |
176 | case WindowBoundary::UNBOUNDED_FOLLOWING: |
177 | to = "UNBOUNDED FOLLOWING" ; |
178 | break; |
179 | case WindowBoundary::EXPR_PRECEDING_ROWS: |
180 | case WindowBoundary::EXPR_PRECEDING_RANGE: |
181 | to = entry.end_expr->ToString() + " PRECEDING" ; |
182 | units = (entry.end == WindowBoundary::EXPR_PRECEDING_RANGE) ? "RANGE" : "ROWS" ; |
183 | break; |
184 | case WindowBoundary::EXPR_FOLLOWING_ROWS: |
185 | case WindowBoundary::EXPR_FOLLOWING_RANGE: |
186 | to = entry.end_expr->ToString() + " FOLLOWING" ; |
187 | units = (entry.end == WindowBoundary::EXPR_FOLLOWING_RANGE) ? "RANGE" : "ROWS" ; |
188 | break; |
189 | default: |
190 | throw InternalException("Unrecognized TO in WindowExpression" ); |
191 | } |
192 | |
193 | if (!from.empty() || !to.empty()) { |
194 | result += sep + units; |
195 | } |
196 | if (!from.empty() && !to.empty()) { |
197 | result += " BETWEEN " ; |
198 | result += from; |
199 | result += " AND " ; |
200 | result += to; |
201 | } else if (!from.empty()) { |
202 | result += " " ; |
203 | result += from; |
204 | } else if (!to.empty()) { |
205 | result += " " ; |
206 | result += to; |
207 | } |
208 | |
209 | result += ")" ; |
210 | |
211 | return result; |
212 | } |
213 | }; |
214 | |
215 | } // namespace duckdb |
216 | |