mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 09:20:39 +01:00
Feature CORE-5346 - Window Function: named window.
This commit is contained in:
parent
f8382b6369
commit
a9378178fd
@ -293,6 +293,7 @@ Firebird 4.0
|
||||
Added as reserved words:
|
||||
|
||||
UNBOUNDED
|
||||
WINDOW
|
||||
|
||||
Added as non-reserved words:
|
||||
|
||||
|
@ -11,7 +11,10 @@ Syntax:
|
||||
```
|
||||
<window function> ::=
|
||||
<window function name>([<expr> [, <expr> ...]])
|
||||
OVER ([<window partition>] [<window order>] [<window frame>])
|
||||
OVER {<window specification> | <existing window name>}
|
||||
|
||||
<window specification> ::=
|
||||
([<existing window name>] [<window partition>] [<window order>] [<window frame>])
|
||||
|
||||
<window partition> ::=
|
||||
PARTITION BY <expr> [, <expr> ...]
|
||||
@ -39,6 +42,25 @@ Syntax:
|
||||
NULLS {FIRST | LAST}
|
||||
```
|
||||
|
||||
```
|
||||
<query spec> ::=
|
||||
SELECT
|
||||
[<limit clause>]
|
||||
[<distinct clause>]
|
||||
<select list>
|
||||
[<where clause>]
|
||||
[<group clause>]
|
||||
[<having clause>]
|
||||
[<named windows clause>]
|
||||
[<plan clause>]
|
||||
|
||||
<named windows clause> ::=
|
||||
WINDOW <window definition> [, <window definition>] ...
|
||||
|
||||
<window definition> ::=
|
||||
<new window name> AS <window specification>
|
||||
```
|
||||
|
||||
## 1. Aggregate functions used as window functions
|
||||
|
||||
All aggregate functions may be used as window functions, adding the `OVER` clause. Imagine a table EMPLOYEE with columns ID, NAME and SALARY, and the need to show each employee with his respective salary and the percentage of his salary over the payroll. With a "normal" query, this is possible in the following manner:
|
||||
@ -261,6 +283,26 @@ Some window functions discard frames. `ROW_NUMBER`, `LAG` and `LEAD` always work
|
||||
|
||||
`FIRST_VALUE`, `LAST_VALUE` and `NTH_VALUE` respect frames, but the `RANGE` unit works identically as `ROWS`.
|
||||
|
||||
## 6. Named windows (FB 4.0)
|
||||
|
||||
To avoid write repetitive or confusing expressions, windows can be named in a query with the `WINDOW` clause. A named window can be used in `OVER` to reference a window definition and can also be used as a base window of another named or inline (`OVER`) window. A window with frame (`ROWS` or `RANGE` clauses) can't be used as base window (but can be used with `OVER <window name>`). And a window with a base window can't have `PARTITION BY` nor can override `ORDER BY` of a base window.
|
||||
|
||||
Example query with named windows:
|
||||
|
||||
```sql
|
||||
select
|
||||
id,
|
||||
department,
|
||||
salary,
|
||||
count(*) over w1,
|
||||
first_value(salary) over w2,
|
||||
last_value(salary) over w2
|
||||
from employee
|
||||
window w1 as (partition by department),
|
||||
w2 as (w1 order by salary)
|
||||
order by department, salary;
|
||||
```
|
||||
|
||||
|
||||
Author:
|
||||
Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
||||
|
@ -1652,6 +1652,16 @@ C --
|
||||
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__dsql_window_not_found
|
||||
PARAMETER (GDS__dsql_window_not_found = 335545121)
|
||||
INTEGER*4 GDS__dsql_window_cant_overr_part
|
||||
PARAMETER (GDS__dsql_window_cant_overr_part = 335545122)
|
||||
INTEGER*4 GDS__dsql_window_cant_overr_order
|
||||
PARAMETER (GDS__dsql_window_cant_overr_order = 335545123)
|
||||
INTEGER*4 GDS__dsql_window_cant_overr_frame
|
||||
PARAMETER (GDS__dsql_window_cant_overr_frame = 335545124)
|
||||
INTEGER*4 GDS__dsql_window_duplicate
|
||||
PARAMETER (GDS__dsql_window_duplicate = 335545125)
|
||||
INTEGER*4 GDS__gfix_db_name
|
||||
PARAMETER (GDS__gfix_db_name = 335740929)
|
||||
INTEGER*4 GDS__gfix_invalid_sw
|
||||
|
@ -1647,6 +1647,16 @@ const
|
||||
gds_dsql_window_frame_value_inv_type = 335545119;
|
||||
isc_window_frame_value_invalid = 335545120;
|
||||
gds_window_frame_value_invalid = 335545120;
|
||||
isc_dsql_window_not_found = 335545121;
|
||||
gds_dsql_window_not_found = 335545121;
|
||||
isc_dsql_window_cant_overr_part = 335545122;
|
||||
gds_dsql_window_cant_overr_part = 335545122;
|
||||
isc_dsql_window_cant_overr_order = 335545123;
|
||||
gds_dsql_window_cant_overr_order = 335545123;
|
||||
isc_dsql_window_cant_overr_frame = 335545124;
|
||||
gds_dsql_window_cant_overr_frame = 335545124;
|
||||
isc_dsql_window_duplicate = 335545125;
|
||||
gds_dsql_window_duplicate = 335545125;
|
||||
isc_gfix_db_name = 335740929;
|
||||
gds_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
|
@ -267,10 +267,7 @@ ValueExprNode* AggNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (dsqlAggregateFinder(aggFinder))
|
||||
{
|
||||
if (!visitor.window && visitor.dsqlScratch->scopeLevel == aggFinder.deepestLevel)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
return PASS1_post_map(visitor.dsqlScratch, this, visitor.context, visitor.windowNode);
|
||||
}
|
||||
|
||||
for (NodeRef** i = dsqlChildNodes.begin(); i != dsqlChildNodes.end(); ++i)
|
||||
|
@ -5261,10 +5261,7 @@ bool FieldNode::dsqlFieldFinder(FieldFinder& visitor)
|
||||
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.windowNode);
|
||||
}
|
||||
return PASS1_post_map(visitor.dsqlScratch, this, visitor.context, visitor.windowNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -6783,10 +6780,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.windowNode);
|
||||
}
|
||||
return PASS1_post_map(visitor.dsqlScratch, this, visitor.context, visitor.windowNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -6978,10 +6972,7 @@ ValueExprNode* DerivedFieldNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
// the given context (of course only if we're in the same scope-level).
|
||||
|
||||
if (scope == visitor.context->ctx_scope_level)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
return PASS1_post_map(visitor.dsqlScratch, this, visitor.context, visitor.windowNode);
|
||||
else if (visitor.context->ctx_scope_level < scope)
|
||||
doDsqlFieldRemapper(visitor, value);
|
||||
|
||||
@ -7500,10 +7491,50 @@ WindowClause::FrameExtent* WindowClause::FrameExtent::copy(thread_db* tdbb, Node
|
||||
|
||||
WindowClause* WindowClause::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
NestConst<WindowClause> window;
|
||||
|
||||
if (name)
|
||||
{
|
||||
fb_assert(dsqlScratch->context->hasData());
|
||||
dsql_ctx* context = dsqlScratch->context->object();
|
||||
|
||||
if (!context->ctx_named_windows.get(*name, window))
|
||||
{
|
||||
ERRD_post(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_window_not_found) << *name);
|
||||
}
|
||||
|
||||
if (partition)
|
||||
{
|
||||
ERRD_post(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_window_cant_overr_part) << *name);
|
||||
}
|
||||
|
||||
if (order && window->order)
|
||||
{
|
||||
ERRD_post(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_window_cant_overr_order) << *name);
|
||||
}
|
||||
|
||||
if (window->extent)
|
||||
{
|
||||
ERRD_post(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_window_cant_overr_frame) << *name);
|
||||
}
|
||||
}
|
||||
else
|
||||
window = this;
|
||||
|
||||
WindowClause* node = FB_NEW_POOL(getPool()) WindowClause(getPool(),
|
||||
doDsqlPass(dsqlScratch, order),
|
||||
doDsqlPass(dsqlScratch, extent),
|
||||
exclusion);
|
||||
window->name,
|
||||
doDsqlPass(dsqlScratch, window->partition),
|
||||
doDsqlPass(dsqlScratch, window->order),
|
||||
doDsqlPass(dsqlScratch, window->extent),
|
||||
window->exclusion);
|
||||
|
||||
if (node->order && node->extent && node->extent->unit == FrameExtent::UNIT_RANGE &&
|
||||
(node->extent->frame1->value || (node->extent->frame2 && node->extent->frame2->value)))
|
||||
@ -7556,15 +7587,23 @@ WindowClause* WindowClause::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
//--------------------
|
||||
|
||||
|
||||
OverNode::OverNode(MemoryPool& pool, AggNode* aAggExpr, ValueListNode* aPartition,
|
||||
WindowClause* aWindow)
|
||||
OverNode::OverNode(MemoryPool& pool, AggNode* aAggExpr, const MetaName* aWindowName)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_OVER>(pool),
|
||||
aggExpr(aAggExpr),
|
||||
partition(aPartition),
|
||||
windowName(aWindowName),
|
||||
window(NULL)
|
||||
{
|
||||
addDsqlChildNode(aggExpr);
|
||||
addDsqlChildNode(window);
|
||||
}
|
||||
|
||||
OverNode::OverNode(MemoryPool& pool, AggNode* aAggExpr, WindowClause* aWindow)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_OVER>(pool),
|
||||
aggExpr(aAggExpr),
|
||||
windowName(NULL),
|
||||
window(aWindow)
|
||||
{
|
||||
addDsqlChildNode(aggExpr);
|
||||
addDsqlChildNode(partition);
|
||||
addDsqlChildNode(window);
|
||||
}
|
||||
|
||||
@ -7573,7 +7612,6 @@ string OverNode::internalPrint(NodePrinter& printer) const
|
||||
ValueExprNode::internalPrint(printer);
|
||||
|
||||
NODE_PRINT(printer, aggExpr);
|
||||
NODE_PRINT(printer, partition);
|
||||
NODE_PRINT(printer, window);
|
||||
|
||||
return "OverNode";
|
||||
@ -7593,7 +7631,6 @@ bool OverNode::dsqlAggregateFinder(AggregateFinder& visitor)
|
||||
else
|
||||
aggregate |= visitor.visit(aggExpr);
|
||||
|
||||
aggregate |= visitor.visit(partition);
|
||||
aggregate |= visitor.visit(window);
|
||||
|
||||
return aggregate;
|
||||
@ -7608,7 +7645,6 @@ bool OverNode::dsqlAggregate2Finder(Aggregate2Finder& visitor)
|
||||
found |= visitor.visit(aggExpr);
|
||||
}
|
||||
|
||||
found |= visitor.visit(partition);
|
||||
found |= visitor.visit(window);
|
||||
|
||||
return found;
|
||||
@ -7622,7 +7658,6 @@ bool OverNode::dsqlInvalidReferenceFinder(InvalidReferenceFinder& visitor)
|
||||
AutoSetRestore<bool> autoInsideHigherMap(&visitor.insideHigherMap, true);
|
||||
|
||||
invalid |= visitor.visit(aggExpr);
|
||||
invalid |= visitor.visit(partition);
|
||||
invalid |= visitor.visit(window);
|
||||
|
||||
return invalid;
|
||||
@ -7636,33 +7671,27 @@ bool OverNode::dsqlSubSelectFinder(SubSelectFinder& /*visitor*/)
|
||||
ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
// Save the values to restore them in the end.
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode(&visitor.partitionNode, visitor.partitionNode);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode(&visitor.windowNode, visitor.windowNode);
|
||||
|
||||
if (partition)
|
||||
if (window->partition)
|
||||
{
|
||||
if (Aggregate2Finder::find(visitor.context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL,
|
||||
true, partition))
|
||||
true, window->partition))
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||
Arg::Gds(isc_dsql_agg_nested_err));
|
||||
}
|
||||
|
||||
visitor.partitionNode = partition;
|
||||
}
|
||||
|
||||
if (window)
|
||||
if (Aggregate2Finder::find(visitor.context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL,
|
||||
true, window))
|
||||
{
|
||||
if (Aggregate2Finder::find(visitor.context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL,
|
||||
true, window))
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||
Arg::Gds(isc_dsql_agg_nested_err));
|
||||
}
|
||||
|
||||
visitor.windowNode = window;
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||
Arg::Gds(isc_dsql_agg_nested_err));
|
||||
}
|
||||
|
||||
visitor.windowNode = window;
|
||||
|
||||
// Before remap, aggExpr must always be an AggNode;
|
||||
AggNode* aggNode = static_cast<AggNode*>(aggExpr.getObject());
|
||||
|
||||
@ -7685,30 +7714,27 @@ ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (!visitor.window)
|
||||
{
|
||||
{ // scope
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode2(&visitor.partitionNode, NULL);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode2(&visitor.windowNode, NULL);
|
||||
|
||||
for (auto& child : aggNode->dsqlChildNodes)
|
||||
child->remap(visitor);
|
||||
}
|
||||
|
||||
if (partition)
|
||||
if (window->partition)
|
||||
{
|
||||
for (unsigned i = 0; i < partition->items.getCount(); ++i)
|
||||
for (unsigned i = 0; i < window->partition->items.getCount(); ++i)
|
||||
{
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode2(&visitor.partitionNode, NULL);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode2(&visitor.windowNode, NULL);
|
||||
|
||||
doDsqlFieldRemapper(visitor, partition->items[i]);
|
||||
doDsqlFieldRemapper(visitor, window->partition->items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// ASF: I'm not sure if this is enough or frame values needs to be remapped as well.
|
||||
if (window && window->order)
|
||||
if (window->order)
|
||||
{
|
||||
for (unsigned i = 0; i < window->order->items.getCount(); ++i)
|
||||
{
|
||||
AutoSetRestore<ValueListNode*> autoPartitionNode(&visitor.partitionNode, NULL);
|
||||
AutoSetRestore<WindowClause*> autoWindowNode2(&visitor.windowNode, NULL);
|
||||
|
||||
doDsqlFieldRemapper(visitor, window->order->items[i]);
|
||||
@ -7719,7 +7745,7 @@ ValueExprNode* OverNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
|
||||
return PASS1_post_map(visitor.dsqlScratch, aggNode, visitor.context,
|
||||
visitor.partitionNode, visitor.windowNode);
|
||||
visitor.windowNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7761,15 +7787,30 @@ dsc* OverNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
|
||||
ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
NestConst<WindowClause> refWindow;
|
||||
|
||||
if (windowName)
|
||||
{
|
||||
fb_assert(dsqlScratch->context->hasData());
|
||||
dsql_ctx* context = dsqlScratch->context->object();
|
||||
|
||||
if (!context->ctx_named_windows.get(*windowName, refWindow))
|
||||
{
|
||||
ERRD_post(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_window_not_found) << *windowName);
|
||||
}
|
||||
}
|
||||
else
|
||||
refWindow = window;
|
||||
|
||||
OverNode* node = FB_NEW_POOL(getPool()) OverNode(getPool(),
|
||||
static_cast<AggNode*>(doDsqlPass(dsqlScratch, aggExpr)),
|
||||
doDsqlPass(dsqlScratch, partition),
|
||||
doDsqlPass(dsqlScratch, window));
|
||||
static_cast<AggNode*>(doDsqlPass(dsqlScratch, aggExpr)), doDsqlPass(dsqlScratch, refWindow));
|
||||
|
||||
const AggNode* aggNode = node->aggExpr->as<AggNode>();
|
||||
|
||||
if (window &&
|
||||
window->extent &&
|
||||
if (node->window &&
|
||||
node->window->extent &&
|
||||
aggNode &&
|
||||
(aggNode->getCapabilities() & AggNode::CAP_RESPECTS_WINDOW_FRAME) !=
|
||||
AggNode::CAP_RESPECTS_WINDOW_FRAME)
|
||||
@ -8342,8 +8383,7 @@ bool RecordKeyNode::dsqlFieldFinder(FieldFinder& /*visitor*/)
|
||||
|
||||
ValueExprNode* RecordKeyNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
return PASS1_post_map(visitor.dsqlScratch, this, visitor.context, visitor.windowNode);
|
||||
}
|
||||
|
||||
void RecordKeyNode::setParameterName(dsql_par* parameter) const
|
||||
|
@ -1131,13 +1131,20 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
explicit WindowClause(MemoryPool& p, ValueListNode* aOrder, FrameExtent* aFrameExtent,
|
||||
Exclusion aExclusion)
|
||||
explicit WindowClause(MemoryPool& p,
|
||||
const Firebird::MetaName* aName = NULL,
|
||||
ValueListNode* aPartition = NULL,
|
||||
ValueListNode* aOrder = NULL,
|
||||
FrameExtent* aFrameExtent = NULL,
|
||||
Exclusion aExclusion = EXCLUDE_NO_OTHERS)
|
||||
: DsqlNode(p),
|
||||
name(aName),
|
||||
partition(aPartition),
|
||||
order(aOrder),
|
||||
extent(aFrameExtent),
|
||||
exclusion(aExclusion)
|
||||
{
|
||||
addDsqlChildNode(partition);
|
||||
addDsqlChildNode(order);
|
||||
addDsqlChildNode(extent);
|
||||
}
|
||||
@ -1145,6 +1152,7 @@ public:
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, partition);
|
||||
NODE_PRINT(printer, order);
|
||||
NODE_PRINT(printer, extent);
|
||||
|
||||
@ -1183,6 +1191,8 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
const Firebird::MetaName* name;
|
||||
NestConst<ValueListNode> partition;
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<FrameExtent> extent;
|
||||
Exclusion exclusion;
|
||||
@ -1193,8 +1203,8 @@ public:
|
||||
class OverNode : public TypedNode<ValueExprNode, ExprNode::TYPE_OVER>
|
||||
{
|
||||
public:
|
||||
explicit OverNode(MemoryPool& pool, AggNode* aAggExpr = NULL, ValueListNode* aPartition = NULL,
|
||||
WindowClause* aWindow = NULL);
|
||||
explicit OverNode(MemoryPool& pool, AggNode* aAggExpr, const Firebird::MetaName* aWindowName);
|
||||
explicit OverNode(MemoryPool& pool, AggNode* aAggExpr, WindowClause* aWindow);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
@ -1215,7 +1225,7 @@ public:
|
||||
|
||||
public:
|
||||
NestConst<ValueExprNode> aggExpr;
|
||||
NestConst<ValueListNode> partition;
|
||||
const Firebird::MetaName* windowName;
|
||||
NestConst<WindowClause> window;
|
||||
};
|
||||
|
||||
|
@ -151,13 +151,13 @@ class FieldRemapper
|
||||
{
|
||||
public:
|
||||
FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
||||
ValueListNode* aPartitionNode = NULL, WindowClause* aWindowNode = NULL);
|
||||
WindowClause* aWindowNode = NULL);
|
||||
|
||||
static ExprNode* remap(DsqlCompilerScratch* dsqlScratch, dsql_ctx* context, bool window,
|
||||
ExprNode* field, ValueListNode* partitionNode = NULL, WindowClause* windowNode = NULL)
|
||||
ExprNode* field, WindowClause* windowNode = NULL)
|
||||
{
|
||||
// The bool value returned by the visitor is completely discarded in this class.
|
||||
return FieldRemapper(dsqlScratch, context, window, partitionNode, windowNode).visit(field);
|
||||
return FieldRemapper(dsqlScratch, context, window, windowNode).visit(field);
|
||||
}
|
||||
|
||||
ExprNode* visit(ExprNode* node);
|
||||
@ -166,7 +166,6 @@ public:
|
||||
DsqlCompilerScratch* const dsqlScratch;
|
||||
dsql_ctx* const context;
|
||||
const bool window;
|
||||
ValueListNode* partitionNode;
|
||||
WindowClause* windowNode;
|
||||
USHORT currentLevel;
|
||||
};
|
||||
|
@ -95,6 +95,11 @@ namespace Jrd
|
||||
class dsql_intlsym;
|
||||
|
||||
typedef Firebird::Stack<dsql_ctx*> DsqlContextStack;
|
||||
|
||||
typedef Firebird::Pair<Firebird::Left<Firebird::MetaName, NestConst<Jrd::WindowClause> > >
|
||||
NamedWindowClause;
|
||||
|
||||
typedef Firebird::ObjectsArray<NamedWindowClause> NamedWindowsClause;
|
||||
}
|
||||
|
||||
namespace Firebird
|
||||
@ -675,16 +680,14 @@ public:
|
||||
|
||||
struct PartitionMap
|
||||
{
|
||||
PartitionMap(ValueListNode* aPartition, WindowClause* aWindow)
|
||||
: partition(aPartition),
|
||||
partitionRemapped(NULL),
|
||||
PartitionMap(WindowClause* aWindow)
|
||||
: partitionRemapped(NULL),
|
||||
window(aWindow),
|
||||
map(NULL),
|
||||
context(0)
|
||||
{
|
||||
}
|
||||
|
||||
NestConst<ValueListNode> partition;
|
||||
NestConst<ValueListNode> partitionRemapped;
|
||||
NestConst<WindowClause> window;
|
||||
dsql_map* map;
|
||||
@ -701,7 +704,8 @@ public:
|
||||
ctx_main_derived_contexts(p),
|
||||
ctx_childs_derived_table(p),
|
||||
ctx_imp_join(p),
|
||||
ctx_win_maps(p)
|
||||
ctx_win_maps(p),
|
||||
ctx_named_windows(p)
|
||||
{
|
||||
}
|
||||
|
||||
@ -723,6 +727,7 @@ public:
|
||||
Firebird::GenericMap<Firebird::Pair<Firebird::Left<
|
||||
Firebird::MetaName, ImplicitJoin*> > > ctx_imp_join; // Map of USING fieldname to ImplicitJoin
|
||||
Firebird::Array<PartitionMap*> ctx_win_maps; // Maps for window functions
|
||||
Firebird::GenericMap<NamedWindowClause> ctx_named_windows;
|
||||
|
||||
dsql_ctx& operator=(dsql_ctx& v)
|
||||
{
|
||||
@ -742,6 +747,7 @@ public:
|
||||
ctx_childs_derived_table.assign(v.ctx_childs_derived_table);
|
||||
ctx_imp_join.assign(v.ctx_imp_join);
|
||||
ctx_win_maps.assign(v.ctx_win_maps);
|
||||
ctx_named_windows.assign(v.ctx_named_windows);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -756,8 +762,7 @@ public:
|
||||
}
|
||||
|
||||
bool getImplicitJoinField(const Firebird::MetaName& name, NestConst<ValueExprNode>& node);
|
||||
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch,
|
||||
ValueListNode* partitionNode, WindowClause* windowNode);
|
||||
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch, WindowClause* windowNode);
|
||||
};
|
||||
|
||||
// Flag values for ctx_flags
|
||||
|
@ -606,6 +606,7 @@ using namespace Firebird;
|
||||
%token <metaNamePtr> SYSTEM
|
||||
%token <metaNamePtr> TIES
|
||||
%token <metaNamePtr> UNBOUNDED
|
||||
%token <metaNamePtr> WINDOW
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
|
||||
@ -687,6 +688,8 @@ using namespace Firebird;
|
||||
Jrd::DecodeNode* decodeNode;
|
||||
Firebird::Array<Jrd::FieldNode*>* fieldArray;
|
||||
Firebird::Array<NestConst<Jrd::FieldNode> >* nestFieldArray;
|
||||
Jrd::NamedWindowClause* namedWindowClause;
|
||||
Jrd::NamedWindowsClause* namedWindowsClause;
|
||||
Jrd::TransactionNode* traNode;
|
||||
Firebird::Array<Jrd::PrivilegeClause>* privilegeArray;
|
||||
Jrd::GranteeClause* granteeClause;
|
||||
@ -3972,6 +3975,7 @@ keyword_or_column
|
||||
| VAR_SAMP
|
||||
| VAR_POP
|
||||
| UNBOUNDED // added in FB 4.0
|
||||
| WINDOW
|
||||
;
|
||||
|
||||
col_opt
|
||||
@ -5198,6 +5202,7 @@ query_spec
|
||||
where_clause
|
||||
group_clause
|
||||
having_clause
|
||||
named_windows_clause
|
||||
plan_clause
|
||||
{
|
||||
RseNode* rse = newNode<RseNode>();
|
||||
@ -5209,7 +5214,8 @@ query_spec
|
||||
rse->dsqlWhere = $6;
|
||||
rse->dsqlGroup = $7;
|
||||
rse->dsqlHaving = $8;
|
||||
rse->rse_plan = $9;
|
||||
rse->dsqlNamedWindows = $9;
|
||||
rse->rse_plan = $10;
|
||||
$$ = rse;
|
||||
}
|
||||
;
|
||||
@ -5512,6 +5518,42 @@ where_clause
|
||||
| WHERE search_condition { $$ = $2; }
|
||||
;
|
||||
|
||||
%type <namedWindowsClause> named_windows_clause
|
||||
named_windows_clause
|
||||
: /* nothing */ { $$ = NULL; }
|
||||
| WINDOW window_definition_list { $$ = $2; }
|
||||
;
|
||||
|
||||
%type <namedWindowsClause> window_definition_list
|
||||
window_definition_list
|
||||
: window_definition
|
||||
{
|
||||
NamedWindowsClause* node = newNode<NamedWindowsClause>();
|
||||
node->add(*$1);
|
||||
$$ = node;
|
||||
}
|
||||
| window_definition_list ',' window_definition
|
||||
{
|
||||
NamedWindowsClause* node = $1;
|
||||
node->add(*$3);
|
||||
$$ = node;
|
||||
}
|
||||
;
|
||||
|
||||
%type <namedWindowClause> window_definition
|
||||
window_definition
|
||||
: symbol_window_name AS '(' window_clause ')'
|
||||
{
|
||||
$$ = newNode<NamedWindowClause>(*$1, $4);
|
||||
}
|
||||
;
|
||||
|
||||
%type <metaNamePtr> symbol_window_name_opt
|
||||
symbol_window_name_opt
|
||||
: /* nothing */ { $$ = NULL; }
|
||||
| symbol_window_name
|
||||
;
|
||||
|
||||
|
||||
// PLAN clause to specify an access plan for a query
|
||||
|
||||
@ -7075,8 +7117,22 @@ aggregate_window_function
|
||||
|
||||
%type <valueExprNode> over_clause
|
||||
over_clause
|
||||
: aggregate_window_function OVER '(' window_partition_opt window_clause ')'
|
||||
{ $$ = newNode<OverNode>($1, $4, $5); }
|
||||
: aggregate_window_function OVER symbol_window_name
|
||||
{ $$ = newNode<OverNode>($1, $3); }
|
||||
| aggregate_window_function OVER '(' window_clause ')'
|
||||
{ $$ = newNode<OverNode>($1, $4); }
|
||||
;
|
||||
|
||||
%type <windowClause> window_clause
|
||||
window_clause
|
||||
: symbol_window_name_opt
|
||||
window_partition_opt
|
||||
order_clause_opt
|
||||
window_frame_extent
|
||||
window_frame_exclusion_opt
|
||||
{
|
||||
$$ = newNode<WindowClause>($1, $2, $3, $4, $5);
|
||||
}
|
||||
;
|
||||
|
||||
%type <valueListNode> window_partition_opt
|
||||
@ -7085,14 +7141,6 @@ 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 */
|
||||
@ -7737,6 +7785,11 @@ symbol_package_name
|
||||
: valid_symbol_name
|
||||
;
|
||||
|
||||
%type <metaNamePtr> symbol_window_name
|
||||
symbol_window_name
|
||||
: valid_symbol_name
|
||||
;
|
||||
|
||||
// symbols
|
||||
|
||||
%type <metaNamePtr> valid_symbol_name
|
||||
|
@ -300,11 +300,10 @@ bool InvalidReferenceFinder::visit(ExprNode* node)
|
||||
|
||||
|
||||
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
||||
ValueListNode* aPartitionNode, WindowClause* aWindowNode)
|
||||
WindowClause* aWindowNode)
|
||||
: dsqlScratch(aDsqlScratch),
|
||||
context(aContext),
|
||||
window(aWindow),
|
||||
partitionNode(aPartitionNode),
|
||||
windowNode(aWindowNode),
|
||||
currentLevel(dsqlScratch->scopeLevel)
|
||||
{
|
||||
@ -1883,6 +1882,24 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod
|
||||
Arg::Gds(isc_dsql_count_mismatch));
|
||||
}
|
||||
|
||||
if (inputRse->dsqlNamedWindows)
|
||||
{
|
||||
for (NamedWindowsClause::iterator i = inputRse->dsqlNamedWindows->begin();
|
||||
i != inputRse->dsqlNamedWindows->end();
|
||||
++i)
|
||||
{
|
||||
if (dsqlScratch->context->object()->ctx_named_windows.exist(i->first))
|
||||
{
|
||||
ERRD_post(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_window_duplicate) << i->first);
|
||||
}
|
||||
|
||||
i->second->dsqlPass(dsqlScratch);
|
||||
dsqlScratch->context->object()->ctx_named_windows.put(i->first, i->second);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass select list
|
||||
rse->dsqlSelectList = pass1_sel_list(dsqlScratch, selectList);
|
||||
--dsqlScratch->inSelectList;
|
||||
@ -2105,11 +2122,12 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod
|
||||
parent_context->ctx_context = dsqlScratch->contextNumber++;
|
||||
}
|
||||
|
||||
const bool sortWindow = rse->dsqlOrder && AggregateFinder::find(dsqlScratch, true, rse->dsqlOrder);
|
||||
bool isWindow = (rse->dsqlOrder && AggregateFinder::find(dsqlScratch, true, rse->dsqlOrder)) ||
|
||||
(rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, true, rse->dsqlSelectList)) ||
|
||||
inputRse->dsqlNamedWindows;
|
||||
|
||||
// WINDOW functions
|
||||
if ((rse->dsqlSelectList && AggregateFinder::find(dsqlScratch, true, rse->dsqlSelectList)) ||
|
||||
sortWindow)
|
||||
if (isWindow)
|
||||
{
|
||||
AutoSetRestore<bool> autoProcessingWindow(&dsqlScratch->processingWindow, true);
|
||||
|
||||
@ -2208,12 +2226,13 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod
|
||||
for (FB_SIZE_T i = 0, mapCount = parent_context->ctx_win_maps.getCount(); i < mapCount; ++i)
|
||||
{
|
||||
PartitionMap* partitionMap = parent_context->ctx_win_maps[i];
|
||||
if (partitionMap->partition)
|
||||
{
|
||||
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch, partitionMap->partition);
|
||||
|
||||
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->partition,
|
||||
partitionMap->window);
|
||||
if (partitionMap->window && partitionMap->window->partition)
|
||||
{
|
||||
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch,
|
||||
partitionMap->window->partition);
|
||||
|
||||
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->window);
|
||||
ExprNode::doDsqlFieldRemapper(remapper2, partitionMap->partitionRemapped);
|
||||
}
|
||||
}
|
||||
@ -2801,8 +2820,8 @@ 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, WindowClause* windowNode)
|
||||
DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node,
|
||||
dsql_ctx* context, WindowClause* windowNode)
|
||||
{
|
||||
DEV_BLKCHK(node, dsql_type_nod);
|
||||
DEV_BLKCHK(context, dsql_type_ctx);
|
||||
@ -2814,7 +2833,7 @@ DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* nod
|
||||
|
||||
if (dsqlScratch->processingWindow)
|
||||
{
|
||||
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, windowNode);
|
||||
partitionMap = context->getPartitionMap(dsqlScratch, windowNode);
|
||||
map = partitionMap->map;
|
||||
}
|
||||
else
|
||||
@ -2930,10 +2949,16 @@ 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,
|
||||
WindowClause* windowNode)
|
||||
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, WindowClause* windowNode)
|
||||
{
|
||||
thread_db* tdbb = JRD_get_thread_data();
|
||||
MemoryPool& pool = *tdbb->getDefaultPool();
|
||||
|
||||
bool isNullWindow = windowNode == NULL;
|
||||
WindowClause nullWindow(pool, NULL, NULL, NULL, NULL, WindowClause::EXCLUDE_NO_OTHERS);
|
||||
|
||||
if (isNullWindow)
|
||||
windowNode = &nullWindow;
|
||||
|
||||
PartitionMap* partitionMap = NULL;
|
||||
|
||||
@ -2941,8 +2966,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
!partitionMap && i != ctx_win_maps.end();
|
||||
++i)
|
||||
{
|
||||
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
|
||||
PASS1_node_match((*i)->window, windowNode, false))
|
||||
if (PASS1_node_match((*i)->window, windowNode, false))
|
||||
{
|
||||
partitionMap = *i;
|
||||
}
|
||||
@ -2950,7 +2974,13 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
|
||||
if (!partitionMap)
|
||||
{
|
||||
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, windowNode);
|
||||
if (isNullWindow)
|
||||
{
|
||||
windowNode = FB_NEW_POOL(pool) WindowClause(pool, NULL, NULL, NULL, NULL,
|
||||
WindowClause::EXCLUDE_NO_OTHERS);
|
||||
}
|
||||
|
||||
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(windowNode);
|
||||
ctx_win_maps.add(partitionMap);
|
||||
partitionMap->context = dsqlScratch->contextNumber++;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ Jrd::ValueExprNode* PASS1_lookup_alias(Jrd::DsqlCompilerScratch*, const Firebird
|
||||
Jrd::dsql_ctx* PASS1_make_context(Jrd::DsqlCompilerScratch* statement, Jrd::RecordSourceNode* relationNode);
|
||||
bool PASS1_node_match(const Jrd::ExprNode*, const Jrd::ExprNode*, bool);
|
||||
Jrd::DsqlMapNode* PASS1_post_map(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, Jrd::dsql_ctx*,
|
||||
Jrd::ValueListNode*, Jrd::WindowClause*);
|
||||
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);
|
||||
|
@ -822,6 +822,11 @@ static const struct {
|
||||
{"dsql_window_range_inv_key_type", 335545118},
|
||||
{"dsql_window_frame_value_inv_type", 335545119},
|
||||
{"window_frame_value_invalid", 335545120},
|
||||
{"dsql_window_not_found", 335545121},
|
||||
{"dsql_window_cant_overr_part", 335545122},
|
||||
{"dsql_window_cant_overr_order", 335545123},
|
||||
{"dsql_window_cant_overr_frame", 335545124},
|
||||
{"dsql_window_duplicate", 335545125},
|
||||
{"gfix_db_name", 335740929},
|
||||
{"gfix_invalid_sw", 335740930},
|
||||
{"gfix_incmp_sw", 335740932},
|
||||
|
@ -856,6 +856,11 @@ 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_dsql_window_not_found = 335545121L;
|
||||
const ISC_STATUS isc_dsql_window_cant_overr_part = 335545122L;
|
||||
const ISC_STATUS isc_dsql_window_cant_overr_order = 335545123L;
|
||||
const ISC_STATUS isc_dsql_window_cant_overr_frame = 335545124L;
|
||||
const ISC_STATUS isc_dsql_window_duplicate = 335545125L;
|
||||
const ISC_STATUS isc_gfix_db_name = 335740929L;
|
||||
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
|
||||
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
|
||||
@ -1327,7 +1332,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 = 1271;
|
||||
const ISC_STATUS isc_err_max = 1276;
|
||||
|
||||
#else /* c definitions */
|
||||
|
||||
@ -2153,6 +2158,11 @@ const ISC_STATUS isc_err_max = 1271;
|
||||
#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_dsql_window_not_found 335545121L
|
||||
#define isc_dsql_window_cant_overr_part 335545122L
|
||||
#define isc_dsql_window_cant_overr_order 335545123L
|
||||
#define isc_dsql_window_cant_overr_frame 335545124L
|
||||
#define isc_dsql_window_duplicate 335545125L
|
||||
#define isc_gfix_db_name 335740929L
|
||||
#define isc_gfix_invalid_sw 335740930L
|
||||
#define isc_gfix_incmp_sw 335740932L
|
||||
@ -2624,7 +2634,7 @@ const ISC_STATUS isc_err_max = 1271;
|
||||
#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 1271
|
||||
#define isc_err_max 1276
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -825,6 +825,11 @@ Data source : @4"}, /* eds_statement */
|
||||
{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 */
|
||||
{335545121, "Window @1 not found"}, /* dsql_window_not_found */
|
||||
{335545122, "Cannot use PARTITION BY clause while overriding the window @1"}, /* dsql_window_cant_overr_part */
|
||||
{335545123, "Cannot use ORDER BY clause while overriding the window @1 which already has an ORDER BY clause"}, /* dsql_window_cant_overr_order */
|
||||
{335545124, "Cannot override the window @1 because it has a frame clause. Tip: it can be used without parenthesis in OVER"}, /* dsql_window_cant_overr_frame */
|
||||
{335545125, "Duplicate window definition for @1"}, /* dsql_window_duplicate */
|
||||
{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 */
|
||||
|
@ -821,6 +821,11 @@ static const struct {
|
||||
{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 */
|
||||
{335545121, -833}, /* 801 dsql_window_not_found */
|
||||
{335545122, -833}, /* 802 dsql_window_cant_overr_part */
|
||||
{335545123, -833}, /* 803 dsql_window_cant_overr_order */
|
||||
{335545124, -833}, /* 804 dsql_window_cant_overr_frame */
|
||||
{335545125, -833}, /* 805 dsql_window_duplicate */
|
||||
{335740929, -901}, /* 1 gfix_db_name */
|
||||
{335740930, -901}, /* 2 gfix_invalid_sw */
|
||||
{335740932, -901}, /* 4 gfix_incmp_sw */
|
||||
|
@ -821,6 +821,11 @@ static const struct {
|
||||
{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
|
||||
{335545121, "42000"}, // 801 dsql_window_not_found
|
||||
{335545122, "42000"}, // 802 dsql_window_cant_overr_part
|
||||
{335545123, "42000"}, // 803 dsql_window_cant_overr_order
|
||||
{335545124, "42000"}, // 804 dsql_window_cant_overr_frame
|
||||
{335545125, "42000"}, // 805 dsql_window_duplicate
|
||||
{335740929, "00000"}, // 1 gfix_db_name
|
||||
{335740930, "00000"}, // 2 gfix_invalid_sw
|
||||
{335740932, "00000"}, // 4 gfix_incmp_sw
|
||||
|
@ -1333,7 +1333,7 @@ void AggregateSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
((*i)->window->extent ||
|
||||
(*i)->window->exclusion != WindowClause::EXCLUDE_NO_OTHERS));
|
||||
|
||||
ValueListNode* partition = (*i)->partition;
|
||||
ValueListNode* partition = (*i)->window ? (*i)->window->partition : NULL;
|
||||
ValueListNode* partitionRemapped = (*i)->partitionRemapped;
|
||||
ValueListNode* order = (*i)->window ? (*i)->window->order : NULL;
|
||||
|
||||
|
@ -639,6 +639,7 @@ public:
|
||||
dsqlJoinUsing(NULL),
|
||||
dsqlGroup(NULL),
|
||||
dsqlHaving(NULL),
|
||||
dsqlNamedWindows(NULL),
|
||||
dsqlOrder(NULL),
|
||||
dsqlStreams(NULL),
|
||||
dsqlExplicitJoin(false),
|
||||
@ -670,6 +671,7 @@ public:
|
||||
obj->dsqlJoinUsing = dsqlJoinUsing;
|
||||
obj->dsqlGroup = dsqlGroup;
|
||||
obj->dsqlHaving = dsqlHaving;
|
||||
obj->dsqlNamedWindows = dsqlNamedWindows;
|
||||
obj->dsqlOrder = dsqlOrder;
|
||||
obj->dsqlStreams = dsqlStreams;
|
||||
obj->dsqlContext = dsqlContext;
|
||||
@ -739,6 +741,7 @@ public:
|
||||
NestConst<ValueListNode> dsqlJoinUsing;
|
||||
NestConst<ValueListNode> dsqlGroup;
|
||||
NestConst<BoolExprNode> dsqlHaving;
|
||||
NamedWindowsClause* dsqlNamedWindows;
|
||||
NestConst<ValueListNode> dsqlOrder;
|
||||
NestConst<RecSourceListNode> dsqlStreams;
|
||||
bool dsqlExplicitJoin;
|
||||
|
@ -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-09-02 12:40:00', 'JRD', 0, 801)
|
||||
('2016-09-23 12:40:00', 'JRD', 0, 806)
|
||||
('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)
|
||||
|
@ -908,6 +908,11 @@ Data source : @4', 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);
|
||||
('dsql_window_not_found', NULL, 'ExprNodes.cpp', NULL, 0, 801, NULL, 'Window @1 not found', NULL, NULL);
|
||||
('dsql_window_cant_overr_part', NULL, 'ExprNodes.cpp', NULL, 0, 802, NULL, 'Cannot use PARTITION BY clause while overriding the window @1', NULL, NULL);
|
||||
('dsql_window_cant_overr_order', NULL, 'ExprNodes.cpp', NULL, 0, 803, NULL, 'Cannot use ORDER BY clause while overriding the window @1 which already has an ORDER BY clause', NULL, NULL);
|
||||
('dsql_window_cant_overr_frame', NULL, 'ExprNodes.cpp', NULL, 0, 804, NULL, 'Cannot override the window @1 because it has a frame clause. Tip: it can be used without parenthesis in OVER', NULL, NULL);
|
||||
('dsql_window_duplicate', NULL, 'ExprNodes.cpp', NULL, 0, 805, NULL, 'Duplicate window definition for @1', 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);
|
||||
|
@ -807,6 +807,11 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
|
||||
(-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)
|
||||
(-833, '42', '000', 0, 801, 'dsql_window_not_found', NULL, NULL)
|
||||
(-833, '42', '000', 0, 802, 'dsql_window_cant_overr_part', NULL, NULL)
|
||||
(-833, '42', '000', 0, 803, 'dsql_window_cant_overr_order', NULL, NULL)
|
||||
(-833, '42', '000', 0, 804, 'dsql_window_cant_overr_frame', NULL, NULL)
|
||||
(-833, '42', '000', 0, 805, 'dsql_window_duplicate', NULL, NULL)
|
||||
-- GFIX
|
||||
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
|
||||
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)
|
||||
|
@ -467,6 +467,7 @@ static const TOK tokens[] =
|
||||
{WHEN, "WHEN", false},
|
||||
{WHERE, "WHERE", false},
|
||||
{WHILE, "WHILE", false},
|
||||
{WINDOW, "WINDOW", false},
|
||||
{WITH, "WITH", false},
|
||||
{WORK, "WORK", false},
|
||||
{WRITE, "WRITE", false},
|
||||
|
Loading…
Reference in New Issue
Block a user