mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 08:40:39 +01:00
Feature CORE-3647 - Window Function: frame (rows / range) clause. Readme to be done.
Also updated README.keywords of others features.
This commit is contained in:
parent
68bbdc1802
commit
41d24deb26
@ -285,3 +285,27 @@ Firebird 3.0
|
||||
TAGS *
|
||||
TRUSTED *
|
||||
USAGE
|
||||
|
||||
|
||||
Firebird 4.0
|
||||
------------
|
||||
|
||||
Added as reserved words:
|
||||
|
||||
UNBOUNDED
|
||||
|
||||
Added as non-reserved words:
|
||||
|
||||
CUME_DIST *
|
||||
EXCLUDE
|
||||
FOLLOWING
|
||||
NTILE *
|
||||
OTHERS
|
||||
PERCENT_RANK *
|
||||
PRECEDING
|
||||
PRIVILEGE *
|
||||
RANGE *
|
||||
RDB$ROLE_IN_USE *
|
||||
RDB$SYSTEM_PRIVILEGE *
|
||||
SYSTEM *
|
||||
TIES
|
||||
|
@ -1642,6 +1642,16 @@ C --
|
||||
PARAMETER (GDS__not_dba = 335545114)
|
||||
INTEGER*4 GDS__no_cursor
|
||||
PARAMETER (GDS__no_cursor = 335545115)
|
||||
INTEGER*4 GDS__dsql_window_incompat_frames
|
||||
PARAMETER (GDS__dsql_window_incompat_frames = 335545116)
|
||||
INTEGER*4 GDS__dsql_window_range_multi_key
|
||||
PARAMETER (GDS__dsql_window_range_multi_key = 335545117)
|
||||
INTEGER*4 GDS__dsql_window_range_inv_key_type
|
||||
PARAMETER (GDS__dsql_window_range_inv_key_type = 335545118)
|
||||
INTEGER*4 GDS__dsql_window_frame_value_inv_type
|
||||
PARAMETER (GDS__dsql_window_frame_value_inv_type = 335545119)
|
||||
INTEGER*4 GDS__window_frame_value_invalid
|
||||
PARAMETER (GDS__window_frame_value_invalid = 335545120)
|
||||
INTEGER*4 GDS__gfix_db_name
|
||||
PARAMETER (GDS__gfix_db_name = 335740929)
|
||||
INTEGER*4 GDS__gfix_invalid_sw
|
||||
|
@ -1637,6 +1637,16 @@ const
|
||||
gds_not_dba = 335545114;
|
||||
isc_no_cursor = 335545115;
|
||||
gds_no_cursor = 335545115;
|
||||
isc_dsql_window_incompat_frames = 335545116;
|
||||
gds_dsql_window_incompat_frames = 335545116;
|
||||
isc_dsql_window_range_multi_key = 335545117;
|
||||
gds_dsql_window_range_multi_key = 335545117;
|
||||
isc_dsql_window_range_inv_key_type = 335545118;
|
||||
gds_dsql_window_range_inv_key_type = 335545118;
|
||||
isc_dsql_window_frame_value_inv_type = 335545119;
|
||||
gds_dsql_window_frame_value_inv_type = 335545119;
|
||||
isc_window_frame_value_invalid = 335545120;
|
||||
gds_window_frame_value_invalid = 335545120;
|
||||
isc_gfix_db_name = 335740929;
|
||||
gds_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
|
@ -69,7 +69,7 @@ inline bool DTYPE_IS_APPROX(UCHAR d)
|
||||
|
||||
inline bool DTYPE_IS_NUMERIC(UCHAR d)
|
||||
{
|
||||
return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64;
|
||||
return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64;
|
||||
}
|
||||
|
||||
// Descriptor format
|
||||
@ -135,6 +135,11 @@ typedef struct dsc
|
||||
return dsc_dtype == dtype_int64 || dsc_dtype == dtype_long || dsc_dtype == dtype_short;
|
||||
}
|
||||
|
||||
bool isNumeric() const
|
||||
{
|
||||
return (dsc_dtype >= dtype_byte && dsc_dtype <= dtype_d_float) || dsc_dtype == dtype_int64;
|
||||
}
|
||||
|
||||
bool isText() const
|
||||
{
|
||||
return dsc_dtype >= dtype_text && dsc_dtype <= dtype_varying;
|
||||
|
@ -60,8 +60,7 @@ AggNode::AggNode(MemoryPool& pool, const AggInfo& aAggInfo, bool aDistinct, bool
|
||||
dialect1(aDialect1),
|
||||
arg(aArg),
|
||||
asb(NULL),
|
||||
indexed(false),
|
||||
ordered(false)
|
||||
indexed(false)
|
||||
{
|
||||
addChildNode(arg, arg);
|
||||
}
|
||||
@ -270,7 +269,7 @@ ValueExprNode* AggNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (!visitor.window && visitor.dsqlScratch->scopeLevel == aggFinder.deepestLevel)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.orderNode);
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,14 +336,10 @@ AggNode* AggNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void AggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (aggType == AGG_TYPE_GROUP)
|
||||
impure->vlux_count = 0;
|
||||
|
||||
impure->aggType = aggType;
|
||||
impure->vlux_count = 0;
|
||||
|
||||
if (distinct)
|
||||
{
|
||||
@ -627,12 +622,9 @@ string AvgAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "AvgAggNode";
|
||||
}
|
||||
|
||||
void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
@ -652,10 +644,6 @@ void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) con
|
||||
void AvgAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
if (dialect1)
|
||||
@ -760,12 +748,9 @@ string ListAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "ListAggNode";
|
||||
}
|
||||
|
||||
void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
// We don't know here what should be the sub-type and text-type.
|
||||
// Defer blob creation for when first record is found.
|
||||
@ -778,9 +763,6 @@ void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
if (!impure->vlu_blob)
|
||||
{
|
||||
impure->vlu_blob = blb::create(tdbb, request->req_transaction,
|
||||
@ -918,12 +900,9 @@ string CountAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "CountAggNode";
|
||||
}
|
||||
|
||||
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0);
|
||||
@ -933,9 +912,6 @@ void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/)
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
if (dialect1)
|
||||
++impure->vlu_misc.vlu_long;
|
||||
else
|
||||
@ -1152,12 +1128,9 @@ string SumAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "SumAggNode";
|
||||
}
|
||||
|
||||
void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
@ -1174,10 +1147,6 @@ void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) con
|
||||
void SumAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
if (dialect1)
|
||||
@ -1252,12 +1221,9 @@ string MaxMinAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "MaxMinAggNode";
|
||||
}
|
||||
|
||||
void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->vlu_desc.dsc_dtype = 0;
|
||||
@ -1266,11 +1232,8 @@ void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType)
|
||||
void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
if (!impure->vlu_desc.dsc_dtype)
|
||||
{
|
||||
EVL_make_value(tdbb, desc, impure);
|
||||
@ -1366,12 +1329,9 @@ string StdDevAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "StdDevAggNode";
|
||||
}
|
||||
|
||||
void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_double(0);
|
||||
@ -1383,10 +1343,6 @@ void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType)
|
||||
void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
const double d = MOV_get_double(desc);
|
||||
@ -1510,12 +1466,9 @@ string CorrAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "CorrAggNode";
|
||||
}
|
||||
|
||||
void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_double(0);
|
||||
@ -1528,9 +1481,6 @@ bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return true;
|
||||
|
||||
dsc* desc = NULL;
|
||||
dsc* desc2 = NULL;
|
||||
|
||||
@ -1702,12 +1652,9 @@ string RegrAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "RegrAggNode";
|
||||
}
|
||||
|
||||
void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_double(0);
|
||||
@ -1720,9 +1667,6 @@ bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return true;
|
||||
|
||||
dsc* desc = NULL;
|
||||
dsc* desc2 = NULL;
|
||||
|
||||
@ -1882,12 +1826,9 @@ string RegrCountAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "RegrCountAggNode";
|
||||
}
|
||||
|
||||
void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0);
|
||||
@ -1897,9 +1838,6 @@ bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return true;
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, arg);
|
||||
if (request->req_flags & req_null)
|
||||
return false;
|
||||
|
@ -37,13 +37,18 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
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, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -62,6 +67,11 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual bool setParameterType(DsqlCompilerScratch* dsqlScratch,
|
||||
@ -69,15 +79,7 @@ public:
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void checkOrderedWindowCapable() const
|
||||
{
|
||||
Firebird::status_exception::raise(
|
||||
Firebird::Arg::Gds(isc_wish_list) <<
|
||||
Firebird::Arg::Gds(isc_random) <<
|
||||
"LIST is not supported in ordered windows");
|
||||
}
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -95,13 +97,18 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -116,12 +123,17 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -142,12 +154,17 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -176,6 +193,11 @@ public:
|
||||
|
||||
explicit StdDevAggNode(MemoryPool& pool, StdDevType aType, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -184,7 +206,7 @@ public:
|
||||
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, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -216,6 +238,11 @@ public:
|
||||
explicit CorrAggNode(MemoryPool& pool, CorrType aType,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -224,7 +251,7 @@ public:
|
||||
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, AggType aggType) const;
|
||||
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;
|
||||
@ -263,6 +290,11 @@ public:
|
||||
explicit RegrAggNode(MemoryPool& pool, RegrType aType,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -271,7 +303,7 @@ public:
|
||||
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, AggType aggType) const;
|
||||
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;
|
||||
@ -293,6 +325,11 @@ public:
|
||||
explicit RegrCountAggNode(MemoryPool& pool,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -300,7 +337,7 @@ public:
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
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;
|
||||
|
@ -5273,7 +5273,7 @@ ValueExprNode* FieldNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (dsqlContext->ctx_scope_level == visitor.context->ctx_scope_level)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this, visitor.context,
|
||||
visitor.partitionNode, visitor.orderNode);
|
||||
visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -6748,7 +6748,7 @@ ValueExprNode* DsqlMapNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (visitor.window && context->ctx_scope_level == visitor.context->ctx_scope_level)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.orderNode);
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -6943,7 +6943,7 @@ ValueExprNode* DerivedFieldNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (scope == visitor.context->ctx_scope_level)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.orderNode);
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
else if (visitor.context->ctx_scope_level < scope)
|
||||
doDsqlFieldRemapper(visitor, value);
|
||||
@ -7332,7 +7332,7 @@ dsc* NullNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
|
||||
|
||||
OrderNode::OrderNode(MemoryPool& pool, ValueExprNode* aValue)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_ORDER>(pool),
|
||||
: DsqlNode(pool),
|
||||
value(aValue),
|
||||
descending(false),
|
||||
nullsPlacement(NULLS_DEFAULT)
|
||||
@ -7373,16 +7373,163 @@ bool OrderNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
//--------------------
|
||||
|
||||
|
||||
bool WindowClause::Frame::sameAs(const ExprNode* other, bool ignoreStreams) const
|
||||
{
|
||||
if (!ExprNode::sameAs(other, ignoreStreams))
|
||||
return false;
|
||||
|
||||
const Frame* const otherNode = other->as<Frame>();
|
||||
fb_assert(otherNode);
|
||||
|
||||
return bound == otherNode->bound;
|
||||
}
|
||||
|
||||
WindowClause::Frame* WindowClause::Frame::pass1(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ListExprNode::pass1(tdbb, csb);
|
||||
return this;
|
||||
}
|
||||
|
||||
WindowClause::Frame* WindowClause::Frame::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ListExprNode::pass2(tdbb, csb);
|
||||
return this;
|
||||
}
|
||||
|
||||
WindowClause::Frame* WindowClause::Frame::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
{
|
||||
Frame* node = FB_NEW_POOL(*tdbb->getDefaultPool()) Frame(*tdbb->getDefaultPool(), bound);
|
||||
node->value = copier.copy(tdbb, value);
|
||||
return node;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
|
||||
bool WindowClause::FrameExtent::sameAs(const ExprNode* other, bool ignoreStreams) const
|
||||
{
|
||||
if (!ExprNode::sameAs(other, ignoreStreams))
|
||||
return false;
|
||||
|
||||
const FrameExtent* const otherNode = other->as<FrameExtent>();
|
||||
fb_assert(otherNode);
|
||||
|
||||
return unit == otherNode->unit;
|
||||
}
|
||||
|
||||
WindowClause::FrameExtent* WindowClause::FrameExtent::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
if (frame1 && frame2)
|
||||
{
|
||||
if (frame1->bound == Frame::BOUND_CURRENT_ROW && frame2->bound == Frame::BOUND_PRECEDING)
|
||||
{
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_dsql_window_incompat_frames) << "CURRENT ROW" << "PRECEDING");
|
||||
}
|
||||
|
||||
if (frame1->bound == Frame::BOUND_FOLLOWING && frame2->bound != Frame::BOUND_FOLLOWING)
|
||||
{
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_dsql_window_incompat_frames) <<
|
||||
"FOLLOWING" << "PRECEDING or CURRENT ROW");
|
||||
}
|
||||
}
|
||||
|
||||
return FB_NEW_POOL(getPool()) FrameExtent(getPool(), unit,
|
||||
doDsqlPass(dsqlScratch, frame1),
|
||||
doDsqlPass(dsqlScratch, frame2));
|
||||
}
|
||||
|
||||
WindowClause::FrameExtent* WindowClause::FrameExtent::pass1(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ListExprNode::pass1(tdbb, csb);
|
||||
return this;
|
||||
}
|
||||
|
||||
WindowClause::FrameExtent* WindowClause::FrameExtent::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ListExprNode::pass2(tdbb, csb);
|
||||
return this;
|
||||
}
|
||||
|
||||
WindowClause::FrameExtent* WindowClause::FrameExtent::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
{
|
||||
FrameExtent* node = FB_NEW_POOL(*tdbb->getDefaultPool()) FrameExtent(
|
||||
*tdbb->getDefaultPool(), unit);
|
||||
node->frame1 = copier.copy(tdbb, frame1);
|
||||
node->frame2 = copier.copy(tdbb, frame2);
|
||||
return node;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
|
||||
WindowClause* WindowClause::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
WindowClause* node = FB_NEW_POOL(getPool()) WindowClause(getPool(),
|
||||
doDsqlPass(dsqlScratch, order),
|
||||
doDsqlPass(dsqlScratch, extent),
|
||||
exclusion);
|
||||
|
||||
if (node->order && node->extent && node->extent->unit == FrameExtent::UNIT_RANGE &&
|
||||
(node->extent->frame1->value || (node->extent->frame2 && node->extent->frame2->value)))
|
||||
{
|
||||
if (node->order->items.getCount() > 1)
|
||||
{
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_dsql_window_range_multi_key));
|
||||
}
|
||||
else
|
||||
{
|
||||
OrderNode* key = node->order->items[0]->as<OrderNode>();
|
||||
fb_assert(key);
|
||||
|
||||
dsc desc;
|
||||
MAKE_desc(dsqlScratch, &desc, key->value);
|
||||
|
||||
if (!desc.isDateTime() && !desc.isNumeric())
|
||||
{
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_dsql_window_range_inv_key_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->extent)
|
||||
{
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
WindowClause::Frame* frame = i == 0 ? node->extent->frame1 : node->extent->frame2;
|
||||
|
||||
if (frame && frame->value)
|
||||
{
|
||||
dsc desc;
|
||||
MAKE_desc(dsqlScratch, &desc, frame->value);
|
||||
|
||||
if (!desc.isNumeric())
|
||||
{
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_dsql_window_frame_value_inv_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
//--------------------
|
||||
|
||||
|
||||
OverNode::OverNode(MemoryPool& pool, AggNode* aAggExpr, ValueListNode* aPartition,
|
||||
ValueListNode* aOrder)
|
||||
WindowClause* aWindow)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_OVER>(pool),
|
||||
aggExpr(aAggExpr),
|
||||
partition(aPartition),
|
||||
order(aOrder)
|
||||
window(aWindow)
|
||||
{
|
||||
addDsqlChildNode(aggExpr);
|
||||
addDsqlChildNode(partition);
|
||||
addDsqlChildNode(order);
|
||||
addDsqlChildNode(window);
|
||||
}
|
||||
|
||||
string OverNode::internalPrint(NodePrinter& printer) const
|
||||
@ -7391,7 +7538,7 @@ string OverNode::internalPrint(NodePrinter& printer) const
|
||||
|
||||
NODE_PRINT(printer, aggExpr);
|
||||
NODE_PRINT(printer, partition);
|
||||
NODE_PRINT(printer, order);
|
||||
NODE_PRINT(printer, window);
|
||||
|
||||
return "OverNode";
|
||||
}
|
||||
@ -7412,7 +7559,7 @@ bool OverNode::dsqlAggregateFinder(AggregateFinder& visitor)
|
||||
aggregate |= visitor.visit(aggExpr);
|
||||
|
||||
aggregate |= visitor.visit(partition);
|
||||
aggregate |= visitor.visit(order);
|
||||
aggregate |= visitor.visit(window);
|
||||
|
||||
return aggregate;
|
||||
}
|
||||
@ -7427,7 +7574,7 @@ bool OverNode::dsqlAggregate2Finder(Aggregate2Finder& visitor)
|
||||
}
|
||||
|
||||
found |= visitor.visit(partition);
|
||||
found |= visitor.visit(order);
|
||||
found |= visitor.visit(window);
|
||||
|
||||
return found;
|
||||
}
|
||||
@ -7441,7 +7588,7 @@ bool OverNode::dsqlInvalidReferenceFinder(InvalidReferenceFinder& visitor)
|
||||
|
||||
invalid |= visitor.visit(aggExpr);
|
||||
invalid |= visitor.visit(partition);
|
||||
invalid |= visitor.visit(order);
|
||||
invalid |= visitor.visit(window);
|
||||
|
||||
return invalid;
|
||||
}
|
||||
@ -7455,7 +7602,7 @@ ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
// Save the values to restore them in the end.
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode(&visitor.partitionNode, visitor.partitionNode);
|
||||
AutoSetRestore<ValueListNode*> autoOrderNode(&visitor.orderNode, visitor.orderNode);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode(&visitor.windowNode, visitor.windowNode);
|
||||
|
||||
if (partition)
|
||||
{
|
||||
@ -7469,16 +7616,16 @@ ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
visitor.partitionNode = partition;
|
||||
}
|
||||
|
||||
if (order)
|
||||
if (window)
|
||||
{
|
||||
if (Aggregate2Finder::find(visitor.context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL,
|
||||
true, order))
|
||||
true, window))
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||
Arg::Gds(isc_dsql_agg_nested_err));
|
||||
}
|
||||
|
||||
visitor.orderNode = order;
|
||||
visitor.windowNode = window;
|
||||
}
|
||||
|
||||
// Before remap, aggExpr must always be an AggNode;
|
||||
@ -7506,7 +7653,7 @@ ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
{ // scope
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode2(&visitor.partitionNode, NULL);
|
||||
AutoSetRestore<ValueListNode*> autoOrderNode2(&visitor.orderNode, NULL);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode2(&visitor.windowNode, NULL);
|
||||
|
||||
Array<NodeRef*>& exprChildren = aggNode->dsqlChildNodes;
|
||||
for (NodeRef** i = exprChildren.begin(); i != exprChildren.end(); ++i)
|
||||
@ -7518,27 +7665,29 @@ ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
for (unsigned i = 0; i < partition->items.getCount(); ++i)
|
||||
{
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode2(&visitor.partitionNode, NULL);
|
||||
AutoSetRestore<ValueListNode*> autoOrderNode2(&visitor.orderNode, NULL);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode2(&visitor.windowNode, NULL);
|
||||
|
||||
doDsqlFieldRemapper(visitor, partition->items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (order)
|
||||
// ASF: I'm not sure if this is enough or frame values needs to be remapped as well.
|
||||
if (window && window->order)
|
||||
{
|
||||
for (unsigned i = 0; i < order->items.getCount(); ++i)
|
||||
for (unsigned i = 0; i < window->order->items.getCount(); ++i)
|
||||
{
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode(&visitor.partitionNode, NULL);
|
||||
AutoSetRestore<ValueListNode*> autoOrderNode(&visitor.orderNode, NULL);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode2(&visitor.windowNode, NULL);
|
||||
|
||||
doDsqlFieldRemapper(visitor, order->items[i]);
|
||||
doDsqlFieldRemapper(visitor, window->order->items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (visitor.dsqlScratch->scopeLevel == aggFinder.deepestLevel)
|
||||
{
|
||||
|
||||
return PASS1_post_map(visitor.dsqlScratch, aggNode, visitor.context,
|
||||
visitor.partitionNode, visitor.orderNode);
|
||||
visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7580,10 +7729,24 @@ dsc* OverNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
|
||||
ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
return FB_NEW_POOL(getPool()) OverNode(getPool(),
|
||||
OverNode* node = FB_NEW_POOL(getPool()) OverNode(getPool(),
|
||||
static_cast<AggNode*>(doDsqlPass(dsqlScratch, aggExpr)),
|
||||
doDsqlPass(dsqlScratch, partition),
|
||||
doDsqlPass(dsqlScratch, order));
|
||||
doDsqlPass(dsqlScratch, window));
|
||||
|
||||
const AggNode* aggNode = node->aggExpr->as<AggNode>();
|
||||
|
||||
if (window &&
|
||||
window->extent &&
|
||||
aggNode &&
|
||||
(aggNode->getCapabilities() & AggNode::CAP_RESPECTS_WINDOW_FRAME) !=
|
||||
AggNode::CAP_RESPECTS_WINDOW_FRAME)
|
||||
{
|
||||
node->window->extent = WindowClause::FrameExtent::createDefault(getPool());
|
||||
node->window->exclusion = WindowClause::EXCLUDE_NO_OTHERS;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
@ -8148,7 +8311,7 @@ bool RecordKeyNode::dsqlFieldFinder(FieldFinder& /*visitor*/)
|
||||
ValueExprNode* RecordKeyNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.orderNode);
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
|
||||
void RecordKeyNode::setParameterName(dsql_par* parameter) const
|
||||
|
@ -951,7 +951,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class OrderNode : public TypedNode<ValueExprNode, ExprNode::TYPE_ORDER>
|
||||
class OrderNode : public DsqlNode<OrderNode, ExprNode::TYPE_ORDER>
|
||||
{
|
||||
public:
|
||||
enum NullsPlacement
|
||||
@ -967,38 +967,6 @@ public:
|
||||
virtual OrderNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const;
|
||||
|
||||
virtual void setParameterName(dsql_par* /*parameter*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual ValueExprNode* copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
NestConst<ValueExprNode> value;
|
||||
bool descending;
|
||||
@ -1006,13 +974,227 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class WindowClause : public DsqlNode<WindowClause, ExprNode::TYPE_WINDOW_CLAUSE>
|
||||
{
|
||||
public:
|
||||
// ListExprNode has no relation with this but works perfectly here for now.
|
||||
|
||||
class Frame : public TypedNode<ListExprNode, ExprNode::TYPE_WINDOW_CLAUSE_FRAME>
|
||||
{
|
||||
public:
|
||||
enum Bound
|
||||
{
|
||||
// Warning: used in BLR
|
||||
BOUND_PRECEDING = 0,
|
||||
BOUND_FOLLOWING,
|
||||
BOUND_CURRENT_ROW
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Frame(MemoryPool& p, Bound aBound, ValueExprNode* aValue = NULL)
|
||||
: TypedNode(p),
|
||||
bound(aBound),
|
||||
value(aValue)
|
||||
{
|
||||
addChildNode(value, value);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, bound);
|
||||
NODE_PRINT(printer, value);
|
||||
|
||||
return "WindowClause::Frame";
|
||||
}
|
||||
|
||||
virtual Frame* dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
Frame* node = FB_NEW_POOL(getPool()) Frame(getPool(), bound,
|
||||
doDsqlPass(dsqlScratch, value));
|
||||
|
||||
if (node->value)
|
||||
{
|
||||
dsc desc;
|
||||
desc.makeLong(0);
|
||||
|
||||
node->value->setParameterType(dsqlScratch, &desc, false);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ListExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const Frame* o = other->as<Frame>();
|
||||
fb_assert(o);
|
||||
|
||||
return bound == o->bound;
|
||||
}
|
||||
|
||||
virtual Frame* dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
ListExprNode::dsqlFieldRemapper(visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const;
|
||||
virtual Frame* pass1(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual Frame* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual Frame* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
public:
|
||||
Bound bound;
|
||||
NestConst<ValueExprNode> value;
|
||||
};
|
||||
|
||||
class FrameExtent : public TypedNode<ListExprNode, ExprNode::TYPE_WINDOW_CLAUSE_FRAME_EXTENT>
|
||||
{
|
||||
public:
|
||||
enum Unit
|
||||
{
|
||||
// Warning: used in BLR
|
||||
UNIT_RANGE = 0,
|
||||
UNIT_ROWS
|
||||
//// TODO: SQL-2013: GROUPS
|
||||
};
|
||||
|
||||
public:
|
||||
explicit FrameExtent(MemoryPool& p, Unit aUnit, Frame* aFrame1 = NULL, Frame* aFrame2 = NULL)
|
||||
: TypedNode(p),
|
||||
unit(aUnit),
|
||||
frame1(aFrame1),
|
||||
frame2(aFrame2)
|
||||
{
|
||||
addChildNode(frame1, frame1);
|
||||
addChildNode(frame2, frame2);
|
||||
}
|
||||
|
||||
static FrameExtent* createDefault(MemoryPool& p)
|
||||
{
|
||||
FrameExtent* frameExtent = FB_NEW_POOL(p) WindowClause::FrameExtent(p, UNIT_RANGE);
|
||||
frameExtent->frame1 = FB_NEW_POOL(p) WindowClause::Frame(p, Frame::BOUND_PRECEDING);
|
||||
frameExtent->frame2 = FB_NEW_POOL(p) WindowClause::Frame(p, Frame::BOUND_CURRENT_ROW);
|
||||
return frameExtent;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, unit);
|
||||
NODE_PRINT(printer, frame1);
|
||||
NODE_PRINT(printer, frame2);
|
||||
|
||||
return "WindowClause::FrameExtent";
|
||||
}
|
||||
|
||||
virtual FrameExtent* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
|
||||
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ListExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const FrameExtent* o = other->as<FrameExtent>();
|
||||
fb_assert(o);
|
||||
|
||||
return unit == o->unit;
|
||||
}
|
||||
|
||||
virtual FrameExtent* dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
ListExprNode::dsqlFieldRemapper(visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const;
|
||||
virtual FrameExtent* pass1(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual FrameExtent* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual FrameExtent* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
public:
|
||||
Unit unit;
|
||||
NestConst<Frame> frame1;
|
||||
NestConst<Frame> frame2;
|
||||
};
|
||||
|
||||
enum Exclusion
|
||||
{
|
||||
// Warning: used in BLR
|
||||
EXCLUDE_NO_OTHERS = 0,
|
||||
EXCLUDE_CURRENT_ROW,
|
||||
EXCLUDE_GROUP,
|
||||
EXCLUDE_TIES
|
||||
};
|
||||
|
||||
public:
|
||||
explicit WindowClause(MemoryPool& p, ValueListNode* aOrder, FrameExtent* aFrameExtent,
|
||||
Exclusion aExclusion)
|
||||
: DsqlNode(p),
|
||||
order(aOrder),
|
||||
extent(aFrameExtent),
|
||||
exclusion(aExclusion)
|
||||
{
|
||||
addDsqlChildNode(order);
|
||||
addDsqlChildNode(extent);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, order);
|
||||
NODE_PRINT(printer, extent);
|
||||
|
||||
return "WindowClause";
|
||||
}
|
||||
|
||||
virtual WindowClause* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
|
||||
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!DsqlNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const WindowClause* o = other->as<WindowClause>();
|
||||
fb_assert(o);
|
||||
|
||||
return exclusion == o->exclusion;
|
||||
}
|
||||
|
||||
virtual WindowClause* dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
DsqlNode::dsqlFieldRemapper(visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual WindowClause* pass1(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
fb_assert(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual WindowClause* pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
fb_assert(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public:
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<FrameExtent> extent;
|
||||
Exclusion exclusion;
|
||||
};
|
||||
|
||||
// OVER is used only in DSQL. In the engine, normal aggregate functions are used in partitioned
|
||||
// maps.
|
||||
class OverNode : public TypedNode<ValueExprNode, ExprNode::TYPE_OVER>
|
||||
{
|
||||
public:
|
||||
explicit OverNode(MemoryPool& pool, AggNode* aAggExpr = NULL, ValueListNode* aPartition = NULL,
|
||||
ValueListNode* aOrder = NULL);
|
||||
WindowClause* aWindow = NULL);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
@ -1034,7 +1216,7 @@ public:
|
||||
public:
|
||||
NestConst<ValueExprNode> aggExpr;
|
||||
NestConst<ValueListNode> partition;
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<WindowClause> window;
|
||||
};
|
||||
|
||||
|
||||
|
@ -434,6 +434,9 @@ public:
|
||||
TYPE_UDF_CALL,
|
||||
TYPE_VALUE_IF,
|
||||
TYPE_VARIABLE,
|
||||
TYPE_WINDOW_CLAUSE,
|
||||
TYPE_WINDOW_CLAUSE_FRAME,
|
||||
TYPE_WINDOW_CLAUSE_FRAME_EXTENT,
|
||||
|
||||
// Bool types
|
||||
TYPE_BINARY_BOOL,
|
||||
@ -820,8 +823,74 @@ public:
|
||||
dsc nodDesc;
|
||||
};
|
||||
|
||||
template <typename T, typename ValueExprNode::Type typeConst>
|
||||
class DsqlNode : public TypedNode<ValueExprNode, typeConst>
|
||||
{
|
||||
public:
|
||||
DsqlNode(MemoryPool& pool)
|
||||
: TypedNode<ValueExprNode, typeConst>(pool)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void setParameterName(dsql_par* /*parameter*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual DsqlNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual DsqlNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual ValueExprNode* copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
class AggNode : public TypedNode<ValueExprNode, ExprNode::TYPE_AGGREGATE>
|
||||
{
|
||||
public:
|
||||
// Capabilities
|
||||
// works in a window frame
|
||||
static const unsigned CAP_SUPPORTS_WINDOW_FRAME = 0x01;
|
||||
// respects window frame boundaries
|
||||
static const unsigned CAP_RESPECTS_WINDOW_FRAME = 0x02 | CAP_SUPPORTS_WINDOW_FRAME;
|
||||
// wants aggPass/aggExecute calls in a window
|
||||
static const unsigned CAP_WANTS_AGG_CALLS = 0x04;
|
||||
// wants winPass call in a window
|
||||
static const unsigned CAP_WANTS_WIN_PASS_CALL = 0x08;
|
||||
|
||||
protected:
|
||||
struct AggInfo
|
||||
{
|
||||
@ -913,6 +982,7 @@ public:
|
||||
RegisterNode<T> registerNode1, registerNode2;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit AggNode(MemoryPool& pool, const AggInfo& aAggInfo, bool aDistinct, bool aDialect1,
|
||||
ValueExprNode* aArg = NULL);
|
||||
|
||||
@ -955,31 +1025,17 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void checkOrderedWindowCapable() const
|
||||
{
|
||||
if (distinct)
|
||||
{
|
||||
Firebird::status_exception::raise(
|
||||
Firebird::Arg::Gds(isc_wish_list) <<
|
||||
Firebird::Arg::Gds(isc_random) <<
|
||||
"DISTINCT is not supported in ordered windows");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
}
|
||||
|
||||
virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const = 0; // pure, but defined
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const = 0; // pure, but defined
|
||||
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 unsigned getCapabilities() const = 0;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const = 0;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const = 0;
|
||||
|
||||
@ -1000,7 +1056,6 @@ public:
|
||||
NestConst<ValueExprNode> arg;
|
||||
const AggregateSort* asb;
|
||||
bool indexed;
|
||||
bool ordered;
|
||||
|
||||
private:
|
||||
static Factory* factories;
|
||||
@ -1012,6 +1067,16 @@ class WinFuncNode : public AggNode
|
||||
{
|
||||
public:
|
||||
explicit WinFuncNode(MemoryPool& pool, const AggInfo& aAggInfo, ValueExprNode* aArg = NULL);
|
||||
|
||||
public:
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
}
|
||||
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -151,13 +151,13 @@ class FieldRemapper
|
||||
{
|
||||
public:
|
||||
FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
||||
ValueListNode* aPartitionNode = NULL, ValueListNode* aOrderNode = NULL);
|
||||
ValueListNode* aPartitionNode = NULL, WindowClause* aWindowNode = NULL);
|
||||
|
||||
static ExprNode* remap(DsqlCompilerScratch* dsqlScratch, dsql_ctx* context, bool window,
|
||||
ExprNode* field, ValueListNode* partitionNode = NULL, ValueListNode* orderNode = NULL)
|
||||
ExprNode* field, ValueListNode* partitionNode = NULL, WindowClause* windowNode = NULL)
|
||||
{
|
||||
// The bool value returned by the visitor is completely discarded in this class.
|
||||
return FieldRemapper(dsqlScratch, context, window, partitionNode, orderNode).visit(field);
|
||||
return FieldRemapper(dsqlScratch, context, window, partitionNode, windowNode).visit(field);
|
||||
}
|
||||
|
||||
ExprNode* visit(ExprNode* node);
|
||||
@ -167,7 +167,7 @@ public:
|
||||
dsql_ctx* const context;
|
||||
const bool window;
|
||||
ValueListNode* partitionNode;
|
||||
ValueListNode* orderNode;
|
||||
WindowClause* windowNode;
|
||||
USHORT currentLevel;
|
||||
};
|
||||
|
||||
|
@ -79,12 +79,9 @@ ValueExprNode* DenseRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c
|
||||
return FB_NEW_POOL(*tdbb->getDefaultPool()) DenseRankWinNode(*tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
@ -97,10 +94,7 @@ void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
|
||||
dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlu_misc.vlu_int64;
|
||||
|
||||
++impure->vlu_misc.vlu_int64;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
@ -158,12 +152,9 @@ AggNode* RankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(1, 0);
|
||||
@ -173,9 +164,7 @@ void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) co
|
||||
void RankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlux_count;
|
||||
++impure->vlux_count;
|
||||
}
|
||||
|
||||
dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
@ -206,7 +195,8 @@ AggNode* RankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
|
||||
static WinFuncNode::RegisterFactory0<PercentRankWinNode> percentRankWinInfo("PERCENT_RANK");
|
||||
|
||||
PercentRankWinNode::PercentRankWinNode(MemoryPool& pool)
|
||||
: WinFuncNode(pool, percentRankWinInfo)
|
||||
: WinFuncNode(pool, percentRankWinInfo),
|
||||
tempImpure(0)
|
||||
{
|
||||
fb_assert(dsqlChildNodes.getCount() == 1 && jrdChildNodes.getCount() == 1);
|
||||
dsqlChildNodes.clear();
|
||||
@ -237,44 +227,51 @@ ValueExprNode* PercentRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/)
|
||||
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, AggType aggType) const
|
||||
void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
impure->make_int64(1, 0);
|
||||
impure->vlux_count = 0;
|
||||
|
||||
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
|
||||
impureTemp->make_double(0);
|
||||
impureTemp->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);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlux_count;
|
||||
++impure->vlux_count;
|
||||
}
|
||||
|
||||
dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
|
||||
|
||||
impureTemp->vlux_count = impure->vlu_misc.vlu_int64;
|
||||
|
||||
impure->vlu_misc.vlu_int64 += impure->vlux_count;
|
||||
impure->vlux_count = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
|
||||
|
||||
double n = impure->vlux_count;
|
||||
double partitionSize = window->getPartitionSize();
|
||||
double orderSize = window->getOrderSize();
|
||||
|
||||
impure->make_double(n == 1 ? 0 : 1 / (partitionSize - 1) * (n - orderSize));
|
||||
return &impure->vlu_desc;
|
||||
impureTemp->vlu_misc.vlu_double = 1 / (partitionSize - 1) * (impureTemp->vlux_count - 1);
|
||||
return &impureTemp->vlu_desc;
|
||||
}
|
||||
|
||||
AggNode* PercentRankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
|
||||
@ -323,39 +320,22 @@ AggNode* CumeDistWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 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);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlux_count;
|
||||
}
|
||||
|
||||
dsc* CumeDistWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
impure->make_double(0);
|
||||
}
|
||||
|
||||
dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
double n = impure->vlux_count;
|
||||
double frameSize = window->getFrameSize();
|
||||
double partitionSize = window->getPartitionSize();
|
||||
|
||||
impure->make_double(n / partitionSize);
|
||||
impure->vlu_misc.vlu_double = frameSize / partitionSize;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
@ -402,30 +382,18 @@ ValueExprNode* RowNumberWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c
|
||||
return FB_NEW_POOL(*tdbb->getDefaultPool()) RowNumberWinNode(*tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
}
|
||||
|
||||
void RowNumberWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* RowNumberWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* /*window*/) const
|
||||
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
++impure->vlu_misc.vlu_int64;
|
||||
impure->vlu_misc.vlu_int64 = window->getRecordPosition() - window->getPartitionStart() + 1;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
@ -474,36 +442,17 @@ ValueExprNode* FirstValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) cons
|
||||
return node;
|
||||
}
|
||||
|
||||
void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
}
|
||||
|
||||
void FirstValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* FirstValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
}
|
||||
|
||||
dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
SINT64 records = impure->vlu_misc.vlu_int64++;
|
||||
|
||||
if (!window->move(-records))
|
||||
{
|
||||
window->move(0); // Come back to our row.
|
||||
if (!window->moveWithinFrame(-(window->getRecordPosition() - window->getFrameStart())))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, arg);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
@ -557,23 +506,16 @@ ValueExprNode* LastValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
return node;
|
||||
}
|
||||
|
||||
void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
}
|
||||
|
||||
void LastValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* LastValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
}
|
||||
|
||||
dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
if (!window->move(0))
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!window->moveWithinFrame(window->getFrameEnd() - window->getRecordPosition()))
|
||||
return NULL;
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, arg);
|
||||
@ -641,32 +583,18 @@ ValueExprNode* NthValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
return node;
|
||||
}
|
||||
|
||||
void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
}
|
||||
|
||||
void NthValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* NthValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) 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, row);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
@ -682,20 +610,12 @@ dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow*
|
||||
const SLONG fromPos = desc ? MOV_get_long(desc, 0) : FROM_FIRST;
|
||||
|
||||
if (fromPos == FROM_FIRST)
|
||||
{
|
||||
if (records > ++impure->vlu_misc.vlu_int64)
|
||||
return NULL;
|
||||
|
||||
records -= impure->vlu_misc.vlu_int64;
|
||||
}
|
||||
records += -(window->getRecordPosition() - window->getFrameStart()) - 1;
|
||||
else
|
||||
records = impure->vlu_misc.vlu_int64 - records + 1;
|
||||
records = window->getFrameEnd() - window->getRecordPosition() - records + 1;
|
||||
|
||||
if (!window->move(records))
|
||||
{
|
||||
window->move(0); // Come back to our row.
|
||||
if (!window->moveWithinFrame(records))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = EVL_expr(tdbb, request, arg);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
@ -757,24 +677,13 @@ void LagLeadWinNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
|
||||
arg->getDesc(tdbb, csb, desc);
|
||||
}
|
||||
|
||||
void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
}
|
||||
|
||||
void LagLeadWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* LagLeadWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
}
|
||||
|
||||
dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
window->move(0); // Come back to our row because rows may reference columns.
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, rows);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
@ -786,10 +695,8 @@ dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* w
|
||||
Arg::Num(2) << Arg::Str(aggInfo.name));
|
||||
}
|
||||
|
||||
if (!window->move(records * direction))
|
||||
if (!window->moveWithinPartition(records * direction))
|
||||
{
|
||||
window->move(0); // Come back to our row because outExpr may reference columns.
|
||||
|
||||
desc = EVL_expr(tdbb, request, outExpr);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
@ -924,12 +831,9 @@ AggNode* NTileWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
ThisImpure* thisImpure = request->getImpure<ThisImpure>(thisImpureOffset);
|
||||
@ -956,15 +860,6 @@ void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) c
|
||||
}
|
||||
}
|
||||
|
||||
void NTileWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* NTileWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
@ -986,7 +881,7 @@ dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow*
|
||||
|
||||
++n;
|
||||
|
||||
impure->make_int64(result);
|
||||
impure->vlu_misc.vlu_int64 = result;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,17 @@ class DenseRankWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit DenseRankWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -55,13 +60,18 @@ class RankWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit RankWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
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, AggType aggType) const;
|
||||
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;
|
||||
|
||||
@ -78,25 +88,28 @@ class PercentRankWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit PercentRankWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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, AggType aggType) const;
|
||||
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 void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
protected:
|
||||
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
|
||||
|
||||
private:
|
||||
USHORT tempImpure;
|
||||
};
|
||||
|
||||
// CUME_DIST function.
|
||||
@ -105,20 +118,18 @@ class CumeDistWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit CumeDistWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -132,19 +143,17 @@ class RowNumberWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit RowNumberWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -158,19 +167,17 @@ class FirstValueWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit FirstValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -186,19 +193,17 @@ class LastValueWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit LastValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -222,19 +227,17 @@ public:
|
||||
explicit NthValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL,
|
||||
ValueExprNode* aRow = NULL, ValueExprNode* aFrom = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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 void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -255,19 +258,16 @@ public:
|
||||
explicit LagLeadWinNode(MemoryPool& pool, const AggInfo& aAggInfo, int aDirection,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, ValueExprNode* aOutExpr = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const = 0;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
protected:
|
||||
@ -323,20 +323,18 @@ class NTileWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit NTileWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
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, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
|
@ -82,6 +82,7 @@ namespace Jrd
|
||||
class TransactionNode;
|
||||
class ValueExprNode;
|
||||
class ValueListNode;
|
||||
class WindowClause;
|
||||
class jrd_tra;
|
||||
class jrd_req;
|
||||
class blb;
|
||||
@ -674,10 +675,10 @@ public:
|
||||
|
||||
struct PartitionMap
|
||||
{
|
||||
PartitionMap(ValueListNode* aPartition, ValueListNode* aOrder)
|
||||
PartitionMap(ValueListNode* aPartition, WindowClause* aWindow)
|
||||
: partition(aPartition),
|
||||
partitionRemapped(NULL),
|
||||
order(aOrder),
|
||||
window(aWindow),
|
||||
map(NULL),
|
||||
context(0)
|
||||
{
|
||||
@ -685,7 +686,7 @@ struct PartitionMap
|
||||
|
||||
NestConst<ValueListNode> partition;
|
||||
NestConst<ValueListNode> partitionRemapped;
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<WindowClause> window;
|
||||
dsql_map* map;
|
||||
USHORT context;
|
||||
};
|
||||
@ -756,7 +757,7 @@ public:
|
||||
|
||||
bool getImplicitJoinField(const Firebird::MetaName& name, NestConst<ValueExprNode>& node);
|
||||
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch,
|
||||
ValueListNode* partitionNode, ValueListNode* orderNode);
|
||||
ValueListNode* partitionNode, WindowClause* windowNode);
|
||||
};
|
||||
|
||||
// Flag values for ctx_flags
|
||||
|
@ -561,7 +561,7 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse)
|
||||
}
|
||||
|
||||
if (rse->dsqlOrder)
|
||||
GEN_sort(dsqlScratch, rse->dsqlOrder);
|
||||
GEN_sort(dsqlScratch, blr_sort, rse->dsqlOrder);
|
||||
|
||||
if (rse->dsqlDistinct)
|
||||
{
|
||||
@ -587,28 +587,32 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse)
|
||||
|
||||
|
||||
// Generate a sort clause.
|
||||
void GEN_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* list)
|
||||
void GEN_sort(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, ValueListNode* list)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_sort);
|
||||
dsqlScratch->appendUChar(list->items.getCount());
|
||||
dsqlScratch->appendUChar(blrVerb);
|
||||
dsqlScratch->appendUChar(list ? list->items.getCount() : 0);
|
||||
|
||||
NestConst<ValueExprNode>* ptr = list->items.begin();
|
||||
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
|
||||
if (list)
|
||||
{
|
||||
OrderNode* orderNode = (*ptr)->as<OrderNode>();
|
||||
NestConst<ValueExprNode>* ptr = list->items.begin();
|
||||
|
||||
switch (orderNode->nullsPlacement)
|
||||
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
|
||||
{
|
||||
case OrderNode::NULLS_FIRST:
|
||||
dsqlScratch->appendUChar(blr_nullsfirst);
|
||||
break;
|
||||
case OrderNode::NULLS_LAST:
|
||||
dsqlScratch->appendUChar(blr_nullslast);
|
||||
break;
|
||||
}
|
||||
OrderNode* orderNode = (*ptr)->as<OrderNode>();
|
||||
|
||||
dsqlScratch->appendUChar((orderNode->descending ? blr_descending : blr_ascending));
|
||||
GEN_expr(dsqlScratch, orderNode->value);
|
||||
switch (orderNode->nullsPlacement)
|
||||
{
|
||||
case OrderNode::NULLS_FIRST:
|
||||
dsqlScratch->appendUChar(blr_nullsfirst);
|
||||
break;
|
||||
case OrderNode::NULLS_LAST:
|
||||
dsqlScratch->appendUChar(blr_nullslast);
|
||||
break;
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar((orderNode->descending ? blr_descending : blr_ascending));
|
||||
GEN_expr(dsqlScratch, orderNode->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ void GEN_parameter(Jrd::DsqlCompilerScratch*, const Jrd::dsql_par*);
|
||||
void GEN_port(Jrd::DsqlCompilerScratch*, Jrd::dsql_msg*);
|
||||
void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*);
|
||||
void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*);
|
||||
void GEN_sort(Jrd::DsqlCompilerScratch*, Jrd::ValueListNode*);
|
||||
void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*);
|
||||
void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*);
|
||||
|
||||
#endif // DSQL_GEN_PROTO_H
|
||||
|
119
src/dsql/parse.y
119
src/dsql/parse.y
@ -592,12 +592,19 @@ using namespace Firebird;
|
||||
// tokens added for Firebird 4.0
|
||||
|
||||
%token <metaNamePtr> CUME_DIST
|
||||
%token <metaNamePtr> EXCLUDE
|
||||
%token <metaNamePtr> FOLLOWING
|
||||
%token <metaNamePtr> NTILE
|
||||
%token <metaNamePtr> OTHERS
|
||||
%token <metaNamePtr> PERCENT_RANK
|
||||
%token <metaNamePtr> PRECEDING
|
||||
%token <metaNamePtr> PRIVILEGE
|
||||
%token <metaNamePtr> RANGE
|
||||
%token <metaNamePtr> RDB_ROLE_IN_USE
|
||||
%token <metaNamePtr> RDB_SYSTEM_PRIVILEGE
|
||||
%token <metaNamePtr> SYSTEM
|
||||
%token <metaNamePtr> TIES
|
||||
%token <metaNamePtr> UNBOUNDED
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
|
||||
@ -655,6 +662,10 @@ using namespace Firebird;
|
||||
Firebird::Array<NestConst<Jrd::DbFileClause> >* dbFilesClause;
|
||||
Jrd::ExternalClause* externalClause;
|
||||
Firebird::Array<NestConst<Jrd::ParameterClause> >* parametersClause;
|
||||
Jrd::WindowClause* windowClause;
|
||||
Jrd::WindowClause::FrameExtent* windowClauseFrameExtent;
|
||||
Jrd::WindowClause::Frame* windowClauseFrame;
|
||||
Jrd::WindowClause::Exclusion windowClauseExclusion;
|
||||
Jrd::Node* node;
|
||||
Jrd::ExprNode* exprNode;
|
||||
Jrd::ValueExprNode* valueExprNode;
|
||||
@ -3959,6 +3970,7 @@ keyword_or_column
|
||||
| UPDATING
|
||||
| VAR_SAMP
|
||||
| VAR_POP
|
||||
| UNBOUNDED // added in FB 4.0
|
||||
;
|
||||
|
||||
col_opt
|
||||
@ -5058,7 +5070,7 @@ lock_clause
|
||||
|
||||
%type <selectExprNode> select_expr
|
||||
select_expr
|
||||
: with_clause select_expr_body order_clause rows_clause
|
||||
: with_clause select_expr_body order_clause_opt rows_clause
|
||||
{
|
||||
SelectExprNode* node = $$ = newNode<SelectExprNode>();
|
||||
node->querySpec = $2;
|
||||
@ -5066,7 +5078,7 @@ select_expr
|
||||
node->rowsClause = $4;
|
||||
node->withClause = $1;
|
||||
}
|
||||
| with_clause select_expr_body order_clause result_offset_clause fetch_first_clause
|
||||
| with_clause select_expr_body order_clause_opt result_offset_clause fetch_first_clause
|
||||
{
|
||||
SelectExprNode* node = $$ = newNode<SelectExprNode>();
|
||||
node->querySpec = $2;
|
||||
@ -5593,10 +5605,15 @@ extra_indices_opt($accessType)
|
||||
|
||||
// ORDER BY clause
|
||||
|
||||
%type <valueListNode> order_clause_opt
|
||||
order_clause_opt
|
||||
: /* nothing */ { $$ = NULL; }
|
||||
| order_clause
|
||||
;
|
||||
|
||||
%type <valueListNode> order_clause
|
||||
order_clause
|
||||
: /* nothing */ { $$ = NULL; }
|
||||
| ORDER BY order_list { $$ = $3; }
|
||||
: ORDER BY order_list { $$ = $3; }
|
||||
;
|
||||
|
||||
%type <valueListNode> order_list
|
||||
@ -5804,7 +5821,7 @@ delete
|
||||
|
||||
%type <stmtNode> delete_searched
|
||||
delete_searched
|
||||
: KW_DELETE FROM table_name where_clause plan_clause order_clause rows_clause_optional returning_clause
|
||||
: KW_DELETE FROM table_name where_clause plan_clause order_clause_opt rows_clause_optional returning_clause
|
||||
{
|
||||
EraseNode* node = newNode<EraseNode>();
|
||||
node->dsqlRelation = $3;
|
||||
@ -5841,7 +5858,7 @@ update
|
||||
%type <stmtNode> update_searched
|
||||
update_searched
|
||||
: UPDATE table_name SET assignments where_clause plan_clause
|
||||
order_clause rows_clause_optional returning_clause
|
||||
order_clause_opt rows_clause_optional returning_clause
|
||||
{
|
||||
ModifyNode* node = newNode<ModifyNode>();
|
||||
node->dsqlRelation = $2;
|
||||
@ -7053,7 +7070,7 @@ aggregate_window_function
|
||||
|
||||
%type <valueExprNode> over_clause
|
||||
over_clause
|
||||
: aggregate_window_function OVER '(' window_partition_opt order_clause ')'
|
||||
: aggregate_window_function OVER '(' window_partition_opt window_clause ')'
|
||||
{ $$ = newNode<OverNode>($1, $4, $5); }
|
||||
;
|
||||
|
||||
@ -7063,6 +7080,86 @@ window_partition_opt
|
||||
| PARTITION BY value_list { $$ = $3; }
|
||||
;
|
||||
|
||||
%type <windowClause> window_clause
|
||||
window_clause
|
||||
: /* nothing */
|
||||
{ $$ = NULL; }
|
||||
| order_clause window_frame_extent window_frame_exclusion_opt
|
||||
{ $$ = newNode<WindowClause>($1, $2, $3); }
|
||||
;
|
||||
|
||||
%type <windowClauseFrameExtent> window_frame_extent
|
||||
window_frame_extent
|
||||
: /* nothing */
|
||||
{ $$ = NULL; }
|
||||
| RANGE
|
||||
{ $$ = newNode<WindowClause::FrameExtent>(WindowClause::FrameExtent::UNIT_RANGE); }
|
||||
window_frame($2)
|
||||
{ $$ = $2; }
|
||||
| ROWS
|
||||
{ $$ = newNode<WindowClause::FrameExtent>(WindowClause::FrameExtent::UNIT_ROWS); }
|
||||
window_frame($2)
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
%type window_frame(<windowClauseFrameExtent>)
|
||||
window_frame($frameExtent)
|
||||
: window_frame_start
|
||||
{
|
||||
$frameExtent->frame1 = $1;
|
||||
$frameExtent->frame2 =
|
||||
newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_CURRENT_ROW);
|
||||
}
|
||||
| BETWEEN window_frame_between_bound1 AND window_frame_between_bound2
|
||||
{
|
||||
$frameExtent->frame1 = $2;
|
||||
$frameExtent->frame2 = $4;
|
||||
}
|
||||
;
|
||||
|
||||
%type <windowClauseFrame> window_frame_start
|
||||
window_frame_start
|
||||
: UNBOUNDED PRECEDING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_PRECEDING); }
|
||||
| CURRENT ROW
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_CURRENT_ROW); }
|
||||
| value PRECEDING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_PRECEDING, $1); }
|
||||
;
|
||||
|
||||
%type <windowClauseFrame> window_frame_between_bound1
|
||||
window_frame_between_bound1
|
||||
: UNBOUNDED PRECEDING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_PRECEDING); }
|
||||
| CURRENT ROW
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_CURRENT_ROW); }
|
||||
| value PRECEDING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_PRECEDING, $1); }
|
||||
| value FOLLOWING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_FOLLOWING, $1); }
|
||||
;
|
||||
|
||||
%type <windowClauseFrame> window_frame_between_bound2
|
||||
window_frame_between_bound2
|
||||
: UNBOUNDED FOLLOWING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_FOLLOWING); }
|
||||
| CURRENT ROW
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_CURRENT_ROW); }
|
||||
| value PRECEDING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_PRECEDING, $1); }
|
||||
| value FOLLOWING
|
||||
{ $$ = newNode<WindowClause::Frame>(WindowClause::Frame::BOUND_FOLLOWING, $1); }
|
||||
;
|
||||
|
||||
%type <windowClauseExclusion> window_frame_exclusion_opt
|
||||
window_frame_exclusion_opt
|
||||
: /* nothing */ { $$ = WindowClause::EXCLUDE_NO_OTHERS; }
|
||||
| EXCLUDE NO OTHERS { $$ = WindowClause::EXCLUDE_NO_OTHERS; }
|
||||
| EXCLUDE CURRENT ROW { $$ = WindowClause::EXCLUDE_CURRENT_ROW; }
|
||||
| EXCLUDE GROUP { $$ = WindowClause::EXCLUDE_GROUP; }
|
||||
| EXCLUDE TIES { $$ = WindowClause::EXCLUDE_TIES; }
|
||||
;
|
||||
|
||||
%type <valueExprNode> delimiter_opt
|
||||
delimiter_opt
|
||||
: /* nothing */ { $$ = MAKE_str_constant(newIntlString(","), lex.att_charset); }
|
||||
@ -7861,12 +7958,18 @@ non_reserved_word
|
||||
| INCREMENT
|
||||
| TRUSTED
|
||||
| CUME_DIST // added in FB 4.0
|
||||
| EXCLUDE
|
||||
| FOLLOWING
|
||||
| NTILE
|
||||
| OTHERS
|
||||
| PERCENT_RANK
|
||||
| PRECEDING
|
||||
| PRIVILEGE
|
||||
| RANGE
|
||||
| RDB_ROLE_IN_USE
|
||||
| RDB_SYSTEM_PRIVILEGE
|
||||
| PRIVILEGE
|
||||
| SYSTEM
|
||||
| TIES
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -300,12 +300,12 @@ bool InvalidReferenceFinder::visit(ExprNode* node)
|
||||
|
||||
|
||||
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
||||
ValueListNode* aPartitionNode, ValueListNode* aOrderNode)
|
||||
ValueListNode* aPartitionNode, WindowClause* aWindowNode)
|
||||
: dsqlScratch(aDsqlScratch),
|
||||
context(aContext),
|
||||
window(aWindow),
|
||||
partitionNode(aPartitionNode),
|
||||
orderNode(aOrderNode),
|
||||
windowNode(aWindowNode),
|
||||
currentLevel(dsqlScratch->scopeLevel)
|
||||
{
|
||||
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
||||
@ -2213,7 +2213,7 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod
|
||||
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch, partitionMap->partition);
|
||||
|
||||
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->partition,
|
||||
partitionMap->order);
|
||||
partitionMap->window);
|
||||
ExprNode::doDsqlFieldRemapper(remapper2, partitionMap->partitionRemapped);
|
||||
}
|
||||
}
|
||||
@ -2802,7 +2802,7 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in
|
||||
|
||||
// Post an item to a map for a context.
|
||||
DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
|
||||
ValueListNode* partitionNode, ValueListNode* orderNode)
|
||||
ValueListNode* partitionNode, WindowClause* windowNode)
|
||||
{
|
||||
DEV_BLKCHK(node, dsql_type_nod);
|
||||
DEV_BLKCHK(context, dsql_type_ctx);
|
||||
@ -2814,7 +2814,7 @@ DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* nod
|
||||
|
||||
if (dsqlScratch->processingWindow)
|
||||
{
|
||||
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, orderNode);
|
||||
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, windowNode);
|
||||
map = partitionMap->map;
|
||||
}
|
||||
else
|
||||
@ -2931,7 +2931,7 @@ bool dsql_ctx::getImplicitJoinField(const MetaName& name, NestConst<ValueExprNod
|
||||
|
||||
// Returns (creating, if necessary) the PartitionMap of a given partition (that may be NULL).
|
||||
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueListNode* partitionNode,
|
||||
ValueListNode* orderNode)
|
||||
WindowClause* windowNode)
|
||||
{
|
||||
thread_db* tdbb = JRD_get_thread_data();
|
||||
|
||||
@ -2942,7 +2942,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
++i)
|
||||
{
|
||||
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
|
||||
PASS1_node_match((*i)->order, orderNode, false))
|
||||
PASS1_node_match((*i)->window, windowNode, false))
|
||||
{
|
||||
partitionMap = *i;
|
||||
}
|
||||
@ -2950,7 +2950,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
|
||||
if (!partitionMap)
|
||||
{
|
||||
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, orderNode);
|
||||
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, windowNode);
|
||||
ctx_win_maps.add(partitionMap);
|
||||
partitionMap->context = dsqlScratch->contextNumber++;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ Jrd::ValueExprNode* PASS1_lookup_alias(Jrd::DsqlCompilerScratch*, const Firebird
|
||||
Jrd::dsql_ctx* PASS1_make_context(Jrd::DsqlCompilerScratch* statement, Jrd::RecordSourceNode* relationNode);
|
||||
bool PASS1_node_match(const Jrd::ExprNode*, const Jrd::ExprNode*, bool);
|
||||
Jrd::DsqlMapNode* PASS1_post_map(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, Jrd::dsql_ctx*,
|
||||
Jrd::ValueListNode*, Jrd::ValueListNode*);
|
||||
Jrd::ValueListNode*, Jrd::WindowClause*);
|
||||
Jrd::RecordSourceNode* PASS1_relation(Jrd::DsqlCompilerScratch*, Jrd::RecordSourceNode*);
|
||||
Jrd::RseNode* PASS1_rse(Jrd::DsqlCompilerScratch*, Jrd::SelectExprNode*, bool);
|
||||
bool PASS1_set_parameter_type(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, const dsc*, bool);
|
||||
|
@ -817,6 +817,11 @@ static const struct {
|
||||
{"crypt_checksum", 335545113},
|
||||
{"not_dba", 335545114},
|
||||
{"no_cursor", 335545115},
|
||||
{"dsql_window_incompat_frames", 335545116},
|
||||
{"dsql_window_range_multi_key", 335545117},
|
||||
{"dsql_window_range_inv_key_type", 335545118},
|
||||
{"dsql_window_frame_value_inv_type", 335545119},
|
||||
{"window_frame_value_invalid", 335545120},
|
||||
{"gfix_db_name", 335740929},
|
||||
{"gfix_invalid_sw", 335740930},
|
||||
{"gfix_incmp_sw", 335740932},
|
||||
|
@ -851,6 +851,11 @@ const ISC_STATUS isc_miss_prvlg = 335545112L;
|
||||
const ISC_STATUS isc_crypt_checksum = 335545113L;
|
||||
const ISC_STATUS isc_not_dba = 335545114L;
|
||||
const ISC_STATUS isc_no_cursor = 335545115L;
|
||||
const ISC_STATUS isc_dsql_window_incompat_frames = 335545116L;
|
||||
const ISC_STATUS isc_dsql_window_range_multi_key = 335545117L;
|
||||
const ISC_STATUS isc_dsql_window_range_inv_key_type = 335545118L;
|
||||
const ISC_STATUS isc_dsql_window_frame_value_inv_type = 335545119L;
|
||||
const ISC_STATUS isc_window_frame_value_invalid = 335545120L;
|
||||
const ISC_STATUS isc_gfix_db_name = 335740929L;
|
||||
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
|
||||
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
|
||||
@ -1322,7 +1327,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
|
||||
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
|
||||
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
|
||||
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
|
||||
const ISC_STATUS isc_err_max = 1266;
|
||||
const ISC_STATUS isc_err_max = 1271;
|
||||
|
||||
#else /* c definitions */
|
||||
|
||||
@ -2143,6 +2148,11 @@ const ISC_STATUS isc_err_max = 1266;
|
||||
#define isc_crypt_checksum 335545113L
|
||||
#define isc_not_dba 335545114L
|
||||
#define isc_no_cursor 335545115L
|
||||
#define isc_dsql_window_incompat_frames 335545116L
|
||||
#define isc_dsql_window_range_multi_key 335545117L
|
||||
#define isc_dsql_window_range_inv_key_type 335545118L
|
||||
#define isc_dsql_window_frame_value_inv_type 335545119L
|
||||
#define isc_window_frame_value_invalid 335545120L
|
||||
#define isc_gfix_db_name 335740929L
|
||||
#define isc_gfix_invalid_sw 335740930L
|
||||
#define isc_gfix_incmp_sw 335740932L
|
||||
@ -2614,7 +2624,7 @@ const ISC_STATUS isc_err_max = 1266;
|
||||
#define isc_trace_switch_param_miss 337182758L
|
||||
#define isc_trace_param_act_notcompat 337182759L
|
||||
#define isc_trace_mandatory_switch_miss 337182760L
|
||||
#define isc_err_max 1266
|
||||
#define isc_err_max 1271
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -820,6 +820,11 @@ Data source : @4"}, /* eds_statement */
|
||||
{335545113, "Invalid or missing checksum of encrypted database"}, /* crypt_checksum */
|
||||
{335545114, "You must have SYSDBA rights at this server"}, /* not_dba */
|
||||
{335545115, "Cannot open cursor for non-SELECT statement"}, /* no_cursor */
|
||||
{335545116, "If <window frame bound 1> specifies @1, then <window frame bound 2> shall not specify @2"}, /* dsql_window_incompat_frames */
|
||||
{335545117, "RANGE based window with <expr> {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value"}, /* dsql_window_range_multi_key */
|
||||
{335545118, "RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types"}, /* dsql_window_range_inv_key_type */
|
||||
{335545119, "Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type"}, /* dsql_window_frame_value_inv_type */
|
||||
{335545120, "Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative"}, /* window_frame_value_invalid */
|
||||
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
|
||||
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
|
||||
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */
|
||||
|
@ -816,6 +816,11 @@ static const struct {
|
||||
{335545113, -902}, /* 793 crypt_checksum */
|
||||
{335545114, -902}, /* 794 not_dba */
|
||||
{335545115, -901}, /* 795 no_cursor */
|
||||
{335545116, -104}, /* 796 dsql_window_incompat_frames */
|
||||
{335545117, -104}, /* 797 dsql_window_range_multi_key */
|
||||
{335545118, -104}, /* 798 dsql_window_range_inv_key_type */
|
||||
{335545119, -104}, /* 799 dsql_window_frame_value_inv_type */
|
||||
{335545120, -833}, /* 800 window_frame_value_invalid */
|
||||
{335740929, -901}, /* 1 gfix_db_name */
|
||||
{335740930, -901}, /* 2 gfix_invalid_sw */
|
||||
{335740932, -901}, /* 4 gfix_incmp_sw */
|
||||
|
@ -816,6 +816,11 @@ static const struct {
|
||||
{335545113, "XX000"}, // 793 crypt_checksum
|
||||
{335545114, "28000"}, // 794 not_dba
|
||||
{335545115, "07005"}, // 795 no_cursor
|
||||
{335545116, "42000"}, // 796 dsql_window_incompat_frames
|
||||
{335545117, "42000"}, // 797 dsql_window_range_multi_key
|
||||
{335545118, "42000"}, // 798 dsql_window_range_inv_key_type
|
||||
{335545119, "42000"}, // 799 dsql_window_frame_value_inv_type
|
||||
{335545120, "42000"}, // 800 window_frame_value_invalid
|
||||
{335740929, "00000"}, // 1 gfix_db_name
|
||||
{335740930, "00000"}, // 2 gfix_invalid_sw
|
||||
{335740932, "00000"}, // 4 gfix_incmp_sw
|
||||
|
@ -44,7 +44,7 @@ using namespace Jrd;
|
||||
|
||||
|
||||
static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* source);
|
||||
static MapNode* parseMap(thread_db* tdbb, CompilerScratch* csb, StreamType stream);
|
||||
static MapNode* parseMap(thread_db* tdbb, CompilerScratch* csb, StreamType stream, bool parseHeader);
|
||||
static int strcmpSpace(const char* p, const char* q);
|
||||
static void processSource(thread_db* tdbb, CompilerScratch* csb, RseNode* rse,
|
||||
RecordSourceNode* source, BoolExprNode** boolean, RecordSourceNodeStack& stack);
|
||||
@ -1248,7 +1248,7 @@ AggregateSourceNode* AggregateSourceNode::parse(thread_db* tdbb, CompilerScratch
|
||||
fb_assert(node->stream <= MAX_STREAMS);
|
||||
node->rse = PAR_rse(tdbb, csb);
|
||||
node->group = PAR_sort(tdbb, csb, blr_group_by, true);
|
||||
node->map = parseMap(tdbb, csb, node->stream);
|
||||
node->map = parseMap(tdbb, csb, node->stream, true);
|
||||
|
||||
return node;
|
||||
}
|
||||
@ -1328,18 +1328,25 @@ void AggregateSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
i != dsqlContext->ctx_win_maps.end();
|
||||
++i)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_partition_by);
|
||||
bool v3 = !((*i)->window &&
|
||||
((*i)->window->extent ||
|
||||
(*i)->window->exclusion != WindowClause::EXCLUDE_NO_OTHERS));
|
||||
|
||||
ValueListNode* partition = (*i)->partition;
|
||||
ValueListNode* partitionRemapped = (*i)->partitionRemapped;
|
||||
ValueListNode* order = (*i)->order;
|
||||
ValueListNode* order = (*i)->window ? (*i)->window->order : NULL;
|
||||
|
||||
if ((*i)->context > MAX_UCHAR)
|
||||
ERRD_post(Arg::Gds(isc_too_many_contexts));
|
||||
|
||||
dsqlScratch->appendUChar(v3 ? blr_partition_by : blr_window_win);
|
||||
dsqlScratch->appendUChar((*i)->context);
|
||||
|
||||
if (partition)
|
||||
{
|
||||
if (!v3)
|
||||
dsqlScratch->appendUChar(blr_window_win_partition);
|
||||
|
||||
dsqlScratch->appendUChar(partition->items.getCount()); // partition by expression count
|
||||
|
||||
NestConst<ValueExprNode>* ptr = partition->items.begin();
|
||||
@ -1347,21 +1354,58 @@ void AggregateSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
GEN_expr(dsqlScratch, *ptr);
|
||||
|
||||
ptr = partitionRemapped->items.begin();
|
||||
for (const NestConst<ValueExprNode>* end = partitionRemapped->items.end(); ptr != end; ++ptr)
|
||||
for (const NestConst<ValueExprNode>* end = partitionRemapped->items.end();
|
||||
ptr != end;
|
||||
++ptr)
|
||||
{
|
||||
GEN_expr(dsqlScratch, *ptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (v3)
|
||||
dsqlScratch->appendUChar(0); // partition by expression count
|
||||
|
||||
if (order)
|
||||
GEN_sort(dsqlScratch, order);
|
||||
else
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_sort);
|
||||
dsqlScratch->appendUChar(0);
|
||||
}
|
||||
if (v3 || order)
|
||||
GEN_sort(dsqlScratch, (v3 ? blr_sort : blr_window_win_order), order);
|
||||
|
||||
genMap(dsqlScratch, (*i)->map);
|
||||
genMap(dsqlScratch, (v3 ? blr_map : blr_window_win_map), (*i)->map);
|
||||
|
||||
if (!v3)
|
||||
{
|
||||
if ((*i)->window->extent)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_window_win_extent_unit);
|
||||
dsqlScratch->appendUChar((UCHAR) (*i)->window->extent->unit);
|
||||
|
||||
WindowClause::Frame* frames[] = {
|
||||
(*i)->window->extent->frame1, (*i)->window->extent->frame2
|
||||
};
|
||||
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
if (frames[j])
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_window_win_extent_frame_bound);
|
||||
dsqlScratch->appendUChar(j + 1);
|
||||
dsqlScratch->appendUChar((UCHAR) frames[j]->bound);
|
||||
|
||||
if (frames[j]->value)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_window_win_extent_frame_value);
|
||||
dsqlScratch->appendUChar(j + 1);
|
||||
frames[j]->value->genBlr(dsqlScratch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((*i)->window->exclusion != WindowClause::EXCLUDE_NO_OTHERS)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_window_win_exclusion);
|
||||
dsqlScratch->appendUChar((UCHAR) (*i)->window->exclusion);
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1381,12 +1425,12 @@ void AggregateSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
else
|
||||
dsqlScratch->appendUChar(0);
|
||||
|
||||
genMap(dsqlScratch, dsqlContext->ctx_map);
|
||||
genMap(dsqlScratch, blr_map, dsqlContext->ctx_map);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a value map for a record selection expression.
|
||||
void AggregateSourceNode::genMap(DsqlCompilerScratch* dsqlScratch, dsql_map* map)
|
||||
void AggregateSourceNode::genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map)
|
||||
{
|
||||
USHORT count = 0;
|
||||
|
||||
@ -1396,7 +1440,7 @@ void AggregateSourceNode::genMap(DsqlCompilerScratch* dsqlScratch, dsql_map* map
|
||||
//if (count >= STREAM_MAP_LENGTH) // not sure if the same limit applies
|
||||
// ERR_post(Arg::Gds(isc_too_many_contexts)); // maybe there's better msg.
|
||||
|
||||
dsqlScratch->appendUChar(blr_map);
|
||||
dsqlScratch->appendUChar(blrVerb);
|
||||
dsqlScratch->appendUShort(count);
|
||||
|
||||
for (dsql_map* temp = map; temp; temp = temp->map_next)
|
||||
@ -1630,7 +1674,7 @@ UnionSourceNode* UnionSourceNode::parse(thread_db* tdbb, CompilerScratch* csb, c
|
||||
while (--count >= 0)
|
||||
{
|
||||
node->clauses.push(PAR_rse(tdbb, csb));
|
||||
node->maps.push(parseMap(tdbb, csb, stream2));
|
||||
node->maps.push(parseMap(tdbb, csb, stream2, true));
|
||||
}
|
||||
|
||||
return node;
|
||||
@ -1977,10 +2021,25 @@ WindowSourceNode* WindowSourceNode::parse(thread_db* tdbb, CompilerScratch* csb)
|
||||
|
||||
node->rse = PAR_rse(tdbb, csb);
|
||||
|
||||
unsigned partitionCount = csb->csb_blr_reader.getByte();
|
||||
unsigned count = csb->csb_blr_reader.getByte();
|
||||
|
||||
for (unsigned i = 0; i < partitionCount; ++i)
|
||||
node->parsePartitionBy(tdbb, csb);
|
||||
for (unsigned i = 0; i < count; ++i)
|
||||
{
|
||||
switch (csb->csb_blr_reader.getByte())
|
||||
{
|
||||
case blr_partition_by:
|
||||
node->parseLegacyPartitionBy(tdbb, csb);
|
||||
break;
|
||||
|
||||
case blr_window_win:
|
||||
node->parseWindow(tdbb, csb);
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_syntax_error(csb, "blr_window");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
@ -1996,27 +2055,149 @@ string WindowSourceNode::internalPrint(NodePrinter& printer) const
|
||||
}
|
||||
|
||||
// Parse PARTITION BY subclauses of window functions.
|
||||
void WindowSourceNode::parsePartitionBy(thread_db* tdbb, CompilerScratch* csb)
|
||||
void WindowSourceNode::parseLegacyPartitionBy(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
if (csb->csb_blr_reader.getByte() != blr_partition_by)
|
||||
PAR_syntax_error(csb, "blr_partition_by");
|
||||
|
||||
SSHORT context;
|
||||
Partition& partition = partitions.add();
|
||||
partition.stream = PAR_context(csb, &context);
|
||||
Window& window = windows.add();
|
||||
window.stream = PAR_context(csb, &context);
|
||||
|
||||
const UCHAR count = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
partition.group = PAR_sort_internal(tdbb, csb, blr_partition_by, count);
|
||||
partition.regroup = PAR_sort_internal(tdbb, csb, blr_partition_by, count);
|
||||
window.group = PAR_sort_internal(tdbb, csb, false, count);
|
||||
window.regroup = PAR_sort_internal(tdbb, csb, false, count);
|
||||
}
|
||||
|
||||
partition.order = PAR_sort(tdbb, csb, blr_sort, true);
|
||||
partition.map = parseMap(tdbb, csb, partition.stream);
|
||||
window.order = PAR_sort(tdbb, csb, blr_sort, true);
|
||||
window.map = parseMap(tdbb, csb, window.stream, true);
|
||||
window.frameExtent = WindowClause::FrameExtent::createDefault(*tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
// Parse frame subclauses of window functions.
|
||||
void WindowSourceNode::parseWindow(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
SSHORT context;
|
||||
Window& window = windows.add();
|
||||
window.stream = PAR_context(csb, &context);
|
||||
window.frameExtent = WindowClause::FrameExtent::createDefault(*tdbb->getDefaultPool());
|
||||
|
||||
UCHAR verb, count;
|
||||
|
||||
while ((verb = csb->csb_blr_reader.getByte()) != blr_end)
|
||||
{
|
||||
switch (verb)
|
||||
{
|
||||
case blr_window_win_partition:
|
||||
count = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
window.group = PAR_sort_internal(tdbb, csb, false, count);
|
||||
window.regroup = PAR_sort_internal(tdbb, csb, false, count);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case blr_window_win_order:
|
||||
count = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (count != 0)
|
||||
window.order = PAR_sort_internal(tdbb, csb, true, count);
|
||||
|
||||
break;
|
||||
|
||||
case blr_window_win_map:
|
||||
window.map = parseMap(tdbb, csb, window.stream, false);
|
||||
break;
|
||||
|
||||
case blr_window_win_extent_unit:
|
||||
window.frameExtent->unit = (WindowClause::FrameExtent::Unit)
|
||||
csb->csb_blr_reader.getByte();
|
||||
|
||||
switch (window.frameExtent->unit)
|
||||
{
|
||||
case WindowClause::FrameExtent::UNIT_RANGE:
|
||||
case WindowClause::FrameExtent::UNIT_ROWS:
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_syntax_error(csb, "blr_window_win_extent_unit");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case blr_window_win_exclusion:
|
||||
//// TODO: CORE-5338 - write code for execution.
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_wish_list) <<
|
||||
Arg::Gds(isc_random) << "window EXCLUDE clause");
|
||||
|
||||
window.exclusion = (WindowClause::Exclusion) csb->csb_blr_reader.getByte();
|
||||
|
||||
switch (window.exclusion)
|
||||
{
|
||||
case WindowClause::EXCLUDE_NO_OTHERS:
|
||||
case WindowClause::EXCLUDE_CURRENT_ROW:
|
||||
case WindowClause::EXCLUDE_GROUP:
|
||||
case WindowClause::EXCLUDE_TIES:
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_syntax_error(csb, "blr_window_win_exclusion");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case blr_window_win_extent_frame_bound:
|
||||
case blr_window_win_extent_frame_value:
|
||||
{
|
||||
UCHAR num = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (num != 1 && num != 2)
|
||||
{
|
||||
PAR_syntax_error(csb, (verb == blr_window_win_extent_frame_bound ?
|
||||
"blr_window_win_extent_frame_bound" : "blr_window_win_extent_frame_value"));
|
||||
}
|
||||
|
||||
NestConst<WindowClause::Frame>& frame = num == 1 ?
|
||||
window.frameExtent->frame1 : window.frameExtent->frame2;
|
||||
|
||||
switch (verb)
|
||||
{
|
||||
case blr_window_win_extent_frame_bound:
|
||||
frame->bound = (WindowClause::Frame::Bound) csb->csb_blr_reader.getByte();
|
||||
|
||||
switch (frame->bound)
|
||||
{
|
||||
case WindowClause::Frame::BOUND_PRECEDING:
|
||||
case WindowClause::Frame::BOUND_FOLLOWING:
|
||||
case WindowClause::Frame::BOUND_CURRENT_ROW:
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_syntax_error(csb, "blr_window_win_extent_frame_bound");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case blr_window_win_extent_frame_value:
|
||||
frame->value = PAR_parse_value(tdbb, csb);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
PAR_syntax_error(csb, "blr_window_win");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowSourceNode* WindowSourceNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
@ -2029,27 +2210,34 @@ WindowSourceNode* WindowSourceNode::copy(thread_db* tdbb, NodeCopier& copier) co
|
||||
|
||||
newSource->rse = rse->copy(tdbb, copier);
|
||||
|
||||
for (ObjectsArray<Partition>::const_iterator inputPartition = partitions.begin();
|
||||
inputPartition != partitions.end();
|
||||
++inputPartition)
|
||||
for (ObjectsArray<Window>::const_iterator inputWindow = windows.begin();
|
||||
inputWindow != windows.end();
|
||||
++inputWindow)
|
||||
{
|
||||
fb_assert(inputPartition->stream <= MAX_STREAMS);
|
||||
fb_assert(inputWindow->stream <= MAX_STREAMS);
|
||||
|
||||
Partition& copyPartition = newSource->partitions.add();
|
||||
Window& copyWindow = newSource->windows.add();
|
||||
|
||||
copyPartition.stream = copier.csb->nextStream();
|
||||
// fb_assert(copyPartition.stream <= MAX_UCHAR);
|
||||
copyWindow.stream = copier.csb->nextStream();
|
||||
// fb_assert(copyWindow.stream <= MAX_UCHAR);
|
||||
|
||||
copier.remap[inputPartition->stream] = copyPartition.stream;
|
||||
CMP_csb_element(copier.csb, copyPartition.stream);
|
||||
copier.remap[inputWindow->stream] = copyWindow.stream;
|
||||
CMP_csb_element(copier.csb, copyWindow.stream);
|
||||
|
||||
if (inputPartition->group)
|
||||
copyPartition.group = inputPartition->group->copy(tdbb, copier);
|
||||
if (inputPartition->regroup)
|
||||
copyPartition.regroup = inputPartition->regroup->copy(tdbb, copier);
|
||||
if (inputPartition->order)
|
||||
copyPartition.order = inputPartition->order->copy(tdbb, copier);
|
||||
copyPartition.map = inputPartition->map->copy(tdbb, copier);
|
||||
if (inputWindow->group)
|
||||
copyWindow.group = inputWindow->group->copy(tdbb, copier);
|
||||
|
||||
if (inputWindow->regroup)
|
||||
copyWindow.regroup = inputWindow->regroup->copy(tdbb, copier);
|
||||
|
||||
if (inputWindow->order)
|
||||
copyWindow.order = inputWindow->order->copy(tdbb, copier);
|
||||
|
||||
if (inputWindow->frameExtent)
|
||||
copyWindow.frameExtent = inputWindow->frameExtent->copy(tdbb, copier);
|
||||
|
||||
copyWindow.map = inputWindow->map->copy(tdbb, copier);
|
||||
copyWindow.exclusion = inputWindow->exclusion;
|
||||
}
|
||||
|
||||
return newSource;
|
||||
@ -2062,25 +2250,26 @@ void WindowSourceNode::ignoreDbKey(thread_db* tdbb, CompilerScratch* csb) const
|
||||
|
||||
RecordSourceNode* WindowSourceNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
fb_assert(partition->stream <= MAX_STREAMS);
|
||||
csb->csb_rpt[partition->stream].csb_flags |= csb_no_dbkey;
|
||||
fb_assert(window->stream <= MAX_STREAMS);
|
||||
csb->csb_rpt[window->stream].csb_flags |= csb_no_dbkey;
|
||||
}
|
||||
|
||||
rse->ignoreDbKey(tdbb, csb);
|
||||
doPass1(tdbb, csb, rse.getAddress());
|
||||
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
doPass1(tdbb, csb, partition->group.getAddress());
|
||||
doPass1(tdbb, csb, partition->regroup.getAddress());
|
||||
doPass1(tdbb, csb, partition->order.getAddress());
|
||||
doPass1(tdbb, csb, partition->map.getAddress());
|
||||
doPass1(tdbb, csb, window->group.getAddress());
|
||||
doPass1(tdbb, csb, window->regroup.getAddress());
|
||||
doPass1(tdbb, csb, window->order.getAddress());
|
||||
doPass1(tdbb, csb, window->frameExtent.getAddress());
|
||||
doPass1(tdbb, csb, window->map.getAddress());
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -2097,11 +2286,11 @@ void WindowSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNod
|
||||
const StreamType viewStream = csb->csb_view_stream;
|
||||
fb_assert(viewStream <= MAX_STREAMS);
|
||||
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
CompilerScratch::csb_repeat* const element = CMP_csb_element(csb, partition->stream);
|
||||
CompilerScratch::csb_repeat* const element = CMP_csb_element(csb, window->stream);
|
||||
element->csb_view = parentView;
|
||||
element->csb_view_stream = viewStream;
|
||||
}
|
||||
@ -2111,26 +2300,27 @@ RecordSourceNode* WindowSourceNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
rse->pass2Rse(tdbb, csb);
|
||||
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
ExprNode::doPass2(tdbb, csb, partition->map.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, partition->group.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, partition->order.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, window->map.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, window->group.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, window->order.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, window->frameExtent.getAddress());
|
||||
|
||||
fb_assert(partition->stream <= MAX_STREAMS);
|
||||
fb_assert(window->stream <= MAX_STREAMS);
|
||||
|
||||
processMap(tdbb, csb, partition->map, &csb->csb_rpt[partition->stream].csb_internal_format);
|
||||
csb->csb_rpt[partition->stream].csb_format =
|
||||
csb->csb_rpt[partition->stream].csb_internal_format;
|
||||
processMap(tdbb, csb, window->map, &csb->csb_rpt[window->stream].csb_internal_format);
|
||||
csb->csb_rpt[window->stream].csb_format =
|
||||
csb->csb_rpt[window->stream].csb_internal_format;
|
||||
}
|
||||
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
ExprNode::doPass2(tdbb, csb, partition->regroup.getAddress());
|
||||
ExprNode::doPass2(tdbb, csb, window->regroup.getAddress());
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -2140,21 +2330,21 @@ void WindowSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
pass2(tdbb, csb);
|
||||
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
csb->csb_rpt[partition->stream].activate();
|
||||
csb->csb_rpt[window->stream].activate();
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSourceNode::containsStream(StreamType checkStream) const
|
||||
{
|
||||
for (ObjectsArray<Partition>::const_iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::const_iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
if (checkStream == partition->stream)
|
||||
if (checkStream == window->stream)
|
||||
return true; // do not mark as variant
|
||||
}
|
||||
|
||||
@ -2166,26 +2356,26 @@ bool WindowSourceNode::containsStream(StreamType checkStream) const
|
||||
|
||||
void WindowSourceNode::collectStreams(SortedStreamList& streamList) const
|
||||
{
|
||||
for (ObjectsArray<Partition>::const_iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::const_iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
if (!streamList.exist(partition->stream))
|
||||
streamList.add(partition->stream);
|
||||
if (!streamList.exist(window->stream))
|
||||
streamList.add(window->stream);
|
||||
}
|
||||
}
|
||||
|
||||
RecordSource* WindowSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/)
|
||||
{
|
||||
for (ObjectsArray<Partition>::iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
opt->beds.add(partition->stream);
|
||||
opt->beds.add(window->stream);
|
||||
}
|
||||
|
||||
RecordSource* const rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) WindowedStream(tdbb, opt->opt_csb,
|
||||
partitions, OPT_compile(tdbb, opt->opt_csb, rse, NULL));
|
||||
windows, OPT_compile(tdbb, opt->opt_csb, rse, NULL));
|
||||
|
||||
StreamList rsbStreams;
|
||||
rsb->findUsedStreams(rsbStreams);
|
||||
@ -2203,11 +2393,11 @@ bool WindowSourceNode::computable(CompilerScratch* csb, StreamType stream,
|
||||
|
||||
void WindowSourceNode::computeRseStreams(StreamList& streamList) const
|
||||
{
|
||||
for (ObjectsArray<Partition>::const_iterator partition = partitions.begin();
|
||||
partition != partitions.end();
|
||||
++partition)
|
||||
for (ObjectsArray<Window>::const_iterator window = windows.begin();
|
||||
window != windows.end();
|
||||
++window)
|
||||
{
|
||||
streamList.add(partition->stream);
|
||||
streamList.add(window->stream);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3302,12 +3492,16 @@ static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, Recor
|
||||
}
|
||||
|
||||
// Parse a MAP clause for a union or global aggregate expression.
|
||||
static MapNode* parseMap(thread_db* tdbb, CompilerScratch* csb, StreamType stream)
|
||||
static MapNode* parseMap(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
bool parseHeader)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
if (csb->csb_blr_reader.getByte() != blr_map)
|
||||
PAR_syntax_error(csb, "blr_map");
|
||||
if (parseHeader)
|
||||
{
|
||||
if (csb->csb_blr_reader.getByte() != blr_map)
|
||||
PAR_syntax_error(csb, "blr_map");
|
||||
}
|
||||
|
||||
unsigned int count = csb->csb_blr_reader.getWord();
|
||||
MapNode* node = FB_NEW_POOL(csb->csb_pool) MapNode(csb->csb_pool);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "../common/classes/objects_array.h"
|
||||
#include "../common/classes/NestConst.h"
|
||||
#include "../common/classes/QualifiedName.h"
|
||||
#include "../dsql/ExprNodes.h"
|
||||
#include "../jrd/jrd.h"
|
||||
#include "../jrd/exe.h"
|
||||
#include "../dsql/Visitors.h"
|
||||
@ -70,6 +71,14 @@ public:
|
||||
bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream);
|
||||
void findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList);
|
||||
|
||||
int getEffectiveNullOrder(unsigned index) const
|
||||
{
|
||||
if (descending[index])
|
||||
return nullOrder[index] == rse_nulls_default ? rse_nulls_last : nullOrder[index];
|
||||
else
|
||||
return nullOrder[index] == rse_nulls_default ? rse_nulls_first : nullOrder[index];
|
||||
}
|
||||
|
||||
public:
|
||||
bool unique; // sorts using unique key - for distinct and group by
|
||||
NestValueArray expressions; // sort expressions
|
||||
@ -460,7 +469,7 @@ public:
|
||||
virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream);
|
||||
|
||||
private:
|
||||
void genMap(DsqlCompilerScratch* dsqlScratch, dsql_map* map);
|
||||
void genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map);
|
||||
|
||||
RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, BoolExprNodeStack* parentStack,
|
||||
StreamType shellStream);
|
||||
@ -543,10 +552,11 @@ private:
|
||||
class WindowSourceNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_WINDOW>
|
||||
{
|
||||
public:
|
||||
struct Partition
|
||||
struct Window
|
||||
{
|
||||
explicit Partition(MemoryPool&)
|
||||
: stream(INVALID_STREAM)
|
||||
explicit Window(MemoryPool&)
|
||||
: stream(INVALID_STREAM),
|
||||
exclusion(WindowClause::EXCLUDE_NO_OTHERS)
|
||||
{
|
||||
}
|
||||
|
||||
@ -555,19 +565,22 @@ public:
|
||||
NestConst<SortNode> regroup;
|
||||
NestConst<SortNode> order;
|
||||
NestConst<MapNode> map;
|
||||
NestConst<WindowClause::FrameExtent> frameExtent;
|
||||
WindowClause::Exclusion exclusion;
|
||||
};
|
||||
|
||||
explicit WindowSourceNode(MemoryPool& pool)
|
||||
: TypedNode<RecordSourceNode, RecordSourceNode::TYPE_WINDOW>(pool),
|
||||
rse(NULL),
|
||||
partitions(pool)
|
||||
windows(pool)
|
||||
{
|
||||
}
|
||||
|
||||
static WindowSourceNode* parse(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
private:
|
||||
void parsePartitionBy(thread_db* tdbb, CompilerScratch* csb);
|
||||
void parseLegacyPartitionBy(thread_db* tdbb, CompilerScratch* csb);
|
||||
void parseWindow(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
public:
|
||||
virtual StreamType getStream() const
|
||||
@ -602,7 +615,7 @@ public:
|
||||
|
||||
private:
|
||||
NestConst<RseNode> rse;
|
||||
Firebird::ObjectsArray<Partition> partitions;
|
||||
Firebird::ObjectsArray<Window> windows;
|
||||
};
|
||||
|
||||
class RseNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_RSE>
|
||||
|
@ -241,5 +241,6 @@ static const struct
|
||||
{"subfunc", function},
|
||||
{"record_version2", byte_line},
|
||||
{"gen_id2", gen_id2}, // 210
|
||||
{"window_win", window_win},
|
||||
{0, 0}
|
||||
};
|
||||
|
@ -407,4 +407,17 @@
|
||||
#define blr_record_version2 (unsigned char) 209
|
||||
#define blr_gen_id2 (unsigned char) 210 // NEXT VALUE FOR generator
|
||||
|
||||
// FB 4.0 specific BLR
|
||||
|
||||
#define blr_window_win (unsigned char) 211
|
||||
|
||||
// subcodes of blr_window_win
|
||||
#define blr_window_win_partition (unsigned char) 1
|
||||
#define blr_window_win_order (unsigned char) 2
|
||||
#define blr_window_win_map (unsigned char) 3
|
||||
#define blr_window_win_extent_unit (unsigned char) 4
|
||||
#define blr_window_win_extent_frame_bound (unsigned char) 5
|
||||
#define blr_window_win_extent_frame_value (unsigned char) 6
|
||||
#define blr_window_win_exclusion (unsigned char) 7
|
||||
|
||||
#endif // JRD_BLR_H
|
||||
|
@ -1402,7 +1402,7 @@ SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
|
||||
if (count == 0 && nullForEmpty)
|
||||
return NULL;
|
||||
|
||||
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp, count);
|
||||
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp == blr_sort, count);
|
||||
|
||||
if (blrOp != blr_sort)
|
||||
sort->unique = true;
|
||||
@ -1413,8 +1413,7 @@ SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
|
||||
|
||||
// Parse the internals of a sort clause. This is used for blr_sort, blr_project, blr_group_by
|
||||
// and blr_partition_by.
|
||||
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
|
||||
USHORT count)
|
||||
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, bool allClauses, USHORT count)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
@ -1427,7 +1426,7 @@ SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
if (blrOp == blr_sort)
|
||||
if (allClauses)
|
||||
{
|
||||
UCHAR code = csb->csb_blr_reader.getByte();
|
||||
|
||||
|
@ -70,8 +70,7 @@ void PAR_procedure_parms(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*
|
||||
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*, SSHORT);
|
||||
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*);
|
||||
Jrd::SortNode* PAR_sort(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR, bool);
|
||||
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR blrOp,
|
||||
USHORT);
|
||||
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, bool, USHORT);
|
||||
SLONG PAR_symbol_to_gdscode(const Firebird::string&);
|
||||
|
||||
typedef Jrd::DmlNode* (*NodeParseFunc)(Jrd::thread_db* tdbb, MemoryPool& pool,
|
||||
|
@ -37,71 +37,51 @@ using namespace Jrd;
|
||||
// Data access: aggregation
|
||||
// ------------------------
|
||||
|
||||
// Note that we can have NULL order here, in case of window function with shouldCallWinPass
|
||||
// returning true, with partition, and without order. Example: ROW_NUMBER() OVER (PARTITION BY N).
|
||||
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, BaseBufferedStream* next,
|
||||
const NestValueArray* order)
|
||||
template <typename ThisType, typename NextType>
|
||||
BaseAggWinStream<ThisType, NextType>::BaseAggWinStream(thread_db* tdbb, CompilerScratch* csb,
|
||||
StreamType stream, const NestValueArray* group, MapNode* groupMap,
|
||||
bool oneRowWhenEmpty, NextType* next)
|
||||
: RecordStream(csb, stream),
|
||||
m_bufferedStream(next),
|
||||
m_next(m_bufferedStream),
|
||||
m_group(group),
|
||||
m_map(map),
|
||||
m_order(order),
|
||||
m_winPassSources(csb->csb_pool),
|
||||
m_winPassTargets(csb->csb_pool)
|
||||
{
|
||||
init(tdbb, csb);
|
||||
}
|
||||
|
||||
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next)
|
||||
: RecordStream(csb, stream),
|
||||
m_bufferedStream(NULL),
|
||||
m_next(next),
|
||||
m_group(group),
|
||||
m_map(map),
|
||||
m_order(NULL),
|
||||
m_winPassSources(csb->csb_pool),
|
||||
m_winPassTargets(csb->csb_pool)
|
||||
m_groupMap(groupMap),
|
||||
m_oneRowWhenEmpty(oneRowWhenEmpty)
|
||||
{
|
||||
init(tdbb, csb);
|
||||
fb_assert(m_next);
|
||||
m_impure = CMP_impure(csb, sizeof(typename ThisType::Impure));
|
||||
}
|
||||
|
||||
void AggregatedStream::open(thread_db* tdbb) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::open(thread_db* tdbb) const
|
||||
{
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
impure->irsb_flags = irsb_open;
|
||||
|
||||
impure->state = STATE_GROUPING;
|
||||
impure->lastGroup = false;
|
||||
impure->partitionBlock.startPosition = impure->partitionBlock.endPosition =
|
||||
impure->partitionBlock.pending = 0;
|
||||
impure->orderBlock = impure->partitionBlock;
|
||||
|
||||
VIO_record(tdbb, &request->req_rpb[m_stream], m_format, tdbb->getDefaultPool());
|
||||
|
||||
unsigned impureCount = m_group ? m_group->getCount() : 0;
|
||||
impureCount += m_order ? m_order->getCount() : 0;
|
||||
|
||||
if (!impure->impureValues && impureCount > 0)
|
||||
if (!impure->groupValues && impureCount > 0)
|
||||
{
|
||||
impure->impureValues = FB_NEW_POOL(*tdbb->getDefaultPool()) impure_value[impureCount];
|
||||
memset(impure->impureValues, 0, sizeof(impure_value) * impureCount);
|
||||
impure->groupValues = FB_NEW_POOL(*tdbb->getDefaultPool()) impure_value[impureCount];
|
||||
memset(impure->groupValues, 0, sizeof(impure_value) * impureCount);
|
||||
}
|
||||
|
||||
m_next->open(tdbb);
|
||||
}
|
||||
|
||||
void AggregatedStream::close(thread_db* tdbb) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::close(thread_db* tdbb) const
|
||||
{
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
|
||||
invalidateRecords(request);
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
if (impure->irsb_flags & irsb_open)
|
||||
{
|
||||
@ -111,232 +91,51 @@ void AggregatedStream::close(thread_db* tdbb) const
|
||||
}
|
||||
}
|
||||
|
||||
bool AggregatedStream::getRecord(thread_db* tdbb) const
|
||||
{
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, 0, true);
|
||||
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
record_param* const rpb = &request->req_rpb[m_stream];
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
if (!(impure->irsb_flags & irsb_open))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_bufferedStream) // Is that a window stream?
|
||||
{
|
||||
const FB_UINT64 position = m_bufferedStream->getPosition(request);
|
||||
|
||||
if (impure->orderBlock.pending == 0)
|
||||
{
|
||||
if (impure->partitionBlock.pending == 0)
|
||||
{
|
||||
if (impure->lastGroup || !evaluateGroup(tdbb, AGG_TYPE_GROUP, MAX_UINT64))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
impure->partitionBlock.startPosition = position;
|
||||
impure->partitionBlock.endPosition = m_bufferedStream->getPosition(request) - 1 -
|
||||
(impure->state == STATE_FETCHED ? 1 : 0);
|
||||
impure->partitionBlock.pending =
|
||||
impure->partitionBlock.endPosition - impure->partitionBlock.startPosition + 1;
|
||||
|
||||
fb_assert(impure->partitionBlock.pending > 0);
|
||||
|
||||
m_bufferedStream->locate(tdbb, position);
|
||||
impure->state = STATE_GROUPING;
|
||||
|
||||
impure->lastGroup = impure->state == STATE_EOF;
|
||||
}
|
||||
|
||||
// Check if we need to re-aggregate by the ORDER BY clause.
|
||||
if (!m_order && m_winPassSources.isEmpty())
|
||||
impure->orderBlock = impure->partitionBlock;
|
||||
else
|
||||
{
|
||||
if (!evaluateGroup(tdbb, AGG_TYPE_ORDER, impure->partitionBlock.pending))
|
||||
fb_assert(false);
|
||||
|
||||
impure->orderBlock.startPosition = position;
|
||||
impure->orderBlock.endPosition = m_bufferedStream->getPosition(request) - 1 -
|
||||
(impure->state == STATE_FETCHED ? 1 : 0);
|
||||
impure->orderBlock.pending =
|
||||
impure->orderBlock.endPosition - impure->orderBlock.startPosition + 1;
|
||||
|
||||
fb_assert(impure->orderBlock.pending > 0);
|
||||
}
|
||||
|
||||
m_bufferedStream->locate(tdbb, position);
|
||||
impure->state = STATE_GROUPING;
|
||||
}
|
||||
|
||||
fb_assert(impure->orderBlock.pending > 0 && impure->partitionBlock.pending > 0);
|
||||
|
||||
--impure->orderBlock.pending;
|
||||
--impure->partitionBlock.pending;
|
||||
|
||||
if (m_winPassSources.hasData())
|
||||
{
|
||||
SlidingWindow window(tdbb, m_bufferedStream, m_group, request,
|
||||
impure->partitionBlock.startPosition, impure->partitionBlock.endPosition,
|
||||
impure->orderBlock.startPosition, impure->orderBlock.endPosition);
|
||||
dsc* desc;
|
||||
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_winPassSources.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_winPassSources.begin(),
|
||||
*target = m_winPassTargets.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
const AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
const FieldNode* field = (*target)->as<FieldNode>();
|
||||
const USHORT id = field->fieldId;
|
||||
Record* record = request->req_rpb[field->fieldStream].rpb_record;
|
||||
|
||||
desc = aggNode->winPass(tdbb, request, &window);
|
||||
|
||||
if (!desc)
|
||||
record->setNull(id);
|
||||
else
|
||||
{
|
||||
MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
|
||||
record->clearNull(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bufferedStream->getRecord(tdbb))
|
||||
fb_assert(false);
|
||||
|
||||
// If there is no group, we should reassign the map items.
|
||||
if (!m_group)
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
const AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
if (!aggNode)
|
||||
EXE_assignment(tdbb, *source, *target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!evaluateGroup(tdbb, AGG_TYPE_GROUP, MAX_UINT64))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rpb->rpb_number.setValid(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AggregatedStream::refetchRecord(thread_db* tdbb) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::refetchRecord(thread_db* tdbb) const
|
||||
{
|
||||
return m_next->refetchRecord(tdbb);
|
||||
}
|
||||
|
||||
bool AggregatedStream::lockRecord(thread_db* /*tdbb*/) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::lockRecord(thread_db* /*tdbb*/) const
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
return false; // compiler silencer
|
||||
}
|
||||
|
||||
void AggregatedStream::print(thread_db* tdbb, string& plan,
|
||||
bool detailed, unsigned level) const
|
||||
{
|
||||
if (detailed)
|
||||
plan += printIndent(++level) + (m_bufferedStream ? "Window" : "Aggregate");
|
||||
|
||||
m_next->print(tdbb, plan, detailed, level);
|
||||
}
|
||||
|
||||
void AggregatedStream::markRecursive()
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::markRecursive()
|
||||
{
|
||||
m_next->markRecursive();
|
||||
}
|
||||
|
||||
void AggregatedStream::invalidateRecords(jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::invalidateRecords(jrd_req* request) const
|
||||
{
|
||||
m_next->invalidateRecords(request);
|
||||
}
|
||||
|
||||
void AggregatedStream::findUsedStreams(StreamList& streams, bool expandAll) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::findUsedStreams(StreamList& streams,
|
||||
bool expandAll) const
|
||||
{
|
||||
RecordStream::findUsedStreams(streams);
|
||||
|
||||
if (expandAll)
|
||||
m_next->findUsedStreams(streams, true);
|
||||
|
||||
if (m_bufferedStream)
|
||||
m_bufferedStream->findUsedStreams(streams, expandAll);
|
||||
}
|
||||
|
||||
void AggregatedStream::nullRecords(thread_db* tdbb) const
|
||||
{
|
||||
RecordStream::nullRecords(tdbb);
|
||||
|
||||
if (m_bufferedStream)
|
||||
m_bufferedStream->nullRecords(tdbb);
|
||||
}
|
||||
|
||||
void AggregatedStream::init(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
fb_assert(m_map && m_next);
|
||||
m_impure = CMP_impure(csb, sizeof(Impure));
|
||||
|
||||
// Separate nodes that requires the winPass call.
|
||||
|
||||
NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
|
||||
for (NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
if (aggNode)
|
||||
{
|
||||
aggNode->ordered = m_order != NULL;
|
||||
|
||||
bool wantWinPass = false;
|
||||
aggNode->aggSetup(wantWinPass);
|
||||
|
||||
if (wantWinPass)
|
||||
{
|
||||
m_winPassSources.add(*source);
|
||||
m_winPassTargets.add(*target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the next aggregated record of a value group.
|
||||
bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64 limit) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::evaluateGroup(thread_db* tdbb) const
|
||||
{
|
||||
const NestValueArray* const group = aggType == AGG_TYPE_GROUP ? m_group : m_order;
|
||||
unsigned groupOffset = aggType == AGG_TYPE_GROUP || !m_group ? 0 : m_group->getCount();
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, 0, true);
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
// if we found the last record last time, we're all done
|
||||
if (impure->state == STATE_EOF)
|
||||
@ -344,48 +143,50 @@ bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64
|
||||
|
||||
try
|
||||
{
|
||||
if (aggType == AGG_TYPE_GROUP || group != NULL)
|
||||
aggInit(tdbb, request, aggType);
|
||||
if (m_groupMap)
|
||||
aggInit(tdbb, request, m_groupMap);
|
||||
|
||||
// If there isn't a record pending, open the stream and get one
|
||||
|
||||
if (!getNextRecord(tdbb, request, limit))
|
||||
if (!getNextRecord(tdbb, request))
|
||||
{
|
||||
impure->state = STATE_EOF;
|
||||
|
||||
if (group || m_bufferedStream)
|
||||
if (!m_oneRowWhenEmpty)
|
||||
{
|
||||
finiDistinct(tdbb, request);
|
||||
if (m_groupMap)
|
||||
aggFinish(tdbb, request, m_groupMap);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
cacheValues(tdbb, request, group, groupOffset);
|
||||
cacheValues(tdbb, request, m_group, impure->groupValues, DummyAdjustFunctor());
|
||||
|
||||
// Loop thru records until either a value change or EOF
|
||||
|
||||
while (impure->state == STATE_GROUPING)
|
||||
{
|
||||
if ((aggType == AGG_TYPE_GROUP || group != NULL) && !aggPass(tdbb, request))
|
||||
if (m_groupMap && !aggPass(tdbb, request, m_groupMap->sourceList, m_groupMap->targetList))
|
||||
impure->state = STATE_EOF;
|
||||
else if (getNextRecord(tdbb, request, limit))
|
||||
else if (getNextRecord(tdbb, request))
|
||||
{
|
||||
// In the case of a group by, look for a change in value of any of
|
||||
// the columns; if we find one, stop aggregating and return what we have.
|
||||
|
||||
if (lookForChange(tdbb, request, group, groupOffset))
|
||||
if (lookForChange(tdbb, request, m_group, NULL, impure->groupValues))
|
||||
impure->state = STATE_FETCHED;
|
||||
}
|
||||
else
|
||||
impure->state = STATE_EOF;
|
||||
}
|
||||
|
||||
if (aggType == AGG_TYPE_GROUP || group != NULL)
|
||||
aggExecute(tdbb, request);
|
||||
if (m_groupMap)
|
||||
aggExecute(tdbb, request, m_groupMap->sourceList, m_groupMap->targetList);
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
finiDistinct(tdbb, request);
|
||||
if (m_groupMap)
|
||||
aggFinish(tdbb, request, m_groupMap);
|
||||
throw;
|
||||
}
|
||||
|
||||
@ -393,32 +194,36 @@ bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64
|
||||
}
|
||||
|
||||
// Initialize the aggregate record
|
||||
void AggregatedStream::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::aggInit(thread_db* tdbb, jrd_req* request,
|
||||
const MapNode* map) const
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = map->sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = map->sourceList.begin(),
|
||||
*target = map->targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
const AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
if (aggNode)
|
||||
aggNode->aggInit(tdbb, request, aggType);
|
||||
aggNode->aggInit(tdbb, request);
|
||||
else if ((*source)->is<LiteralNode>())
|
||||
EXE_assignment(tdbb, *source, *target);
|
||||
}
|
||||
}
|
||||
|
||||
// Go through and compute all the aggregates on this record
|
||||
bool AggregatedStream::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::aggPass(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray& sourceList, const NestValueArray& targetList) const
|
||||
{
|
||||
bool ret = true;
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = sourceList.begin(),
|
||||
*target = targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
@ -440,12 +245,14 @@ bool AggregatedStream::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AggregatedStream::aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::aggExecute(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray& sourceList, const NestValueArray& targetList) const
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = sourceList.begin(),
|
||||
*target = targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
@ -469,87 +276,14 @@ void AggregatedStream::aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
}
|
||||
}
|
||||
|
||||
bool AggregatedStream::getNextRecord(thread_db* tdbb, jrd_req* request, FB_UINT64& limit) const
|
||||
{
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
if (limit == 0)
|
||||
return false;
|
||||
else if (impure->state == STATE_FETCHED)
|
||||
{
|
||||
impure->state = STATE_GROUPING;
|
||||
return true;
|
||||
}
|
||||
else if (m_next->getRecord(tdbb))
|
||||
{
|
||||
--limit;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache the values of a group/order in the impure.
|
||||
inline void AggregatedStream::cacheValues(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, unsigned impureOffset) const
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue, ++impureOffset)
|
||||
{
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* target = &impure->impureValues[impureOffset];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
target->vlu_desc.dsc_address = NULL;
|
||||
else
|
||||
EVL_make_value(tdbb, desc, target);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for change in the values of a group/order.
|
||||
inline bool AggregatedStream::lookForChange(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, unsigned impureOffset) const
|
||||
{
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue, ++impureOffset)
|
||||
{
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* vtemp = &impure->impureValues[impureOffset];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
{
|
||||
if (vtemp->vlu_desc.dsc_address)
|
||||
return true;
|
||||
}
|
||||
else if (!vtemp->vlu_desc.dsc_address || MOV_compare(&vtemp->vlu_desc, desc) != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finalize a sort for distinct aggregate
|
||||
void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::aggFinish(thread_db* tdbb, jrd_req* request,
|
||||
const MapNode* map) const
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = map->sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = map->sourceList.begin();
|
||||
source != sourceEnd;
|
||||
++source)
|
||||
{
|
||||
@ -560,131 +294,108 @@ void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SlidingWindow::SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
|
||||
const NestValueArray* aGroup, jrd_req* aRequest,
|
||||
FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd,
|
||||
FB_UINT64 aOrderStart, FB_UINT64 aOrderEnd)
|
||||
: tdbb(aTdbb), // Note: instanciate the class only as local variable
|
||||
stream(aStream),
|
||||
group(aGroup),
|
||||
request(aRequest),
|
||||
partitionStart(aPartitionStart),
|
||||
partitionEnd(aPartitionEnd),
|
||||
orderStart(aOrderStart),
|
||||
orderEnd(aOrderEnd),
|
||||
moved(false)
|
||||
// Look for change in the values of a group/order.
|
||||
template <typename ThisType, typename NextType>
|
||||
int BaseAggWinStream<ThisType, NextType>::lookForChange(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, const SortNode* sort, impure_value* values) const
|
||||
{
|
||||
savedPosition = stream->getPosition(request);
|
||||
}
|
||||
|
||||
SlidingWindow::~SlidingWindow()
|
||||
{
|
||||
if (!moved)
|
||||
return;
|
||||
|
||||
for (impure_value* impure = partitionKeys.begin(); impure != partitionKeys.end(); ++impure)
|
||||
delete impure->vlu_string;
|
||||
|
||||
// Position the stream where we received it.
|
||||
stream->locate(tdbb, savedPosition);
|
||||
}
|
||||
|
||||
// Move in the window without pass partition boundaries.
|
||||
bool SlidingWindow::move(SINT64 delta)
|
||||
{
|
||||
const SINT64 newPosition = SINT64(savedPosition) + delta;
|
||||
|
||||
// If we try to go out of bounds, no need to check the partition.
|
||||
if (newPosition < 0 || newPosition >= (SINT64) stream->getCount(tdbb))
|
||||
return false;
|
||||
|
||||
if (!group)
|
||||
{
|
||||
// No partition, we may go everywhere.
|
||||
|
||||
moved = true;
|
||||
|
||||
stream->locate(tdbb, newPosition);
|
||||
|
||||
if (!stream->getRecord(tdbb))
|
||||
{
|
||||
fb_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!moved)
|
||||
{
|
||||
// This is our first move. We should cache the partition values, so subsequente moves didn't
|
||||
// need to evaluate them again.
|
||||
|
||||
if (!stream->getRecord(tdbb))
|
||||
{
|
||||
fb_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
impure_value* impure = partitionKeys.getBuffer(group->getCount());
|
||||
memset(impure, 0, sizeof(impure_value) * group->getCount());
|
||||
|
||||
const NestConst<ValueExprNode>* const end = group->end();
|
||||
dsc* desc;
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr < end; ++ptr, ++impure)
|
||||
{
|
||||
const ValueExprNode* from = *ptr;
|
||||
desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
impure->vlu_desc.dsc_address = NULL;
|
||||
else
|
||||
EVL_make_value(tdbb, desc, impure);
|
||||
}
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
stream->locate(tdbb, savedPosition); // Reposition for a new try.
|
||||
throw;
|
||||
}
|
||||
|
||||
moved = true;
|
||||
}
|
||||
|
||||
stream->locate(tdbb, newPosition);
|
||||
|
||||
if (!stream->getRecord(tdbb))
|
||||
{
|
||||
fb_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify if we're still inside the same partition.
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
impure_value* impure = partitionKeys.begin();
|
||||
dsc* desc;
|
||||
const NestConst<ValueExprNode>* const end = group->end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr != end; ++ptr, ++impure)
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue)
|
||||
{
|
||||
const ValueExprNode* from = *ptr;
|
||||
desc = EVL_expr(tdbb, request, from);
|
||||
int direction = 1;
|
||||
int nullDirection = 1;
|
||||
|
||||
if (sort)
|
||||
{
|
||||
unsigned index = ptrValue - group->begin();
|
||||
|
||||
if (sort->descending[index])
|
||||
direction = -1;
|
||||
|
||||
nullDirection = (sort->getEffectiveNullOrder(index) == rse_nulls_first ? 1 : -1);
|
||||
}
|
||||
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* vtemp = &values[ptrValue - group->begin()];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
int n;
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
{
|
||||
if (impure->vlu_desc.dsc_address)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!impure->vlu_desc.dsc_address || MOV_compare(&impure->vlu_desc, desc) != 0)
|
||||
return false;
|
||||
if (vtemp->vlu_desc.dsc_address)
|
||||
return -1 * nullDirection;
|
||||
}
|
||||
else if (!vtemp->vlu_desc.dsc_address)
|
||||
return 1 * nullDirection;
|
||||
else if ((n = MOV_compare(desc, &vtemp->vlu_desc)) != 0)
|
||||
return n * direction;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::getNextRecord(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
if (impure->state == STATE_FETCHED)
|
||||
{
|
||||
impure->state = STATE_GROUPING;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return m_next->getRecord(tdbb);
|
||||
}
|
||||
|
||||
// Export the template for WindowedStream::WindowStream.
|
||||
template class BaseAggWinStream<WindowedStream::WindowStream, BaseBufferedStream>;
|
||||
|
||||
// ------------------------------
|
||||
|
||||
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next)
|
||||
: BaseAggWinStream(tdbb, csb, stream, group, map, !group, next)
|
||||
{
|
||||
fb_assert(map);
|
||||
}
|
||||
|
||||
void AggregatedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const
|
||||
{
|
||||
if (detailed)
|
||||
plan += printIndent(++level) + "Aggregate";
|
||||
|
||||
m_next->print(tdbb, plan, detailed, level);
|
||||
}
|
||||
|
||||
bool AggregatedStream::getRecord(thread_db* tdbb) const
|
||||
{
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, 0, true);
|
||||
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
record_param* const rpb = &request->req_rpb[m_stream];
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
if (!(impure->irsb_flags & irsb_open))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!evaluateGroup(tdbb))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
rpb->rpb_number.setValid(true);
|
||||
return true;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "../jrd/req.h"
|
||||
#include "../jrd/rse.h"
|
||||
#include "../jrd/inf_pub.h"
|
||||
#include "../jrd/evl_proto.h"
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
@ -601,10 +602,9 @@ namespace Jrd
|
||||
class SlidingWindow
|
||||
{
|
||||
public:
|
||||
SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
|
||||
const NestValueArray* aGroup, jrd_req* aRequest,
|
||||
SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, jrd_req* request,
|
||||
FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd,
|
||||
FB_UINT64 aOrderStart, FB_UINT64 aOrderEnd);
|
||||
FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd);
|
||||
~SlidingWindow();
|
||||
|
||||
FB_UINT64 getPartitionStart() const
|
||||
@ -622,39 +622,44 @@ namespace Jrd
|
||||
return partitionEnd - partitionStart + 1;
|
||||
}
|
||||
|
||||
FB_UINT64 getOrderStart() const
|
||||
FB_UINT64 getFrameStart() const
|
||||
{
|
||||
return orderStart;
|
||||
return frameStart;
|
||||
}
|
||||
|
||||
FB_UINT64 getOrderEnd() const
|
||||
FB_UINT64 getFrameEnd() const
|
||||
{
|
||||
return orderEnd;
|
||||
return frameEnd;
|
||||
}
|
||||
|
||||
FB_UINT64 getOrderSize() const
|
||||
FB_UINT64 getFrameSize() const
|
||||
{
|
||||
return orderEnd - orderStart + 1;
|
||||
return frameEnd - frameStart + 1;
|
||||
}
|
||||
|
||||
bool move(SINT64 delta);
|
||||
FB_UINT64 getRecordPosition() const
|
||||
{
|
||||
return savedPosition;
|
||||
}
|
||||
|
||||
bool moveWithinPartition(SINT64 delta);
|
||||
bool moveWithinFrame(SINT64 delta);
|
||||
|
||||
private:
|
||||
thread_db* tdbb;
|
||||
const BaseBufferedStream* const stream;
|
||||
const NestValueArray* group;
|
||||
jrd_req* request;
|
||||
Firebird::Array<impure_value> partitionKeys;
|
||||
FB_UINT64 partitionStart;
|
||||
FB_UINT64 partitionEnd;
|
||||
FB_UINT64 orderStart;
|
||||
FB_UINT64 orderEnd;
|
||||
FB_UINT64 frameStart;
|
||||
FB_UINT64 frameEnd;
|
||||
FB_UINT64 savedPosition;
|
||||
bool moved;
|
||||
};
|
||||
|
||||
class AggregatedStream : public RecordStream
|
||||
template <typename ThisType, typename NextType>
|
||||
class BaseAggWinStream : public RecordStream
|
||||
{
|
||||
protected:
|
||||
enum State
|
||||
{
|
||||
STATE_EOF, // We processed everything now process EOF
|
||||
@ -662,74 +667,196 @@ namespace Jrd
|
||||
STATE_GROUPING // Entering EVL group before fetching the first record
|
||||
};
|
||||
|
||||
struct Block
|
||||
{
|
||||
FB_UINT64 startPosition;
|
||||
FB_UINT64 endPosition;
|
||||
FB_UINT64 pending;
|
||||
};
|
||||
|
||||
struct Impure : public RecordSource::Impure
|
||||
{
|
||||
impure_value* impureValues;
|
||||
Block partitionBlock;
|
||||
Block orderBlock;
|
||||
impure_value* groupValues;
|
||||
State state;
|
||||
bool lastGroup;
|
||||
};
|
||||
|
||||
struct DummyAdjustFunctor
|
||||
{
|
||||
void operator ()(impure_value* target)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, BaseBufferedStream* next,
|
||||
const NestValueArray* order);
|
||||
|
||||
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next);
|
||||
BaseAggWinStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* groupMap, bool oneRowWhenEmpty, NextType* next);
|
||||
|
||||
public:
|
||||
void open(thread_db* tdbb) const;
|
||||
void close(thread_db* tdbb) const;
|
||||
|
||||
bool getRecord(thread_db* tdbb) const;
|
||||
bool refetchRecord(thread_db* tdbb) const;
|
||||
bool lockRecord(thread_db* tdbb) const;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level) const;
|
||||
|
||||
void markRecursive();
|
||||
void invalidateRecords(jrd_req* request) const;
|
||||
|
||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const;
|
||||
void nullRecords(thread_db* tdbb) const;
|
||||
|
||||
protected:
|
||||
Impure* getImpure(jrd_req* request) const
|
||||
{
|
||||
return request->getImpure<typename ThisType::Impure>(m_impure);
|
||||
}
|
||||
|
||||
bool evaluateGroup(thread_db* tdbb) const;
|
||||
|
||||
void aggInit(thread_db* tdbb, jrd_req* request, const MapNode* map) const;
|
||||
bool aggPass(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray& sourceList, const NestValueArray& targetList) const;
|
||||
void aggExecute(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray& sourceList, const NestValueArray& targetList) const;
|
||||
void aggFinish(thread_db* tdbb, jrd_req* request, const MapNode* map) const;
|
||||
|
||||
// Cache the values of a group/order in the impure.
|
||||
template <typename AdjustFunctor>
|
||||
void cacheValues(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, impure_value* values,
|
||||
AdjustFunctor adjustFunctor) const
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue)
|
||||
{
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* target = &values[ptrValue - group->begin()];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
target->vlu_desc.dsc_address = NULL;
|
||||
else
|
||||
{
|
||||
EVL_make_value(tdbb, desc, target);
|
||||
adjustFunctor(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lookForChange(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, const SortNode* sort, impure_value* values) const;
|
||||
|
||||
private:
|
||||
void init(thread_db* tdbb, CompilerScratch* csb);
|
||||
bool getNextRecord(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
bool evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64 limit) const;
|
||||
void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
bool aggPass(thread_db* tdbb, jrd_req* request) const;
|
||||
void aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
bool getNextRecord(thread_db* tdbb, jrd_req* request, FB_UINT64& limit) const;
|
||||
void cacheValues(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, unsigned impureOffset) const;
|
||||
bool lookForChange(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, unsigned impureOffset) const;
|
||||
void finiDistinct(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
NestConst<BaseBufferedStream> m_bufferedStream;
|
||||
NestConst<RecordSource> m_next;
|
||||
protected:
|
||||
NestConst<NextType> m_next;
|
||||
const NestValueArray* const m_group;
|
||||
NestConst<MapNode> m_map;
|
||||
const NestValueArray* const m_order;
|
||||
NestValueArray m_winPassSources;
|
||||
NestValueArray m_winPassTargets;
|
||||
NestConst<MapNode> m_groupMap;
|
||||
bool m_oneRowWhenEmpty;
|
||||
};
|
||||
|
||||
class AggregatedStream : public BaseAggWinStream<AggregatedStream, RecordSource>
|
||||
{
|
||||
public:
|
||||
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next);
|
||||
|
||||
public:
|
||||
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level) const;
|
||||
bool getRecord(thread_db* tdbb) const;
|
||||
};
|
||||
|
||||
class WindowedStream : public RecordSource
|
||||
{
|
||||
public:
|
||||
class WindowStream : public BaseAggWinStream<WindowStream, BaseBufferedStream>
|
||||
{
|
||||
private:
|
||||
struct AdjustFunctor
|
||||
{
|
||||
AdjustFunctor(const ArithmeticNode* aArithNode, const dsc* aOffsetDesc)
|
||||
: arithNode(aArithNode),
|
||||
offsetDesc(aOffsetDesc)
|
||||
{
|
||||
}
|
||||
|
||||
void operator ()(impure_value* target)
|
||||
{
|
||||
ArithmeticNode::add2(offsetDesc, target, arithNode, arithNode->blrOp);
|
||||
}
|
||||
|
||||
const ArithmeticNode* arithNode;
|
||||
const dsc* offsetDesc;
|
||||
};
|
||||
|
||||
struct Block
|
||||
{
|
||||
SINT64 startPosition;
|
||||
SINT64 endPosition;
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
startPosition = endPosition = MIN_SINT64;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !(startPosition == MIN_SINT64 && endPosition == MIN_SINT64);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
struct Impure : public BaseAggWinStream::Impure
|
||||
{
|
||||
impure_value* orderValues;
|
||||
SINT64 partitionPending, rangePending;
|
||||
Block partitionBlock, windowBlock;
|
||||
impure_value_ex startOffset, endOffset;
|
||||
};
|
||||
|
||||
public:
|
||||
WindowStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, BaseBufferedStream* next,
|
||||
SortNode* order, MapNode* windowMap,
|
||||
WindowClause::FrameExtent* frameExtent,
|
||||
WindowClause::Exclusion exclusion);
|
||||
|
||||
public:
|
||||
void open(thread_db* tdbb) const;
|
||||
void close(thread_db* tdbb) const;
|
||||
|
||||
bool getRecord(thread_db* tdbb) const;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level) const;
|
||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const;
|
||||
void nullRecords(thread_db* tdbb) const;
|
||||
|
||||
protected:
|
||||
Impure* getImpure(jrd_req* request) const
|
||||
{
|
||||
return request->getImpure<Impure>(m_impure);
|
||||
}
|
||||
|
||||
private:
|
||||
const void getFrameValue(thread_db* tdbb, jrd_req* request,
|
||||
const WindowClause::Frame* frame, impure_value_ex* impureValue) const;
|
||||
|
||||
SINT64 locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure,
|
||||
const WindowClause::Frame* frame, const dsc* offsetDesc, SINT64 position) const;
|
||||
|
||||
private:
|
||||
NestConst<SortNode> m_order;
|
||||
const MapNode* m_windowMap;
|
||||
NestConst<WindowClause::FrameExtent> m_frameExtent;
|
||||
Firebird::Array<NestConst<ArithmeticNode> > m_arithNodes;
|
||||
NestValueArray m_aggSources, m_aggTargets;
|
||||
NestValueArray m_winPassSources, m_winPassTargets;
|
||||
WindowClause::Exclusion m_exclusion;
|
||||
UCHAR m_invariantOffsets; // 0x1 | 0x2 bitmask
|
||||
};
|
||||
|
||||
public:
|
||||
WindowedStream(thread_db* tdbb, CompilerScratch* csb,
|
||||
Firebird::ObjectsArray<WindowSourceNode::Partition>& partitions, RecordSource* next);
|
||||
Firebird::ObjectsArray<WindowSourceNode::Window>& windows, RecordSource* next);
|
||||
|
||||
void open(thread_db* tdbb) const;
|
||||
void close(thread_db* tdbb) const;
|
||||
@ -738,9 +865,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const;
|
||||
bool lockRecord(thread_db* tdbb) const;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level) const;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level) const;
|
||||
void markRecursive();
|
||||
void invalidateRecords(jrd_req* request) const;
|
||||
|
||||
@ -822,7 +947,6 @@ namespace Jrd
|
||||
const Format* m_format;
|
||||
};
|
||||
|
||||
|
||||
// Multiplexing (many -> one) access methods
|
||||
|
||||
class NestedLoopJoin : public RecordSource
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -127,17 +127,10 @@ inline void impure_value::make_double(const double val)
|
||||
this->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&this->vlu_misc.vlu_double);
|
||||
}
|
||||
|
||||
enum AggType
|
||||
{
|
||||
AGG_TYPE_GROUP,
|
||||
AGG_TYPE_ORDER
|
||||
};
|
||||
|
||||
struct impure_value_ex : public impure_value
|
||||
{
|
||||
SINT64 vlux_count;
|
||||
blb* vlu_blob;
|
||||
AggType aggType;
|
||||
};
|
||||
|
||||
const int VLU_computed = 1; // An invariant sub-query has been computed
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
|
||||
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
|
||||
--
|
||||
('2016-07-28 14:55:12', 'JRD', 0, 796)
|
||||
('2016-09-02 12:40:00', 'JRD', 0, 801)
|
||||
('2015-03-17 18:33:00', 'QLI', 1, 533)
|
||||
('2015-01-07 18:01:51', 'GFIX', 3, 134)
|
||||
('1996-11-07 13:39:40', 'GPRE', 4, 1)
|
||||
|
@ -903,6 +903,11 @@ Data source : @4', NULL, NULL)
|
||||
('crypt_checksum', 'CryptoManager::checkDigitalSignature', 'CryptoManager.cpp', NULL, 0, 793, NULL, 'Invalid or missing checksum of encrypted database', NULL, NULL);
|
||||
('not_dba', 'Service::start', 'svc.cpp', NULL, 0, 794, NULL, 'You must have SYSDBA rights at this server', NULL, NULL);
|
||||
('no_cursor', 'DSQL_open', 'dsql.cpp', NULL, 0, 795, NULL, 'Cannot open cursor for non-SELECT statement', NULL, NULL);
|
||||
('dsql_window_incompat_frames', NULL, 'ExprNodes.cpp', NULL, 0, 796, NULL, 'If <window frame bound 1> specifies @1, then <window frame bound 2> shall not specify @2', NULL, NULL);
|
||||
('dsql_window_range_multi_key', NULL, 'ExprNodes.cpp', NULL, 0, 797, NULL, 'RANGE based window with <expr> {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value', NULL, NULL);
|
||||
('dsql_window_range_inv_key_type', NULL, 'ExprNodes.cpp', NULL, 0, 798, NULL, 'RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types', NULL, NULL);
|
||||
('dsql_window_frame_value_inv_type', NULL, 'ExprNodes.cpp', NULL, 0, 799, NULL, 'Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type', NULL, NULL);
|
||||
('window_frame_value_invalid', NULL, 'ExprNodes.cpp', NULL, 0, 800, NULL, 'Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative', NULL, NULL);
|
||||
-- QLI
|
||||
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
|
||||
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);
|
||||
|
@ -802,6 +802,11 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
|
||||
(-902, 'XX', '000', 0, 793, 'crypt_checksum', NULL, NULL)
|
||||
(-902, '28', '000', 0, 794, 'not_dba', NULL, NULL)
|
||||
(-901, '07', '005', 0, 795, 'no_cursor', NULL, NULL)
|
||||
(-104, '42', '000', 0, 796, 'dsql_window_incompat_frames', NULL, NULL)
|
||||
(-104, '42', '000', 0, 797, 'dsql_window_range_multi_key', NULL, NULL)
|
||||
(-104, '42', '000', 0, 798, 'dsql_window_range_inv_key_type', NULL, NULL)
|
||||
(-104, '42', '000', 0, 799, 'dsql_window_frame_value_inv_type', NULL, NULL)
|
||||
(-833, '42', '000', 0, 800, 'window_frame_value_invalid', NULL, NULL)
|
||||
-- GFIX
|
||||
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
|
||||
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)
|
||||
|
@ -259,6 +259,7 @@ const int op_derived_expr = 25;
|
||||
const int op_partition_args = 26;
|
||||
const int op_subproc_decl = 27;
|
||||
const int op_subfunc_decl = 28;
|
||||
const int op_window_win = 29;
|
||||
|
||||
static const UCHAR
|
||||
// generic print formats
|
||||
@ -339,7 +340,8 @@ static const UCHAR
|
||||
decode[] = { op_line, op_verb, op_indent, op_byte, op_line, op_args, op_indent, op_byte,
|
||||
op_line, op_args, 0},
|
||||
subproc_decl[] = { op_subproc_decl, 0},
|
||||
subfunc_decl[] = { op_subfunc_decl, 0};
|
||||
subfunc_decl[] = { op_subfunc_decl, 0},
|
||||
window_win[] = { op_byte, op_window_win, 0};
|
||||
|
||||
|
||||
#include "../jrd/blp.h"
|
||||
@ -3597,6 +3599,94 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
|
||||
break;
|
||||
}
|
||||
|
||||
case op_window_win:
|
||||
{
|
||||
offset = blr_print_line(control, offset);
|
||||
static const char* sub_codes[] =
|
||||
{
|
||||
NULL,
|
||||
"partition",
|
||||
"order",
|
||||
"map",
|
||||
"extent_unit",
|
||||
"extent_frame_bound",
|
||||
"extent_frame_value",
|
||||
"exclusion"
|
||||
};
|
||||
|
||||
while ((blr_operator = control->ctl_blr_reader.getByte()) != blr_end)
|
||||
{
|
||||
blr_indent(control, level);
|
||||
|
||||
if (blr_operator > 0 && blr_operator < FB_NELEM(sub_codes))
|
||||
blr_format(control, "blr_window_win_%s, ", sub_codes[blr_operator]);
|
||||
|
||||
switch (blr_operator)
|
||||
{
|
||||
case blr_window_win_partition:
|
||||
case blr_window_win_order:
|
||||
n = blr_print_byte(control);
|
||||
offset = blr_print_line(control, offset);
|
||||
++level;
|
||||
|
||||
while (--n >= 0)
|
||||
{
|
||||
blr_print_verb(control, level);
|
||||
|
||||
if (blr_operator == blr_window_win_partition)
|
||||
blr_print_verb(control, level);
|
||||
}
|
||||
|
||||
--level;
|
||||
break;
|
||||
|
||||
case blr_window_win_map:
|
||||
n = blr_print_word(control);
|
||||
offset = blr_print_line(control, offset);
|
||||
++level;
|
||||
|
||||
while (--n >= 0)
|
||||
{
|
||||
blr_indent(control, level);
|
||||
blr_print_word(control);
|
||||
offset = blr_print_line(control, (SSHORT) offset);
|
||||
blr_print_verb(control, level);
|
||||
}
|
||||
|
||||
--level;
|
||||
break;
|
||||
|
||||
case blr_window_win_extent_unit:
|
||||
case blr_window_win_exclusion:
|
||||
blr_print_byte(control);
|
||||
offset = blr_print_line(control, offset);
|
||||
break;
|
||||
|
||||
case blr_window_win_extent_frame_bound:
|
||||
blr_print_byte(control);
|
||||
blr_print_byte(control);
|
||||
offset = blr_print_line(control, offset);
|
||||
break;
|
||||
|
||||
case blr_window_win_extent_frame_value:
|
||||
blr_print_byte(control);
|
||||
offset = blr_print_line(control, offset);
|
||||
++level;
|
||||
blr_print_verb(control, level);
|
||||
--level;
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// print blr_end
|
||||
control->ctl_blr_reader.seekBackward(1);
|
||||
blr_print_verb(control, level);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
break;
|
||||
|
@ -183,6 +183,7 @@ static const TOK tokens[] =
|
||||
{ENTRY_POINT, "ENTRY_POINT", false},
|
||||
{ESCAPE, "ESCAPE", false},
|
||||
{EXCEPTION, "EXCEPTION", false},
|
||||
{EXCLUDE, "EXCLUDE", false},
|
||||
{EXECUTE, "EXECUTE", false},
|
||||
{EXISTS, "EXISTS", false},
|
||||
{EXIT, "EXIT", false},
|
||||
@ -198,6 +199,7 @@ static const TOK tokens[] =
|
||||
{FIRSTNAME, "FIRSTNAME", false},
|
||||
{KW_FLOAT, "FLOAT", false},
|
||||
{FLOOR, "FLOOR", false},
|
||||
{FOLLOWING, "FOLLOWING", false},
|
||||
{FOR, "FOR", false},
|
||||
{FOREIGN, "FOREIGN", false},
|
||||
{FREE_IT, "FREE_IT", true},
|
||||
@ -296,6 +298,7 @@ static const TOK tokens[] =
|
||||
{OR, "OR", false},
|
||||
{ORDER, "ORDER", false},
|
||||
{OS_NAME, "OS_NAME", false},
|
||||
{OTHERS, "OTHERS", false},
|
||||
{OUTER, "OUTER", false},
|
||||
{OUTPUT_TYPE, "OUTPUT_TYPE", false},
|
||||
{OVER, "OVER", false},
|
||||
@ -317,6 +320,7 @@ static const TOK tokens[] =
|
||||
{POSITION, "POSITION", false},
|
||||
{POST_EVENT, "POST_EVENT", false},
|
||||
{POWER, "POWER", false},
|
||||
{PRECEDING, "PRECEDING", false},
|
||||
{PRECISION, "PRECISION", false},
|
||||
{PRESERVE, "PRESERVE", true},
|
||||
{PRIMARY, "PRIMARY", false},
|
||||
@ -326,6 +330,7 @@ static const TOK tokens[] =
|
||||
{PROCEDURE, "PROCEDURE", false},
|
||||
{PROTECTED, "PROTECTED", false},
|
||||
{RAND, "RAND", false},
|
||||
{RANGE, "RANGE", false},
|
||||
{RANK, "RANK", false},
|
||||
{DB_KEY, "RDB$DB_KEY", false},
|
||||
{RDB_GET_CONTEXT, "RDB$GET_CONTEXT", true},
|
||||
@ -420,6 +425,7 @@ static const TOK tokens[] =
|
||||
{TANH, "TANH", false},
|
||||
{TEMPORARY, "TEMPORARY", true},
|
||||
{THEN, "THEN", false},
|
||||
{TIES, "TIES", false},
|
||||
{TIME, "TIME", false},
|
||||
{TIMESTAMP, "TIMESTAMP", false},
|
||||
{TIMEOUT, "TIMEOUT", true},
|
||||
@ -433,6 +439,7 @@ static const TOK tokens[] =
|
||||
{TRUSTED, "TRUSTED", false},
|
||||
{TWO_PHASE, "TWO_PHASE", true},
|
||||
{KW_TYPE, "TYPE", true},
|
||||
{UNBOUNDED, "UNBOUNDED", false},
|
||||
{UNCOMMITTED, "UNCOMMITTED", false},
|
||||
{UNDO, "UNDO", true},
|
||||
{UNION, "UNION", false},
|
||||
|
Loading…
Reference in New Issue
Block a user