From 986e96fac88b45cda573203d38519ea910304474 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes <529415+asfernandes@users.noreply.github.com> Date: Mon, 18 Sep 2023 07:51:41 -0300 Subject: [PATCH] SQL 2023 ANY_VALUE aggregate function. (#7617) --- .../README.aggregate_functions.md | 24 ++++++ src/common/ParserTokens.h | 1 + src/dsql/AggNodes.cpp | 86 +++++++++++++++++++ src/dsql/AggNodes.h | 27 ++++++ src/dsql/parse.y | 8 ++ 5 files changed, 146 insertions(+) create mode 100644 doc/sql.extensions/README.aggregate_functions.md diff --git a/doc/sql.extensions/README.aggregate_functions.md b/doc/sql.extensions/README.aggregate_functions.md new file mode 100644 index 00000000000..5ed0211e346 --- /dev/null +++ b/doc/sql.extensions/README.aggregate_functions.md @@ -0,0 +1,24 @@ +# Aggregate Functions + + +## ANY_VALUE (Firebird 6.0) + +`ANY_VALUE` is a non-deterministic aggregate function that returns its expression for an arbitrary +record from the grouped rows. + +`NULLs` are ignored. It's returned only in the case of none evaluated records having a non-null value. + +Syntax: + +``` + ::= ANY_VALUE() +``` + +Example: + +``` +select department, + any_value(employee) employee + from employee_department + group by department +``` diff --git a/src/common/ParserTokens.h b/src/common/ParserTokens.h index 29fb135b666..b4b8ba17daa 100644 --- a/src/common/ParserTokens.h +++ b/src/common/ParserTokens.h @@ -62,6 +62,7 @@ PARSER_TOKEN(TOK_AFTER, "AFTER", true) PARSER_TOKEN(TOK_ALL, "ALL", false) PARSER_TOKEN(TOK_ALTER, "ALTER", false) PARSER_TOKEN(TOK_ALWAYS, "ALWAYS", true) +PARSER_TOKEN(TOK_ANY_VALUE, "ANY_VALUE", true) PARSER_TOKEN(TOK_AND, "AND", false) PARSER_TOKEN(TOK_ANY, "ANY", false) PARSER_TOKEN(TOK_AS, "AS", false) diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index 0e9a9d3dd8c..f65dc7122bb 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -491,6 +491,92 @@ dsc* AggNode::execute(thread_db* tdbb, Request* request) const //-------------------- +static AggNode::RegisterFactory0 anyValueAggInfo("ANY_VALUE"); + +AnyValueAggNode::AnyValueAggNode(MemoryPool& pool, ValueExprNode* aArg) + : AggNode(pool, anyValueAggInfo, false, false, aArg) +{ +} + +DmlNode* AnyValueAggNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) +{ + const auto node = FB_NEW_POOL(pool) AnyValueAggNode(pool); + node->arg = PAR_parse_value(tdbb, csb); + return node; +} + +void AnyValueAggNode::parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned /*count*/) +{ + arg = PAR_parse_value(tdbb, csb); +} + +void AnyValueAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) +{ + DsqlDescMaker::fromNode(dsqlScratch, desc, arg, true); +} + +void AnyValueAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) +{ + arg->getDesc(tdbb, csb, desc); +} + +ValueExprNode* AnyValueAggNode::copy(thread_db* tdbb, NodeCopier& copier) const +{ + const auto node = FB_NEW_POOL(*tdbb->getDefaultPool()) AnyValueAggNode(*tdbb->getDefaultPool()); + node->nodScale = nodScale; + node->arg = copier.copy(tdbb, arg); + return node; +} + +string AnyValueAggNode::internalPrint(NodePrinter& printer) const +{ + AggNode::internalPrint(printer); + + return "AnyValueAggNode"; +} + +void AnyValueAggNode::aggInit(thread_db* tdbb, Request* request) const +{ + AggNode::aggInit(tdbb, request); + + const auto impure = request->getImpure(impureOffset); + impure->vlu_desc.dsc_dtype = 0; +} + +void AnyValueAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const +{ + const auto impure = request->getImpure(impureOffset); + + if (!impure->vlu_desc.dsc_dtype) + { + const auto argValue = EVL_expr(tdbb, request, arg); + + if (!(request->req_flags & req_null)) + EVL_make_value(tdbb, argValue, impure); + } + +} + +dsc* AnyValueAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const +{ + const auto impure = request->getImpure(impureOffset); + + if (impure->vlu_desc.dsc_dtype) + return &impure->vlu_desc; + + return nullptr; +} + +AggNode* AnyValueAggNode::dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/ +{ + return FB_NEW_POOL(dsqlScratch->getPool()) AnyValueAggNode(dsqlScratch->getPool(), + doDsqlPass(dsqlScratch, arg)); +} + + +//-------------------- + + static AggNode::Register avgAggInfo("AVG", blr_agg_average, blr_agg_average_distinct); AvgAggNode::AvgAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg) diff --git a/src/dsql/AggNodes.h b/src/dsql/AggNodes.h index 0c5f105a21a..861b1e6c377 100644 --- a/src/dsql/AggNodes.h +++ b/src/dsql/AggNodes.h @@ -30,6 +30,33 @@ namespace Jrd { +class AnyValueAggNode final : public AggNode +{ +public: + explicit AnyValueAggNode(MemoryPool& pool, ValueExprNode* aArg = nullptr); + + static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + + unsigned getCapabilities() const override + { + return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS; + } + + void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count) override; + + Firebird::string internalPrint(NodePrinter& printer) const override; + void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; + ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; + + void aggInit(thread_db* tdbb, Request* request) const override; + void aggPass(thread_db* tdbb, Request* request, dsc* desc) const override; + dsc* aggExecute(thread_db* tdbb, Request* request) const override; + +protected: + AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/ override; +}; + class AvgAggNode final : public AggNode { public: diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 2c93e06ee14..a10ed10b9c0 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -696,6 +696,10 @@ using namespace Firebird; %token UNICODE_CHAR %token UNICODE_VAL +// tokens added for Firebird 6.0 + +%token ANY_VALUE + // precedence declarations for expression evaluation %left OR @@ -8029,6 +8033,8 @@ aggregate_function_prefix { $$ = newNode(RegrAggNode::TYPE_REGR_SXY, $3, $5); } | REGR_SYY '(' value ',' value ')' { $$ = newNode(RegrAggNode::TYPE_REGR_SYY, $3, $5); } + | ANY_VALUE '(' distinct_noise value ')' + { $$ = newNode($4); } ; %type window_function @@ -9225,6 +9231,8 @@ non_reserved_word | TIMEZONE_NAME | UNICODE_CHAR | UNICODE_VAL + // added in FB 6.0 + | ANY_VALUE ; %%