8
0
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:
Adriano dos Santos Fernandes 2016-09-02 15:20:01 -03:00
parent 68bbdc1802
commit 41d24deb26
38 changed files with 2514 additions and 1368 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;
};

View File

@ -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;
}
};

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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
;
%%

View File

@ -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++;
}

View File

@ -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);

View File

@ -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},

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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);

View File

@ -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>

View File

@ -241,5 +241,6 @@ static const struct
{"subfunc", function},
{"record_version2", byte_line},
{"gen_id2", gen_id2}, // 210
{"window_win", window_win},
{0, 0}
};

View File

@ -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

View File

@ -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();

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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},