Skip to content

Commit

Permalink
SQL 2023 ANY_VALUE aggregate function. (#7617)
Browse files Browse the repository at this point in the history
  • Loading branch information
asfernandes committed Sep 18, 2023
1 parent fa9c6d4 commit 986e96f
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 0 deletions.
24 changes: 24 additions & 0 deletions 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> ::= ANY_VALUE(<expression>)
```

Example:

```
select department,
any_value(employee) employee
from employee_department
group by department
```
1 change: 1 addition & 0 deletions src/common/ParserTokens.h
Expand Up @@ -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)
Expand Down
86 changes: 86 additions & 0 deletions src/dsql/AggNodes.cpp
Expand Up @@ -491,6 +491,92 @@ dsc* AggNode::execute(thread_db* tdbb, Request* request) const
//--------------------


static AggNode::RegisterFactory0<AnyValueAggNode> 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<impure_value_ex>(impureOffset);
impure->vlu_desc.dsc_dtype = 0;
}

void AnyValueAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const
{
const auto impure = request->getImpure<impure_value_ex>(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<impure_value_ex>(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<AvgAggNode> avgAggInfo("AVG", blr_agg_average, blr_agg_average_distinct);

AvgAggNode::AvgAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg)
Expand Down
27 changes: 27 additions & 0 deletions src/dsql/AggNodes.h
Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions src/dsql/parse.y
Expand Up @@ -696,6 +696,10 @@ using namespace Firebird;
%token <metaNamePtr> UNICODE_CHAR
%token <metaNamePtr> UNICODE_VAL

// tokens added for Firebird 6.0

%token <metaNamePtr> ANY_VALUE

// precedence declarations for expression evaluation

%left OR
Expand Down Expand Up @@ -8029,6 +8033,8 @@ aggregate_function_prefix
{ $$ = newNode<RegrAggNode>(RegrAggNode::TYPE_REGR_SXY, $3, $5); }
| REGR_SYY '(' value ',' value ')'
{ $$ = newNode<RegrAggNode>(RegrAggNode::TYPE_REGR_SYY, $3, $5); }
| ANY_VALUE '(' distinct_noise value ')'
{ $$ = newNode<AnyValueAggNode>($4); }
;

%type <aggNode> window_function
Expand Down Expand Up @@ -9225,6 +9231,8 @@ non_reserved_word
| TIMEZONE_NAME
| UNICODE_CHAR
| UNICODE_VAL
// added in FB 6.0
| ANY_VALUE
;

%%

0 comments on commit 986e96f

Please sign in to comment.