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
14namespace duckdb {
15
16enum 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
28const 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.
32class WindowExpression : public ParsedExpression {
33public:
34 static constexpr const ExpressionClass TYPE = ExpressionClass::WINDOW;
35
36public:
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
65public:
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
84public:
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