8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 08:00:39 +01:00

Add window functions, PERCENT_RANK(), CUME_DIST() and NTILE().

This commit is contained in:
Hajime Nakagami 2016-04-16 02:32:35 +09:00
parent 783806a4ed
commit 8745489bb0
10 changed files with 450 additions and 59 deletions

View File

@ -144,6 +144,9 @@ Syntax:
<ranking window function> ::=
DENSE_RANK() |
RANK() |
PERCENT_RANK() |
CUME_DIST() |
NTILE(<expr>) |
ROW_NUMBER()
The rank functions compute the ordinal rank of a row within the window partition. In this category
@ -158,6 +161,9 @@ select
salary,
dense_rank() over (order by salary),
rank() over (order by salary),
percent_rank() over (order by salary),
cume_dist() over (order by salary),
ntile(3) over (order by salary),
row_number() over (order by salary),
sum(1) over (order by salary)
from employee
@ -165,18 +171,21 @@ select
And the result set:
id salary dense_rank rank row_number sum
-- ------ ---------- ---- ---------- ---
3 8.00 1 1 1 1
4 9.00 2 2 2 2
1 10.00 3 3 3 4
5 10.00 3 3 4 4
2 12.00 4 5 5 5
id salary dense_rank rank percent_rank cume_dist ntile row_number sum
-- ------ ---------- ---- ------------------ -------------------- ------ ---------- ---
3 8.00 1 1 0.000000000000000 0.2000000000000000 1 1 1
4 9.00 2 2 0.2500000000000000 0.4000000000000000 1 2 2
1 10.00 3 3 0.5000000000000000 0.8000000000000000 2 3 4
5 10.00 3 3 0.5000000000000000 0.8000000000000000 2 4 4
2 12.00 4 5 1.000000000000000 1.000000000000000 3 5 5
The difference between DENSE_RANK and RANK is that there is a gap related to duplicate rows (in
relation to the window ordering) only in RANK. DENSE_RANK continues assigning sequential numbers
after the duplicate salary. On the other hand, ROW_NUMBER always assigns sequential numbers, even
when there is duplicate values.
PERCENT_RANK is a ratio of RANK to group count.
CUME_DIST is cumulative distribution of a value in a group.
NTILE distributes the rows into a specified number of groups.
4.2 Navigational functions
--------------------------

View File

@ -425,7 +425,7 @@ void AggNode::aggFinish(thread_db* /*tdbb*/, jrd_req* request) const
}
}
dsc* AggNode::execute(thread_db* tdbb, jrd_req* request) const
dsc* AggNode::execute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -464,7 +464,14 @@ dsc* AggNode::execute(thread_db* tdbb, jrd_req* request) const
}
}
return aggExecute(tdbb, request);
return aggExecute(tdbb, request, win_row_count);
}
dsc* AggNode::execute(thread_db* tdbb, jrd_req* request) const
{
fb_assert(false);
return NULL;
}
@ -652,7 +659,7 @@ void AvgAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
ArithmeticNode::add2(desc, impure, this, blr_add);
}
dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -797,7 +804,7 @@ void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
impure->vlu_blob->BLB_put_data(tdbb, temp, len);
}
dsc* ListAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* ListAggNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -904,7 +911,7 @@ void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/)
++impure->vlu_misc.vlu_int64;
}
dsc* CountAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
dsc* CountAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -1141,7 +1148,7 @@ void SumAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
ArithmeticNode::add2(desc, impure, this, blr_add);
}
dsc* SumAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
dsc* SumAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -1232,7 +1239,7 @@ void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
EVL_make_value(tdbb, desc, impure);
}
dsc* MaxMinAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
dsc* MaxMinAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -1338,7 +1345,7 @@ void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
impure2->x2 += d * d;
}
dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
StdDevImpure* impure2 = request->getImpure<StdDevImpure>(impure2Offset);
@ -1497,7 +1504,7 @@ void CorrAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc
fb_assert(false);
}
dsc* CorrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* CorrAggNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
CorrImpure* impure2 = request->getImpure<CorrImpure>(impure2Offset);
@ -1682,7 +1689,7 @@ void RegrAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc
fb_assert(false);
}
dsc* RegrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* RegrAggNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
RegrImpure* impure2 = request->getImpure<RegrImpure>(impure2Offset);
@ -1839,7 +1846,7 @@ void RegrCountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
fb_assert(false);
}
dsc* RegrCountAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* RegrCountAggNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);

View File

@ -45,7 +45,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -79,7 +79,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -103,7 +103,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -123,7 +123,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -149,7 +149,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -186,7 +186,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -227,7 +227,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -274,7 +274,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -303,7 +303,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;

View File

@ -974,7 +974,7 @@ public:
return false;
}
virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/) const
virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/, FB_UINT64 /*win_rou_count*/) const
{
return NULL;
}
@ -983,9 +983,10 @@ public:
virtual void aggFinish(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* execute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const = 0;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const = 0;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const = 0;
virtual AggNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);

View File

@ -91,7 +91,7 @@ void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
{
}
dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
++impure->vlu_misc.vlu_int64;
@ -167,7 +167,7 @@ void RankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/)
++impure->vlux_count;
}
dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request) const
dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -192,6 +192,171 @@ AggNode* RankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
//--------------------
static WinFuncNode::RegisterFactory0<PercentRankWinNode> percentRankWinInfo("PERCENT_RANK");
PercentRankWinNode::PercentRankWinNode(MemoryPool& pool)
: WinFuncNode(pool, percentRankWinInfo),
tempImpure(0)
{
fb_assert(dsqlChildNodes.getCount() == 1 && jrdChildNodes.getCount() == 1);
dsqlChildNodes.clear();
jrdChildNodes.clear();
}
string PercentRankWinNode::internalPrint(NodePrinter& printer) const
{
WinFuncNode::internalPrint(printer);
NODE_PRINT(printer, tempImpure);
return "PercentRankWinNode";
}
void PercentRankWinNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
{
desc->makeDouble();
}
void PercentRankWinNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc)
{
desc->makeDouble();
}
ValueExprNode* PercentRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) const
{
return FB_NEW_POOL(*tdbb->getDefaultPool()) PercentRankWinNode(*tdbb->getDefaultPool());
}
AggNode* PercentRankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
AggNode::pass2(tdbb, csb);
tempImpure = CMP_impure(csb, sizeof(impure_value_ex));
return this;
}
void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(1, 0);
impure->vlux_count = 0;
}
void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
++impure->vlux_count;
}
dsc* PercentRankWinNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
dsc temp;
double d = (double)(impure->vlu_misc.vlu_int64 -1) / (double)(win_row_count -1);
temp.makeDouble(&d);
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
EVL_make_value(tdbb, &temp, impureTemp);
impure->vlu_misc.vlu_int64 += impure->vlux_count;
impure->vlux_count = 0;
return &impureTemp->vlu_desc;
}
AggNode* PercentRankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
{
return FB_NEW_POOL(getPool()) PercentRankWinNode(getPool());
}
//--------------------
static WinFuncNode::RegisterFactory0<CumeDistWinNode> cumeDistWinInfo("CUME_DIST");
CumeDistWinNode::CumeDistWinNode(MemoryPool& pool)
: WinFuncNode(pool, cumeDistWinInfo),
tempImpure(0)
{
fb_assert(dsqlChildNodes.getCount() == 1 && jrdChildNodes.getCount() == 1);
dsqlChildNodes.clear();
jrdChildNodes.clear();
}
string CumeDistWinNode::internalPrint(NodePrinter& printer) const
{
WinFuncNode::internalPrint(printer);
NODE_PRINT(printer, tempImpure);
return "CumeDistWinNode";
}
void CumeDistWinNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
{
desc->makeDouble();
}
void CumeDistWinNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc)
{
desc->makeDouble();
}
ValueExprNode* CumeDistWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) const
{
return FB_NEW_POOL(*tdbb->getDefaultPool()) CumeDistWinNode(*tdbb->getDefaultPool());
}
AggNode* CumeDistWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
AggNode::pass2(tdbb, csb);
tempImpure = CMP_impure(csb, sizeof(impure_value_ex));
return this;
}
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(1, 0);
impure->vlux_count = 0;
}
void CumeDistWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
++impure->vlux_count;
}
dsc* CumeDistWinNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
dsc temp;
impure->vlu_misc.vlu_int64 += impure->vlux_count;
impure->vlux_count = 0;
double d = (double)(impure->vlu_misc.vlu_int64 -1) / (double)(win_row_count);
temp.makeDouble(&d);
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
EVL_make_value(tdbb, &temp, impureTemp);
return &impureTemp->vlu_desc;
}
AggNode* CumeDistWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
{
return FB_NEW_POOL(getPool()) CumeDistWinNode(getPool());
}
//--------------------
static WinFuncNode::RegisterFactory0<RowNumberWinNode> rowNumberWinInfo("ROW_NUMBER");
RowNumberWinNode::RowNumberWinNode(MemoryPool& pool)
@ -238,13 +403,13 @@ void RowNumberWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
{
}
dsc* RowNumberWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
dsc* RowNumberWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
return &impure->vlu_desc;
}
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* /*window*/) const
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* /*window*/, FB_UINT64 /*win_row_count*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
++impure->vlu_misc.vlu_int64;
@ -308,12 +473,12 @@ void FirstValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc*
{
}
dsc* FirstValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
dsc* FirstValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/, FB_UINT64 win_row_count) const
{
return NULL;
}
dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_row_count*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
SINT64 records = impure->vlu_misc.vlu_int64++;
@ -385,12 +550,12 @@ void LastValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
{
}
dsc* LastValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
dsc* LastValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/, FB_UINT64 win_row_count) const
{
return NULL;
}
dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_row_count*/) const
{
if (!window->move(0))
return NULL;
@ -472,12 +637,12 @@ void NthValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*
{
}
dsc* NthValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
dsc* NthValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/, FB_UINT64 win_row_count) const
{
return NULL;
}
dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_row_count*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -582,12 +747,12 @@ void LagLeadWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*d
{
}
dsc* LagLeadWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
dsc* LagLeadWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/, FB_UINT64 win_row_count) const
{
return NULL;
}
dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_row_count*/) const
{
window->move(0); // Come back to our row because rows may reference columns.
@ -679,4 +844,117 @@ AggNode* LeadWinNode::dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/
}
//--------------------
static WinFuncNode::RegisterFactory0<NTileWinNode> nTileWinInfo("NTILE");
NTileWinNode::NTileWinNode(MemoryPool& pool, ValueExprNode* aArg)
: WinFuncNode(pool, nTileWinInfo, aArg),
tempImpure(0)
{
fb_assert(dsqlChildNodes.getCount() == 1 && jrdChildNodes.getCount() == 1);
dsqlChildNodes.clear();
jrdChildNodes.clear();
addChildNode(arg, arg);
}
void NTileWinNode::parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned /*count*/)
{
arg = PAR_parse_value(tdbb, csb);
}
string NTileWinNode::internalPrint(NodePrinter& printer) const
{
WinFuncNode::internalPrint(printer);
NODE_PRINT(printer, tempImpure);
return "NTileWinNode";
}
void NTileWinNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
{
if (dsqlScratch->clientDialect == 1)
desc->makeDouble();
else
desc->makeInt64(0);
}
void NTileWinNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc)
{
desc->makeInt64(0);
}
ValueExprNode* NTileWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
{
NTileWinNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) NTileWinNode(*tdbb->getDefaultPool());
node->arg = copier.copy(tdbb, arg);
return node;
}
AggNode* NTileWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
AggNode::pass2(tdbb, csb);
tempImpure = CMP_impure(csb, sizeof(impure_value_ex));
return this;
}
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
impure->vlux_count = 0;
}
void NTileWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
{
}
dsc* NTileWinNode::aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 /*win_row_count*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
return &impure->vlu_desc;
}
dsc* NTileWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 win_row_count) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
window->move(0); // Come back to our row because row may reference columns.
dsc* desc = EVL_expr(tdbb, request, arg);
if (!desc || (request->req_flags & req_null))
status_exception::raise(Arg::Gds(isc_sysf_argnmustbe_positive) <<
Arg::Num(1) << Arg::Str(aggInfo.name));
SINT64 buckets = MOV_get_int64(desc, 0);
if (buckets <= 0)
{
status_exception::raise(Arg::Gds(isc_sysf_argnmustbe_positive) <<
Arg::Num(1) << Arg::Str(aggInfo.name));
}
SINT64 n = impure->vlux_count;
SINT64 top_boundary = win_row_count / buckets + 1;
SINT64 bottom_boundary = win_row_count / buckets;
SINT64 num_top_boundary = win_row_count % buckets;
if (n < top_boundary * num_top_boundary)
impure->vlu_misc.vlu_int64 = (n / top_boundary) + 1;
else
impure->vlu_misc.vlu_int64 = num_top_boundary + (n - num_top_boundary * top_boundary) / bottom_boundary + 1;
++impure->vlux_count;
return &impure->vlu_desc;
}
AggNode* NTileWinNode::dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/
{
return FB_NEW_POOL(getPool()) NTileWinNode(getPool(), doDsqlPass(dsqlScratch, arg));
}
} // namespace Jrd

View File

@ -43,7 +43,7 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -63,7 +63,53 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
private:
USHORT tempImpure;
};
// PERCENT_RANK function.
class PercentRankWinNode : public WinFuncNode
{
public:
explicit PercentRankWinNode(MemoryPool& pool);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
private:
USHORT tempImpure;
};
// CUME_DIST function.
class CumeDistWinNode : public WinFuncNode
{
public:
explicit CumeDistWinNode(MemoryPool& pool);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -85,14 +131,14 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual bool shouldCallWinPass() const
{
return true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_rou_count*/) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -111,14 +157,14 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual bool shouldCallWinPass() const
{
return true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_rou_count*/) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -139,14 +185,14 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual bool shouldCallWinPass() const
{
return true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_rou_count*/) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -175,14 +221,14 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual bool shouldCallWinPass() const
{
return true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_rou_count*/) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
@ -207,14 +253,14 @@ public:
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual bool shouldCallWinPass() const
{
return true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_rou_count*/) const;
protected:
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
@ -263,6 +309,36 @@ protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
};
// NTILE function.
class NTileWinNode : public WinFuncNode
{
public:
explicit NTileWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request, FB_UINT64 win_row_count) const;
virtual bool shouldCallWinPass() const
{
return true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window, FB_UINT64 /*win_rou_count*/) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
private:
USHORT tempImpure;
};
} // namespace

View File

@ -589,6 +589,12 @@ using namespace Firebird;
%token <metaNamePtr> REGR_SXY
%token <metaNamePtr> REGR_SYY
// tokens added for Firebird 4.0
%token <metaNamePtr> PERCENT_RANK
%token <metaNamePtr> CUME_DIST
%token <metaNamePtr> NTILE
// precedence declarations for expression evaluation
%left OR
@ -6945,6 +6951,10 @@ window_function
{ $$ = newNode<DenseRankWinNode>(); }
| RANK '(' ')'
{ $$ = newNode<RankWinNode>(); }
| PERCENT_RANK '(' ')'
{ $$ = newNode<PercentRankWinNode>(); }
| CUME_DIST '(' ')'
{ $$ = newNode<CumeDistWinNode>(); }
| ROW_NUMBER '(' ')'
{ $$ = newNode<RowNumberWinNode>(); }
| FIRST_VALUE '(' value ')'
@ -6965,6 +6975,8 @@ window_function
{ $$ = newNode<LeadWinNode>($3, $5, newNode<NullNode>()); }
| LEAD '(' value ')'
{ $$ = newNode<LeadWinNode>($3, MAKE_const_slong(1), newNode<NullNode>()); }
| NTILE '(' u_numeric_constant ')'
{ $$ = newNode<NTileWinNode>($3); }
;
%type <valueExprNode> nth_from
@ -7774,7 +7786,10 @@ non_reserved_word
| LAST_VALUE
| LAG
| LEAD
| NTILE
| RANK
| PERCENT_RANK
| CUME_DIST
| ROW_NUMBER
| USAGE
| LINGER

View File

@ -125,6 +125,8 @@ bool AggregatedStream::getRecord(thread_db* tdbb) const
if (m_bufferedStream) // Is that a window stream?
{
const FB_UINT64 position = m_bufferedStream->getPosition(request);
FB_UINT64 win_row_count = m_bufferedStream->getCount(tdbb);
m_bufferedStream->locate(tdbb, position);
if (impure->pending == 0)
{
@ -134,7 +136,7 @@ bool AggregatedStream::getRecord(thread_db* tdbb) const
fb_assert(false);
}
impure->state = evaluateGroup(tdbb, impure->state);
impure->state = evaluateGroup(tdbb, impure->state, win_row_count);
if (impure->state == STATE_PROCESS_EOF)
{
@ -165,7 +167,7 @@ bool AggregatedStream::getRecord(thread_db* tdbb) const
const USHORT id = field->fieldId;
Record* record = request->req_rpb[field->fieldStream].rpb_record;
desc = aggNode->winPass(tdbb, request, &window);
desc = aggNode->winPass(tdbb, request, &window, win_row_count);
if (!desc)
record->setNull(id);
@ -205,7 +207,7 @@ bool AggregatedStream::getRecord(thread_db* tdbb) const
}
else
{
impure->state = evaluateGroup(tdbb, impure->state);
impure->state = evaluateGroup(tdbb, impure->state, 0);
if (impure->state == STATE_PROCESS_EOF)
{
@ -295,7 +297,7 @@ void AggregatedStream::init(thread_db* tdbb, CompilerScratch* csb)
// Compute the next aggregated record of a value group. evlGroup is driven by, and returns, a state
// variable.
AggregatedStream::State AggregatedStream::evaluateGroup(thread_db* tdbb, State state) const
AggregatedStream::State AggregatedStream::evaluateGroup(thread_db* tdbb, State state, FB_UINT64 win_row_count) const
{
jrd_req* const request = tdbb->getRequest();
@ -414,7 +416,7 @@ AggregatedStream::State AggregatedStream::evaluateGroup(thread_db* tdbb, State s
const USHORT id = field->fieldId;
Record* record = request->req_rpb[field->fieldStream].rpb_record;
dsc* desc = aggNode->execute(tdbb, request);
dsc* desc = aggNode->execute(tdbb, request, win_row_count);
if (!desc || !desc->dsc_dtype)
record->setNull(id);
else

View File

@ -661,7 +661,7 @@ namespace Jrd
private:
void init(thread_db* tdbb, CompilerScratch* csb);
State evaluateGroup(thread_db* tdbb, State state) const;
State evaluateGroup(thread_db* tdbb, State state, FB_UINT64 win_row_count) const;
void cacheValues(thread_db* tdbb, jrd_req* request,
const NestValueArray* group, unsigned impureOffset) const;
bool lookForChange(thread_db* tdbb, jrd_req* request,

View File

@ -139,6 +139,7 @@ static const TOK tokens[] =
{CREATE, "CREATE", 1, false},
{CROSS, "CROSS", 2, false},
{CSTRING, "CSTRING", 1, false},
{CUME_DIST, "CUME_DIST", 2, false},
{CURRENT, "CURRENT", 1, false},
{CURRENT_CONNECTION, "CURRENT_CONNECTION", 2, false},
{CURRENT_DATE, "CURRENT_DATE", 2, false},
@ -280,6 +281,7 @@ static const TOK tokens[] =
{NO, "NO", 1, false},
{NOT, "NOT", 1, false},
{NTH_VALUE, "NTH_VALUE", 2, false},
{NTILE, "NTILE", 2, false},
{NULLIF, "NULLIF", 2, true},
{KW_NULL, "NULL", 1, false},
{NULLS, "NULLS", 2, true},
@ -307,6 +309,7 @@ static const TOK tokens[] =
{PARAMETER, "PARAMETER", 1, false},
{PARTITION, "PARTITION", 2, false},
{PASSWORD, "PASSWORD", 1, false},
{PERCENT_RANK, "PERCENT_RANK", 2, false},
{PI, "PI", 2, false},
{PLACING, "PLACING", 2, false},
{PLAN, "PLAN", 1, false},