From 8745489bb0909ac60f2296051a268ea876dcb6e9 Mon Sep 17 00:00:00 2001 From: Hajime Nakagami Date: Sat, 16 Apr 2016 02:32:35 +0900 Subject: [PATCH] Add window functions, PERCENT_RANK(), CUME_DIST() and NTILE(). --- .../README.window_functions.txt | 23 +- src/dsql/AggNodes.cpp | 29 +- src/dsql/AggNodes.h | 18 +- src/dsql/Nodes.h | 5 +- src/dsql/WinNodes.cpp | 302 +++++++++++++++++- src/dsql/WinNodes.h | 100 +++++- src/dsql/parse.y | 15 + src/jrd/recsrc/AggregatedStream.cpp | 12 +- src/jrd/recsrc/RecordSource.h | 2 +- src/yvalve/keywords.cpp | 3 + 10 files changed, 450 insertions(+), 59 deletions(-) diff --git a/doc/sql.extensions/README.window_functions.txt b/doc/sql.extensions/README.window_functions.txt index c9a893c51d..21370ff08f 100644 --- a/doc/sql.extensions/README.window_functions.txt +++ b/doc/sql.extensions/README.window_functions.txt @@ -144,6 +144,9 @@ Syntax: ::= DENSE_RANK() | RANK() | + PERCENT_RANK() | + CUME_DIST() | + NTILE() | 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 -------------------------- diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index b2487f5d01..7a41a6e847 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -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(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(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(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(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(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(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(impureOffset); StdDevImpure* impure2 = request->getImpure(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(impureOffset); CorrImpure* impure2 = request->getImpure(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(impureOffset); RegrImpure* impure2 = request->getImpure(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(impureOffset); diff --git a/src/dsql/AggNodes.h b/src/dsql/AggNodes.h index b42f7ae8ac..7e0c319a32 100644 --- a/src/dsql/AggNodes.h +++ b/src/dsql/AggNodes.h @@ -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*/; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index b7d153dcb3..e50d6a5b10 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -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); diff --git a/src/dsql/WinNodes.cpp b/src/dsql/WinNodes.cpp index 6cbe2b9d9e..27be4ad8c6 100644 --- a/src/dsql/WinNodes.cpp +++ b/src/dsql/WinNodes.cpp @@ -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(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(impureOffset); @@ -192,6 +192,171 @@ AggNode* RankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/ //-------------------- +static WinFuncNode::RegisterFactory0 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(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(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(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(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 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(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(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(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(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 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(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(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(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(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 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(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(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(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 diff --git a/src/dsql/WinNodes.h b/src/dsql/WinNodes.h index 2a7100e123..ac77d5afb5 100644 --- a/src/dsql/WinNodes.h +++ b/src/dsql/WinNodes.h @@ -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 diff --git a/src/dsql/parse.y b/src/dsql/parse.y index c9a9aa8481..a21c24dd9c 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -589,6 +589,12 @@ using namespace Firebird; %token REGR_SXY %token REGR_SYY +// tokens added for Firebird 4.0 + +%token PERCENT_RANK +%token CUME_DIST +%token NTILE + // precedence declarations for expression evaluation %left OR @@ -6945,6 +6951,10 @@ window_function { $$ = newNode(); } | RANK '(' ')' { $$ = newNode(); } + | PERCENT_RANK '(' ')' + { $$ = newNode(); } + | CUME_DIST '(' ')' + { $$ = newNode(); } | ROW_NUMBER '(' ')' { $$ = newNode(); } | FIRST_VALUE '(' value ')' @@ -6965,6 +6975,8 @@ window_function { $$ = newNode($3, $5, newNode()); } | LEAD '(' value ')' { $$ = newNode($3, MAKE_const_slong(1), newNode()); } + | NTILE '(' u_numeric_constant ')' + { $$ = newNode($3); } ; %type nth_from @@ -7774,7 +7786,10 @@ non_reserved_word | LAST_VALUE | LAG | LEAD + | NTILE | RANK + | PERCENT_RANK + | CUME_DIST | ROW_NUMBER | USAGE | LINGER diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index a69c0fd6ee..89e7f6bdbe 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -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 diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 2f567cd5cd..2a5713cf31 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -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, diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index 474ea391ad..970535bfc6 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -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},