1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
#pragma once
#include <memory>
#include <AggregateFunctions/IAggregateFunction.h>
#include <Analyzer/ConstantValue.h>
#include <Analyzer/IQueryTreeNode.h>
#include <Analyzer/ListNode.h>
#include <Common/typeid_cast.h>
#include <Core/ColumnsWithTypeAndName.h>
#include <Core/IResolvedFunction.h>
#include <DataTypes/DataTypeNullable.h>
#include <Functions/IFunction.h>
namespace DB
{
namespace ErrorCodes
{
extern const int UNSUPPORTED_METHOD;
extern const int LOGICAL_ERROR;
}
class IFunctionOverloadResolver;
using FunctionOverloadResolverPtr = std::shared_ptr<IFunctionOverloadResolver>;
/** Function node represents function in query tree.
* Function syntax: function_name(parameter_1, ...)(argument_1, ...).
* If function does not have parameters its syntax is function_name(argument_1, ...).
* If function does not have arguments its syntax is function_name().
*
* In query tree function parameters and arguments are represented by ListNode.
*
* Function can be:
* 1. Aggregate function. Example: quantile(0.5)(x), sum(x).
* 2. Non aggregate function. Example: plus(x, x).
* 3. Window function. Example: sum(x) OVER (PARTITION BY expr ORDER BY expr).
*
* Initially function node is initialized with function name.
* For window function client must initialize function window node.
*
* During query analysis pass function must be resolved using `resolveAsFunction`, `resolveAsAggregateFunction`, `resolveAsWindowFunction` methods.
* Resolved function is function that has result type and is initialized with concrete aggregate or non aggregate function.
*/
class FunctionNode;
using FunctionNodePtr = std::shared_ptr<FunctionNode>;
enum class FunctionKind
{
UNKNOWN,
ORDINARY,
AGGREGATE,
WINDOW,
};
class FunctionNode final : public IQueryTreeNode
{
public:
/** Initialize function node with function name.
* Later during query analysis pass function must be resolved.
*/
explicit FunctionNode(String function_name_);
/// Get function name
const String & getFunctionName() const { return function_name; }
/// Get parameters
const ListNode & getParameters() const { return children[parameters_child_index]->as<const ListNode &>(); }
/// Get parameters
ListNode & getParameters() { return children[parameters_child_index]->as<ListNode &>(); }
/// Get parameters node
const QueryTreeNodePtr & getParametersNode() const { return children[parameters_child_index]; }
/// Get parameters node
QueryTreeNodePtr & getParametersNode() { return children[parameters_child_index]; }
/// Get arguments
const ListNode & getArguments() const { return children[arguments_child_index]->as<const ListNode &>(); }
/// Get arguments
ListNode & getArguments() { return children[arguments_child_index]->as<ListNode &>(); }
/// Get arguments node
const QueryTreeNodePtr & getArgumentsNode() const { return children[arguments_child_index]; }
/// Get arguments node
QueryTreeNodePtr & getArgumentsNode() { return children[arguments_child_index]; }
/// Get argument types
const DataTypes & getArgumentTypes() const;
/// Get argument columns
ColumnsWithTypeAndName getArgumentColumns() const;
/// Returns true if function node has window, false otherwise
bool hasWindow() const { return children[window_child_index] != nullptr; }
/** Get window node.
* Valid only for window function node.
* Result window node can be identifier node or window node.
* 1. It can be identifier node if window function is defined as expr OVER window_name.
* 2. It can be window node if window function is defined as expr OVER (window_name ...).
*/
const QueryTreeNodePtr & getWindowNode() const { return children[window_child_index]; }
/** Get window node.
* Valid only for window function node.
*/
QueryTreeNodePtr & getWindowNode() { return children[window_child_index]; }
/** Get ordinary function.
* If function is not resolved or is resolved as non ordinary function nullptr is returned.
*/
FunctionBasePtr getFunction() const
{
if (kind != FunctionKind::ORDINARY)
return {};
return std::static_pointer_cast<const IFunctionBase>(function);
}
/** Get ordinary function.
* If function is not resolved or is resolved as non ordinary function exception is thrown.
*/
FunctionBasePtr getFunctionOrThrow() const
{
if (kind != FunctionKind::ORDINARY)
throw Exception(ErrorCodes::LOGICAL_ERROR,
"Function node with name '{}' is not resolved as ordinary function",
function_name);
return std::static_pointer_cast<const IFunctionBase>(function);
}
/** Get aggregate function.
* If function is not resolved nullptr returned.
* If function is resolved as non aggregate function nullptr returned.
*/
AggregateFunctionPtr getAggregateFunction() const
{
if (kind == FunctionKind::UNKNOWN || kind == FunctionKind::ORDINARY)
return {};
return std::static_pointer_cast<const IAggregateFunction>(function);
}
/// Is function node resolved
bool isResolved() const { return function != nullptr; }
/// Is function node window function
bool isWindowFunction() const { return hasWindow(); }
/// Is function node aggregate function
bool isAggregateFunction() const { return kind == FunctionKind::AGGREGATE; }
/// Is function node ordinary function
bool isOrdinaryFunction() const { return kind == FunctionKind::ORDINARY; }
/** Resolve function node as non aggregate function.
* It is important that function name is updated with resolved function name.
* Main motivation for this is query tree optimizations.
* Assume we have `multiIf` function with single condition, it can be converted to `if` function.
* Function name must be updated accordingly.
*/
void resolveAsFunction(FunctionBasePtr function_value);
void resolveAsFunction(const FunctionOverloadResolverPtr & resolver)
{
resolveAsFunction(resolver->build(getArgumentColumns()));
}
/** Resolve function node as aggregate function.
* It is important that function name is updated with resolved function name.
* Main motivation for this is query tree optimizations.
*/
void resolveAsAggregateFunction(AggregateFunctionPtr aggregate_function_value);
/** Resolve function node as window function.
* It is important that function name is updated with resolved function name.
* Main motivation for this is query tree optimizations.
*/
void resolveAsWindowFunction(AggregateFunctionPtr window_function_value);
QueryTreeNodeType getNodeType() const override { return QueryTreeNodeType::FUNCTION; }
DataTypePtr getResultType() const override
{
if (!function)
throw Exception(ErrorCodes::UNSUPPORTED_METHOD,
"Function node with name '{}' is not resolved",
function_name);
auto type = function->getResultType();
if (wrap_with_nullable)
return makeNullableSafe(type);
return type;
}
void convertToNullable() override
{
chassert(kind == FunctionKind::ORDINARY);
wrap_with_nullable = true;
}
void dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, size_t indent) const override;
protected:
bool isEqualImpl(const IQueryTreeNode & rhs) const override;
void updateTreeHashImpl(HashState & hash_state) const override;
QueryTreeNodePtr cloneImpl() const override;
ASTPtr toASTImpl(const ConvertToASTOptions & options) const override;
private:
String function_name;
FunctionKind kind = FunctionKind::UNKNOWN;
IResolvedFunctionPtr function;
bool wrap_with_nullable = false;
static constexpr size_t parameters_child_index = 0;
static constexpr size_t arguments_child_index = 1;
static constexpr size_t window_child_index = 2;
static constexpr size_t children_size = window_child_index + 1;
};
}
|