something works

This commit is contained in:
Alexander Kuzmenkov 2021-01-13 22:29:52 +03:00
parent e6c2d0219d
commit 703731c547
16 changed files with 316 additions and 215 deletions

View File

@ -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);

View File

@ -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; }

View File

@ -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.

View File

@ -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.

View File

@ -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; }

View File

@ -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>())

View File

@ -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");
}
}

View File

@ -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 << ")";
}
}

View File

@ -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:

View File

@ -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 : "");

View File

@ -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 << ")";
}
}

View File

@ -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;
};
}

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View File

@ -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))
;