mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
something works
This commit is contained in:
parent
e6c2d0219d
commit
703731c547
@ -22,6 +22,7 @@
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/ASTUseQuery.h>
|
||||
#include <Parsers/ASTWindowDefinition.h>
|
||||
#include <Parsers/ParserQuery.h>
|
||||
#include <Parsers/formatAST.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
@ -403,10 +404,11 @@ void QueryFuzzer::fuzz(ASTPtr & ast)
|
||||
fuzzColumnLikeExpressionList(fn->arguments.get());
|
||||
fuzzColumnLikeExpressionList(fn->parameters.get());
|
||||
|
||||
if (fn->is_window_function)
|
||||
if (fn->is_window_function && fn->window_definition)
|
||||
{
|
||||
fuzzColumnLikeExpressionList(fn->window_partition_by.get());
|
||||
fuzzOrderByList(fn->window_order_by.get());
|
||||
auto & def = fn->window_definition->as<ASTWindowDefinition &>();
|
||||
fuzzColumnLikeExpressionList(def.partition_by.get());
|
||||
fuzzOrderByList(def.order_by.get());
|
||||
}
|
||||
|
||||
fuzz(fn->children);
|
||||
|
@ -46,6 +46,7 @@ template <typename T> WriteBuffer & operator<< (WriteBuffer & buf, const T &
|
||||
/// If you do not use the manipulators, the string is displayed without an escape, as is.
|
||||
template <> inline WriteBuffer & operator<< (WriteBuffer & buf, const String & x) { writeString(x, buf); return buf; }
|
||||
template <> inline WriteBuffer & operator<< (WriteBuffer & buf, const std::string_view & x) { writeString(StringRef(x), buf); return buf; }
|
||||
template <> inline WriteBuffer & operator<< (WriteBuffer & buf, const StringRef & x) { writeString(x, buf); return buf; }
|
||||
template <> inline WriteBuffer & operator<< (WriteBuffer & buf, const char & x) { writeChar(x, buf); return buf; }
|
||||
template <> inline WriteBuffer & operator<< (WriteBuffer & buf, const pcg32_fast & x) { PcgSerializer::serializePcg32(x, buf); return buf; }
|
||||
|
||||
|
@ -738,13 +738,9 @@ void ActionsMatcher::visit(const ASTFunction & node, const ASTPtr & ast, Data &
|
||||
if (node.is_window_function)
|
||||
{
|
||||
// Also add columns from PARTITION BY and ORDER BY of window functions.
|
||||
if (node.window_partition_by)
|
||||
if (node.window_definition)
|
||||
{
|
||||
visit(node.window_partition_by, data);
|
||||
}
|
||||
if (node.window_order_by)
|
||||
{
|
||||
visit(node.window_order_by, data);
|
||||
visit(node.window_definition, data);
|
||||
}
|
||||
|
||||
// Also manually add columns for arguments of the window function itself.
|
||||
|
@ -1,12 +1,13 @@
|
||||
#include <Core/Block.h>
|
||||
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTOrderByElement.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
#include <Parsers/ASTOrderByElement.h>
|
||||
#include <Parsers/ASTWindowDefinition.h>
|
||||
#include <Parsers/DumpASTNode.h>
|
||||
|
||||
#include <DataTypes/DataTypeNullable.h>
|
||||
@ -289,7 +290,7 @@ void ExpressionAnalyzer::analyzeAggregation()
|
||||
aggregated_columns = temp_actions->getNamesAndTypesList();
|
||||
}
|
||||
|
||||
has_window = makeWindowDescriptions(temp_actions);
|
||||
fmt::print(stderr, "aggregated columns: {}\n", aggregated_columns.toString());
|
||||
}
|
||||
|
||||
|
||||
@ -471,8 +472,52 @@ bool ExpressionAnalyzer::makeAggregateDescriptions(ActionsDAGPtr & actions)
|
||||
return !aggregates().empty();
|
||||
}
|
||||
|
||||
void makeWindowDescription(WindowDescription & desc, const IAST * ast)
|
||||
{
|
||||
const auto & definition = ast->as<const ASTWindowDefinition &>();
|
||||
|
||||
bool ExpressionAnalyzer::makeWindowDescriptions(ActionsDAGPtr & actions)
|
||||
if (definition.partition_by)
|
||||
{
|
||||
for (const auto & column_ast : definition.partition_by->children)
|
||||
{
|
||||
const auto * with_alias = dynamic_cast<const ASTWithAlias *>(
|
||||
column_ast.get());
|
||||
if (!with_alias)
|
||||
{
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Expected a column in PARTITION BY in window definition,"
|
||||
" got '{}'",
|
||||
column_ast->formatForErrorMessage());
|
||||
}
|
||||
desc.partition_by.push_back(SortColumnDescription(
|
||||
with_alias->getColumnName(), 1 /* direction */,
|
||||
1 /* nulls_direction */));
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.order_by)
|
||||
{
|
||||
for (const auto & column_ast
|
||||
: definition.order_by->children)
|
||||
{
|
||||
// Parser should have checked that we have a proper element here.
|
||||
const auto & order_by_element
|
||||
= column_ast->as<ASTOrderByElement &>();
|
||||
// Ignore collation for now.
|
||||
desc.order_by.push_back(
|
||||
SortColumnDescription(
|
||||
order_by_element.children.front()->getColumnName(),
|
||||
order_by_element.direction,
|
||||
order_by_element.nulls_direction));
|
||||
}
|
||||
}
|
||||
|
||||
desc.full_sort_description = desc.partition_by;
|
||||
desc.full_sort_description.insert(desc.full_sort_description.end(),
|
||||
desc.order_by.begin(), desc.order_by.end());
|
||||
}
|
||||
|
||||
void ExpressionAnalyzer::makeWindowDescriptions(ActionsDAGPtr actions)
|
||||
{
|
||||
// Convenient to check here because at least we have the Context.
|
||||
if (!syntax->window_function_asts.empty() &&
|
||||
@ -483,57 +528,34 @@ bool ExpressionAnalyzer::makeWindowDescriptions(ActionsDAGPtr & actions)
|
||||
syntax->window_function_asts[0]->formatForErrorMessage());
|
||||
}
|
||||
|
||||
// Window definitions from the WINDOW clause
|
||||
const auto * select_query = query->as<ASTSelectQuery>();
|
||||
if (select_query && select_query->window())
|
||||
{
|
||||
for (const auto & ptr : select_query->window()->children)
|
||||
{
|
||||
const auto & elem = ptr->as<const ASTWindowListElement &>();
|
||||
WindowDescription desc;
|
||||
desc.window_name = elem.name;
|
||||
makeWindowDescription(desc, elem.definition.get());
|
||||
|
||||
auto [it, inserted] = window_descriptions.insert(
|
||||
{desc.window_name, desc});
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Window '{}' is defined twice in the WINDOW clause",
|
||||
desc.window_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Window functions
|
||||
for (const ASTFunction * function_node : syntax->window_function_asts)
|
||||
{
|
||||
assert(function_node->is_window_function);
|
||||
|
||||
WindowDescription window_description;
|
||||
window_description.window_name = function_node->getWindowDescription();
|
||||
|
||||
if (function_node->window_partition_by)
|
||||
{
|
||||
for (const auto & column_ast
|
||||
: function_node->window_partition_by->children)
|
||||
{
|
||||
const auto * with_alias = dynamic_cast<const ASTWithAlias *>(
|
||||
column_ast.get());
|
||||
if (!with_alias)
|
||||
{
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Expected a column in PARTITION BY for window '{}',"
|
||||
" got '{}'", window_description.window_name,
|
||||
column_ast->formatForErrorMessage());
|
||||
}
|
||||
window_description.partition_by.push_back(
|
||||
SortColumnDescription(
|
||||
with_alias->getColumnName(), 1 /* direction */,
|
||||
1 /* nulls_direction */));
|
||||
}
|
||||
}
|
||||
|
||||
if (function_node->window_order_by)
|
||||
{
|
||||
for (const auto & column_ast
|
||||
: function_node->window_order_by->children)
|
||||
{
|
||||
// Parser should have checked that we have a proper element here.
|
||||
const auto & order_by_element
|
||||
= column_ast->as<ASTOrderByElement &>();
|
||||
// Ignore collation for now.
|
||||
window_description.order_by.push_back(
|
||||
SortColumnDescription(
|
||||
order_by_element.children.front()->getColumnName(),
|
||||
order_by_element.direction,
|
||||
order_by_element.nulls_direction));
|
||||
}
|
||||
}
|
||||
|
||||
window_description.full_sort_description = window_description.partition_by;
|
||||
window_description.full_sort_description.insert(
|
||||
window_description.full_sort_description.end(),
|
||||
window_description.order_by.begin(),
|
||||
window_description.order_by.end());
|
||||
|
||||
WindowFunctionDescription window_function;
|
||||
window_function.function_node = function_node;
|
||||
window_function.column_name
|
||||
@ -578,19 +600,43 @@ bool ExpressionAnalyzer::makeWindowDescriptions(ActionsDAGPtr & actions)
|
||||
window_function.argument_types,
|
||||
window_function.function_parameters, properties);
|
||||
|
||||
auto [it, inserted] = window_descriptions.insert(
|
||||
{window_description.window_name, window_description});
|
||||
|
||||
if (!inserted)
|
||||
// Find the window corresponding to this function. It may be either
|
||||
// referenced by name and previously defined in WINDOW clause, or it
|
||||
// may be defined inline.
|
||||
if (!function_node->window_name.empty())
|
||||
{
|
||||
assert(it->second.full_sort_description
|
||||
== window_description.full_sort_description);
|
||||
auto it = window_descriptions.find(function_node->window_name);
|
||||
if (it == std::end(window_descriptions))
|
||||
{
|
||||
throw Exception(ErrorCodes::UNKNOWN_IDENTIFIER,
|
||||
"Window '{}' is not defined (referenced by '{}')",
|
||||
function_node->window_name,
|
||||
function_node->formatForErrorMessage());
|
||||
}
|
||||
|
||||
it->second.window_functions.push_back(window_function);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto & definition = function_node->window_definition->as<
|
||||
const ASTWindowDefinition &>();
|
||||
WindowDescription desc;
|
||||
desc.window_name = definition.getDefaultWindowName();
|
||||
makeWindowDescription(desc, &definition);
|
||||
|
||||
it->second.window_functions.push_back(window_function);
|
||||
auto [it, inserted] = window_descriptions.insert(
|
||||
{desc.window_name, desc});
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
assert(it->second.full_sort_description
|
||||
== desc.full_sort_description);
|
||||
}
|
||||
|
||||
it->second.window_functions.push_back(window_function);
|
||||
}
|
||||
}
|
||||
|
||||
return !syntax->window_function_asts.empty();
|
||||
}
|
||||
|
||||
|
||||
@ -969,28 +1015,42 @@ void SelectQueryExpressionAnalyzer::appendWindowFunctionsArguments(
|
||||
{
|
||||
ExpressionActionsChain::Step & step = chain.lastStep(aggregated_columns);
|
||||
|
||||
// 1) Add actions for window functions and their arguments;
|
||||
// 2) Mark the columns that are really required. We have to mark them as
|
||||
// required because we finish the expression chain before processing the
|
||||
// window functions.
|
||||
// (1) Add actions for window functions and the columns they require.
|
||||
// (2) Mark the columns that are really required. We have to mark them as
|
||||
// required because we finish the expression chain before processing the
|
||||
// window functions.
|
||||
// The required columns are:
|
||||
// (a) window function arguments,
|
||||
// (b) the columns from PARTITION BY and ORDER BY.
|
||||
|
||||
// (1a) Actions for PARTITION BY and ORDER BY for windows defined in the
|
||||
// WINDOW clause. The inline window definitions will be processed
|
||||
// recursively together with (1b) as ASTFunction::window_definition.
|
||||
if (getSelectQuery()->window())
|
||||
{
|
||||
getRootActionsNoMakeSet(getSelectQuery()->window(),
|
||||
true /* no_subqueries */, step.actions());
|
||||
}
|
||||
|
||||
for (const auto & [_, w] : window_descriptions)
|
||||
{
|
||||
for (const auto & f : w.window_functions)
|
||||
{
|
||||
// 1.1) arguments of window functions;
|
||||
// (1b) Actions for function arguments, and also the inline window
|
||||
// definitions (1a).
|
||||
// Requiring a constant reference to a shared pointer to non-const AST
|
||||
// doesn't really look sane, but the visitor does indeed require it.
|
||||
getRootActionsNoMakeSet(f.function_node->clone(),
|
||||
true /* no_subqueries */, step.actions());
|
||||
|
||||
// 2.1) function arguments;
|
||||
// (2b) Required function argument columns.
|
||||
for (const auto & a : f.function_node->arguments->children)
|
||||
{
|
||||
step.required_output.push_back(a->getColumnName());
|
||||
}
|
||||
}
|
||||
|
||||
// 2.1) PARTITION BY and ORDER BY columns.
|
||||
// (2a) Required PARTITION BY and ORDER BY columns.
|
||||
for (const auto & c : w.full_sort_description)
|
||||
{
|
||||
step.required_output.push_back(c.column_name);
|
||||
@ -1409,6 +1469,8 @@ ExpressionAnalysisResult::ExpressionAnalysisResult(
|
||||
// the main SELECT, similar to what we do for aggregate functions.
|
||||
if (has_window)
|
||||
{
|
||||
query_analyzer.makeWindowDescriptions(chain.getLastActions());
|
||||
|
||||
query_analyzer.appendWindowFunctionsArguments(chain, only_types || !first_stage);
|
||||
|
||||
// Build a list of output columns of the window step.
|
||||
|
@ -62,7 +62,6 @@ struct ExpressionAnalyzerData
|
||||
NamesAndTypesList aggregation_keys;
|
||||
AggregateDescriptions aggregate_descriptions;
|
||||
|
||||
bool has_window = false;
|
||||
WindowDescriptions window_descriptions;
|
||||
NamesAndTypesList window_columns;
|
||||
|
||||
@ -125,6 +124,8 @@ public:
|
||||
/// A list of windows for window functions.
|
||||
const WindowDescriptions & windowDescriptions() const { return window_descriptions; }
|
||||
|
||||
void makeWindowDescriptions(ActionsDAGPtr actions);
|
||||
|
||||
protected:
|
||||
ExpressionAnalyzer(
|
||||
const ASTPtr & query_,
|
||||
@ -168,8 +169,6 @@ protected:
|
||||
void analyzeAggregation();
|
||||
bool makeAggregateDescriptions(ActionsDAGPtr & actions);
|
||||
|
||||
bool makeWindowDescriptions(ActionsDAGPtr & actions);
|
||||
|
||||
const ASTSelectQuery * getSelectQuery() const;
|
||||
|
||||
bool isRemoteStorage() const { return syntax->is_remote_storage; }
|
||||
@ -272,7 +271,7 @@ public:
|
||||
|
||||
/// Does the expression have aggregate functions or a GROUP BY or HAVING section.
|
||||
bool hasAggregation() const { return has_aggregation; }
|
||||
bool hasWindow() const { return has_window; }
|
||||
bool hasWindow() const { return !syntax->window_function_asts.empty(); }
|
||||
bool hasGlobalSubqueries() { return has_global_subqueries; }
|
||||
bool hasTableJoin() const { return syntax->ast_join; }
|
||||
|
||||
|
@ -177,14 +177,9 @@ void QueryNormalizer::visitChildren(IAST * node, Data & data)
|
||||
}
|
||||
}
|
||||
|
||||
if (func_node->window_partition_by)
|
||||
if (func_node->window_definition)
|
||||
{
|
||||
visitChildren(func_node->window_partition_by.get(), data);
|
||||
}
|
||||
|
||||
if (func_node->window_order_by)
|
||||
{
|
||||
visitChildren(func_node->window_order_by.get(), data);
|
||||
visitChildren(func_node->window_definition.get(), data);
|
||||
}
|
||||
}
|
||||
else if (!node->as<ASTSelectQuery>())
|
||||
|
@ -464,6 +464,8 @@ std::vector<const ASTFunction *> getWindowFunctions(ASTPtr & query, const ASTSel
|
||||
assertNoWindows(select_query.where(), "in WHERE");
|
||||
if (select_query.prewhere())
|
||||
assertNoWindows(select_query.prewhere(), "in PREWHERE");
|
||||
if (select_query.window())
|
||||
assertNoWindows(select_query.window(), "in WINDOW");
|
||||
|
||||
GetAggregatesVisitor::Data data;
|
||||
GetAggregatesVisitor(data).visit(query);
|
||||
@ -481,20 +483,9 @@ std::vector<const ASTFunction *> getWindowFunctions(ASTPtr & query, const ASTSel
|
||||
}
|
||||
}
|
||||
|
||||
if (node->window_partition_by)
|
||||
if (node->window_definition)
|
||||
{
|
||||
for (auto & arg : node->window_partition_by->children)
|
||||
{
|
||||
assertNoWindows(arg, "inside PARTITION BY of a window");
|
||||
}
|
||||
}
|
||||
|
||||
if (node->window_order_by)
|
||||
{
|
||||
for (auto & arg : node->window_order_by->children)
|
||||
{
|
||||
assertNoWindows(arg, "inside ORDER BY of a window");
|
||||
}
|
||||
assertNoWindows(node->window_definition, "inside window definition");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <Parsers/ASTFunction.h>
|
||||
|
||||
#include <Common/quoteString.h>
|
||||
#include <Common/SipHash.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <IO/Operators.h>
|
||||
@ -42,12 +43,20 @@ void ASTFunction::appendColumnNameImpl(WriteBuffer & ostr) const
|
||||
|
||||
if (is_window_function)
|
||||
{
|
||||
writeCString(" OVER (", ostr);
|
||||
FormatSettings settings{ostr, true /* one_line */};
|
||||
FormatState state;
|
||||
FormatStateStacked frame;
|
||||
appendWindowDescription(settings, state, frame);
|
||||
writeCString(")", ostr);
|
||||
writeCString(" OVER ", ostr);
|
||||
if (!window_name.empty())
|
||||
{
|
||||
ostr << window_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
FormatSettings settings{ostr, true /* one_line */};
|
||||
FormatState state;
|
||||
FormatStateStacked frame;
|
||||
writeCString("(", ostr);
|
||||
window_definition->formatImpl(settings, state, frame);
|
||||
writeCString(")", ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,22 +74,10 @@ ASTPtr ASTFunction::clone() const
|
||||
if (arguments) { res->arguments = arguments->clone(); res->children.push_back(res->arguments); }
|
||||
if (parameters) { res->parameters = parameters->clone(); res->children.push_back(res->parameters); }
|
||||
|
||||
if (window_name)
|
||||
if (window_definition)
|
||||
{
|
||||
res->window_name = window_name->clone();
|
||||
res->children.push_back(res->window_name);
|
||||
}
|
||||
|
||||
if (window_partition_by)
|
||||
{
|
||||
res->window_partition_by = window_partition_by->clone();
|
||||
res->children.push_back(res->window_partition_by);
|
||||
}
|
||||
|
||||
if (window_order_by)
|
||||
{
|
||||
res->window_order_by = window_order_by->clone();
|
||||
res->children.push_back(res->window_order_by);
|
||||
res->window_definition = window_definition->clone();
|
||||
res->children.push_back(res->window_definition);
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -487,44 +484,16 @@ void ASTFunction::formatImplWithoutAlias(const FormatSettings & settings, Format
|
||||
return;
|
||||
}
|
||||
|
||||
settings.ostr << " OVER (";
|
||||
appendWindowDescription(settings, state, nested_dont_need_parens);
|
||||
settings.ostr << ")";
|
||||
}
|
||||
|
||||
std::string ASTFunction::getWindowDescription() const
|
||||
{
|
||||
WriteBufferFromOwnString ostr;
|
||||
FormatSettings settings{ostr, true /* one_line */};
|
||||
FormatState state;
|
||||
FormatStateStacked frame;
|
||||
appendWindowDescription(settings, state, frame);
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
void ASTFunction::appendWindowDescription(const FormatSettings & settings,
|
||||
FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
if (!is_window_function)
|
||||
settings.ostr << " OVER ";
|
||||
if (!window_name.empty())
|
||||
{
|
||||
return;
|
||||
settings.ostr << backQuoteIfNeed(window_name);
|
||||
}
|
||||
|
||||
if (window_partition_by)
|
||||
else
|
||||
{
|
||||
settings.ostr << "PARTITION BY ";
|
||||
window_partition_by->formatImpl(settings, state, frame);
|
||||
}
|
||||
|
||||
if (window_partition_by && window_order_by)
|
||||
{
|
||||
settings.ostr << " ";
|
||||
}
|
||||
|
||||
if (window_order_by)
|
||||
{
|
||||
settings.ostr << "ORDER BY ";
|
||||
window_order_by->formatImpl(settings, state, frame);
|
||||
settings.ostr << "(";
|
||||
window_definition->formatImpl(settings, state, frame);
|
||||
settings.ostr << ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,14 +32,8 @@ public:
|
||||
// pointers of proper type (see e.g. IAST::set), but this is not compatible
|
||||
// with the visitor interface.
|
||||
|
||||
// ASTIdentifier
|
||||
ASTPtr window_name;
|
||||
|
||||
// ASTExpressionList
|
||||
ASTPtr window_partition_by;
|
||||
|
||||
// ASTExpressionList of
|
||||
ASTPtr window_order_by;
|
||||
String window_name;
|
||||
ASTPtr window_definition;
|
||||
|
||||
/// do not print empty parentheses if there are no args - compatibility with new AST for data types and engine names.
|
||||
bool no_empty_args = false;
|
||||
@ -55,9 +49,6 @@ public:
|
||||
|
||||
ASTPtr toLiteral() const; // Try to convert functions like Array or Tuple to a literal form.
|
||||
|
||||
void appendWindowDescription(const FormatSettings & settings,
|
||||
FormatState & state, FormatStateStacked frame) const;
|
||||
|
||||
std::string getWindowDescription() const;
|
||||
|
||||
protected:
|
||||
|
@ -44,6 +44,7 @@ ASTPtr ASTSelectQuery::clone() const
|
||||
CLONE(Expression::WHERE);
|
||||
CLONE(Expression::GROUP_BY);
|
||||
CLONE(Expression::HAVING);
|
||||
CLONE(Expression::WINDOW);
|
||||
CLONE(Expression::ORDER_BY);
|
||||
CLONE(Expression::LIMIT_BY_OFFSET);
|
||||
CLONE(Expression::LIMIT_BY_LENGTH);
|
||||
@ -133,6 +134,13 @@ void ASTSelectQuery::formatImpl(const FormatSettings & s, FormatState & state, F
|
||||
having()->formatImpl(s, state, frame);
|
||||
}
|
||||
|
||||
if (window())
|
||||
{
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str <<
|
||||
"WINDOW " << (s.hilite ? hilite_none : "");
|
||||
window()->formatImpl(s, state, frame);
|
||||
}
|
||||
|
||||
if (orderBy())
|
||||
{
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "ORDER BY" << (s.hilite ? hilite_none : "");
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include <Parsers/ASTWindowDefinition.h>
|
||||
|
||||
#include <Common/quoteString.h>
|
||||
#include <IO/Operators.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -27,6 +30,37 @@ String ASTWindowDefinition::getID(char) const
|
||||
return "WindowDefinition";
|
||||
}
|
||||
|
||||
void ASTWindowDefinition::formatImpl(const FormatSettings & settings,
|
||||
FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
if (partition_by)
|
||||
{
|
||||
settings.ostr << "PARTITION BY ";
|
||||
partition_by->formatImpl(settings, state, frame);
|
||||
}
|
||||
|
||||
if (partition_by && order_by)
|
||||
{
|
||||
settings.ostr << " ";
|
||||
}
|
||||
|
||||
if (order_by)
|
||||
{
|
||||
settings.ostr << "ORDER BY ";
|
||||
order_by->formatImpl(settings, state, frame);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ASTWindowDefinition::getDefaultWindowName() const
|
||||
{
|
||||
WriteBufferFromOwnString ostr;
|
||||
FormatSettings settings{ostr, true /* one_line */};
|
||||
FormatState state;
|
||||
FormatStateStacked frame;
|
||||
formatImpl(settings, state, frame);
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
ASTPtr ASTWindowListElement::clone() const
|
||||
{
|
||||
auto result = std::make_shared<ASTWindowListElement>();
|
||||
@ -43,4 +77,13 @@ String ASTWindowListElement::getID(char) const
|
||||
return "WindowListElement";
|
||||
}
|
||||
|
||||
void ASTWindowListElement::formatImpl(const FormatSettings & settings,
|
||||
FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
settings.ostr << backQuoteIfNeed(name);
|
||||
settings.ostr << " AS (";
|
||||
definition->formatImpl(settings, state, frame);
|
||||
settings.ostr << ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,9 +12,14 @@ struct ASTWindowDefinition : public IAST
|
||||
|
||||
ASTPtr order_by;
|
||||
|
||||
|
||||
ASTPtr clone() const override;
|
||||
|
||||
String getID(char delimiter) const override;
|
||||
|
||||
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
|
||||
std::string getDefaultWindowName() const;
|
||||
};
|
||||
|
||||
struct ASTWindowListElement : public IAST
|
||||
@ -24,9 +29,12 @@ struct ASTWindowListElement : public IAST
|
||||
// ASTWindowDefinition
|
||||
ASTPtr definition;
|
||||
|
||||
|
||||
ASTPtr clone() const override;
|
||||
|
||||
String getID(char delimiter) const override;
|
||||
|
||||
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -440,8 +440,7 @@ bool ParserWindowReference::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
ParserIdentifier window_name_parser;
|
||||
if (window_name_parser.parse(pos, window_name_ast, expected))
|
||||
{
|
||||
function->children.push_back(window_name_ast);
|
||||
function->window_name = window_name_ast;
|
||||
function->window_name = getIdentifierName(window_name_ast);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -449,52 +448,11 @@ bool ParserWindowReference::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++pos;
|
||||
|
||||
// Variant 2:
|
||||
// function_name ( * ) OVER ( window_definition )
|
||||
ParserKeyword keyword_partition_by("PARTITION BY");
|
||||
ParserNotEmptyExpressionList columns_partition_by(
|
||||
false /* we don't allow declaring aliases here*/);
|
||||
ParserKeyword keyword_order_by("ORDER BY");
|
||||
ParserOrderByExpressionList columns_order_by;
|
||||
|
||||
if (keyword_partition_by.ignore(pos, expected))
|
||||
{
|
||||
ASTPtr partition_by_ast;
|
||||
if (columns_partition_by.parse(pos, partition_by_ast, expected))
|
||||
{
|
||||
function->children.push_back(partition_by_ast);
|
||||
function->window_partition_by = partition_by_ast;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyword_order_by.ignore(pos, expected))
|
||||
{
|
||||
ASTPtr order_by_ast;
|
||||
if (columns_order_by.parse(pos, order_by_ast, expected))
|
||||
{
|
||||
function->children.push_back(order_by_ast);
|
||||
function->window_order_by = order_by_ast;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos->type != TokenType::ClosingRoundBracket)
|
||||
{
|
||||
expected.add(pos, "')'");
|
||||
return false;
|
||||
}
|
||||
++pos;
|
||||
|
||||
return true;
|
||||
ParserWindowDefinition parser_definition;
|
||||
return parser_definition.parse(pos, function->window_definition, expected);
|
||||
}
|
||||
|
||||
bool ParserWindowDefinition::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
|
@ -189,6 +189,10 @@ std::string getLexicalErrorMessage(
|
||||
writeQueryAroundTheError(out, begin, end, hilite, &last_token, 1);
|
||||
|
||||
out << getErrorTokenDescription(last_token.type);
|
||||
if (last_token.size())
|
||||
{
|
||||
out << ": '" << StringRef{last_token.begin, last_token.size()} << "'";
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
@ -123,16 +123,26 @@ select * from (select * from numbers(5) order by rand()) order by count() over (
|
||||
3
|
||||
4
|
||||
select * from (select * from numbers(5) order by rand()) group by number order by sum(any(number + 1)) over (order by min(number) desc) desc;
|
||||
|
||||
-- different windows
|
||||
-- an explain test would also be helpful, but it's too immature now and I don't
|
||||
-- want to change reference all the time
|
||||
-- some more simple cases w/aggregate functions
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
select sum(any(number)) over () from numbers(1);
|
||||
|
||||
0
|
||||
select sum(any(number) + 1) over () from numbers(1);
|
||||
|
||||
1
|
||||
select sum(any(number + 1)) over () from numbers(1);
|
||||
|
||||
-- different windows
|
||||
-- an explain test would also be helpful, but it's too immature now and I don't
|
||||
-- want to change reference all the time
|
||||
|
||||
1
|
||||
select number, max(number) over (partition by intDiv(number, 3) order by number desc), count(number) over (partition by intDiv(number, 5) order by number) as m from numbers(31) order by number settings max_block_size = 2;
|
||||
|
||||
-- two functions over the same window
|
||||
@ -210,6 +220,8 @@ select distinct any(number) over () from numbers(2);
|
||||
0
|
||||
with number + 1 as x select intDiv(number, 3) as y, sum(x + y) over (partition by y order by x) from numbers(7);
|
||||
|
||||
-- WINDOW clause
|
||||
|
||||
0 1
|
||||
0 3
|
||||
0 6
|
||||
@ -217,3 +229,43 @@ with number + 1 as x select intDiv(number, 3) as y, sum(x + y) over (partition b
|
||||
1 11
|
||||
1 18
|
||||
2 9
|
||||
select 1 window w1 as ();
|
||||
|
||||
|
||||
1
|
||||
select sum(number) over w1, sum(number) over w2
|
||||
from numbers(10)
|
||||
window
|
||||
w1 as (),
|
||||
w2 as (partition by intDiv(number, 3))
|
||||
;
|
||||
|
||||
|
||||
0 0
|
||||
1 1
|
||||
3 3
|
||||
6 3
|
||||
10 7
|
||||
15 12
|
||||
21 6
|
||||
28 13
|
||||
36 21
|
||||
45 9
|
||||
select
|
||||
sum(number) over w1,
|
||||
sum(number) over (partition by intDiv(number, 3))
|
||||
from numbers(10)
|
||||
window
|
||||
w1 as (partition by intDiv(number, 3))
|
||||
;
|
||||
|
||||
0 0
|
||||
1 1
|
||||
3 3
|
||||
3 3
|
||||
7 7
|
||||
12 12
|
||||
6 6
|
||||
13 13
|
||||
21 21
|
||||
9 9
|
||||
|
@ -42,6 +42,10 @@ select * from (select * from numbers(5) order by rand()) order by count() over (
|
||||
-- the same as the above one, only we replace `number` with
|
||||
-- `any(number) group by number` and so on.
|
||||
select * from (select * from numbers(5) order by rand()) group by number order by sum(any(number + 1)) over (order by min(number) desc) desc;
|
||||
-- some more simple cases w/aggregate functions
|
||||
select sum(any(number)) over () from numbers(1);
|
||||
select sum(any(number) + 1) over () from numbers(1);
|
||||
select sum(any(number + 1)) over () from numbers(1);
|
||||
|
||||
-- different windows
|
||||
-- an explain test would also be helpful, but it's too immature now and I don't
|
||||
@ -70,3 +74,21 @@ select distinct any(number) over () from numbers(2);
|
||||
-- Various kinds of aliases are properly substituted into various parts of window
|
||||
-- function definition.
|
||||
with number + 1 as x select intDiv(number, 3) as y, sum(x + y) over (partition by y order by x) from numbers(7);
|
||||
|
||||
-- WINDOW clause
|
||||
select 1 window w1 as ();
|
||||
|
||||
select sum(number) over w1, sum(number) over w2
|
||||
from numbers(10)
|
||||
window
|
||||
w1 as (),
|
||||
w2 as (partition by intDiv(number, 3))
|
||||
;
|
||||
|
||||
select
|
||||
sum(number) over w1,
|
||||
sum(number) over (partition by intDiv(number, 3))
|
||||
from numbers(10)
|
||||
window
|
||||
w1 as (partition by intDiv(number, 3))
|
||||
;
|
||||
|
Loading…
Reference in New Issue
Block a user