mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 09:20:39 +01:00
Merge remote-tracking branch 'origin/master' into work/c++11
This commit is contained in:
commit
62d2a001f8
@ -457,8 +457,8 @@ ISC_PASSWD=$NewPasswd
|
||||
#
|
||||
# generated on $FB_HOST at time $FB_TIME
|
||||
#
|
||||
# Your password can be changed to a more suitable one using the
|
||||
# @FB_BINDIR@/gsec utility.
|
||||
# Your password can be changed to a more suitable one using
|
||||
# SQL operator ALTER USER.
|
||||
#
|
||||
EOT
|
||||
|
||||
|
@ -86,6 +86,7 @@ if DEFINED VS100COMNTOOLS (
|
||||
@set FB_GEN_DIR=%FB_ROOT_PATH%\gen
|
||||
@set FB_GEN_DB_DIR=%FB_DB_PATH%/gen
|
||||
@set FB_ICU_SOURCE_BIN=%FB_ROOT_PATH%\extern\icu\%FB_TARGET_PLATFORM%\release\bin\
|
||||
@set FIREBIRD_BOOT_BUILD=1
|
||||
|
||||
|
||||
|
||||
|
@ -7,12 +7,12 @@
|
||||
<META NAME="AUTHOR" CONTENT="alex ">
|
||||
<META NAME="CREATED" CONTENT="20130531;10003100">
|
||||
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
|
||||
<META NAME="CHANGED" CONTENT="20160407;18553100">
|
||||
<META NAME="CHANGED" CONTENT="20160905;13142600">
|
||||
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
|
||||
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
|
||||
<STYLE TYPE="text/css">
|
||||
<!--
|
||||
@page { size: 8.5in 11in; margin: 0.79in }
|
||||
TD P { margin-bottom: 0.08in; color: #000000 }
|
||||
H1 { color: #000000 }
|
||||
P { margin-bottom: 0.08in; color: #000000 }
|
||||
-->
|
||||
@ -519,7 +519,7 @@ cycle:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>while
|
||||
(curs->fetchNext(&status, buffer) == IStatus::RESULT_OK)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>// row processing</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>// row processing</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
@ -587,43 +587,43 @@ namespace Firebird), timestamp – with class FbTimestamp, containing
|
||||
two public data members date and time of appropriate class, char -
|
||||
with struct <A HREF="#FbChar">FbChar</A> and varchar – with struct
|
||||
<A HREF="#FbVarChar">FbVarChar</A>. For each field preprocessor
|
||||
creates two data members in the message – <I>name</I> for
|
||||
field/parameter value and <I>nameNull</I> for NULL indicator. Message
|
||||
constructor has 2 parameters – pointer to status wrapper and master
|
||||
interface:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>FB_MESSAGE(Output,
|
||||
creates two data members in the message – </FONT><FONT SIZE=4><I>name</I></FONT><FONT SIZE=4>
|
||||
for field/parameter value and </FONT><FONT SIZE=4><I>nameNull</I></FONT><FONT SIZE=4>
|
||||
for NULL indicator. Message constructor has 2 parameters – pointer
|
||||
to status wrapper and master interface:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>FB_MESSAGE(Output,
|
||||
ThrowStatusWrapper,</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>(FB_SMALLINT,
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>(FB_SMALLINT,
|
||||
relationId)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>(FB_CHAR(31),
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>(FB_CHAR(31),
|
||||
relationName)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>(FB_VARCHAR(100),
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>(FB_VARCHAR(100),
|
||||
description)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>)
|
||||
output(&status, master);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>) output(&status,
|
||||
master);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>For static messages use of
|
||||
FB_MESSAGE is sooner of all the best choice – they can be at the
|
||||
same time easily passed to execute, openCursor and fetch methods:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>rs =
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>rs =
|
||||
att->openCursor(&status, tra, 0, sqlText,</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>SQL_DIALECT_V6,
|
||||
NULL, NULL, output.getMetadata(), NULL, 0);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>SQL_DIALECT_V6, NULL,
|
||||
NULL, output.getMetadata(), NULL, 0);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>and used to work with
|
||||
values of individual fields:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>while
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>while
|
||||
(rs->fetchNext(&status, output.getData()) ==
|
||||
IStatus::RESULT_OK)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>printf("%4d
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>printf("%4d
|
||||
%31.31s %*.*s\n", output->relationId,
|
||||
output->relationName.str,</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>output->descriptionNull
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>output->descriptionNull
|
||||
? 0 : output->description.length,</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>output->descriptionNull
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>output->descriptionNull
|
||||
? 0 : output->description.length, output->description.str);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>An example of using macro
|
||||
@ -663,9 +663,8 @@ blobFieldNumber)];</I></FONT></P>
|
||||
and FB_MESSAGE macro blob field is declared as having FB_BLOB type:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>FB_MESSAGE(Msg,
|
||||
ThrowStatusWrapper,</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>(FB_BLOB,
|
||||
b)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>) message(&status,
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>(FB_BLOB, b)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>) message(&status,
|
||||
master);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>ISC_QUAD* blobPtr =
|
||||
&message->b;</I></FONT></P>
|
||||
@ -687,7 +686,7 @@ engine:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>unsigned segmentLength;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>while
|
||||
(userFunctionProvidingBlobData(&segmentData, &segmentLength))</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>blob->putSegment(&status,
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>blob->putSegment(&status,
|
||||
segmentLength, segmentData);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>After sending some data to
|
||||
blob do not forget to close blob interface:</FONT></P>
|
||||
@ -707,32 +706,30 @@ may be done using fetch() or execute() methods. After it use
|
||||
openBlob() attachment's method:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I><A HREF="#Blob">IBlob</A>*
|
||||
blob = att->openBlob(status, tra, blobPtr, 0, NULL);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Blob interface is ready
|
||||
to provide blob data. Use getSegment() method to receive data from
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Blob interface is ready to
|
||||
provide blob data. Use getSegment() method to receive data from
|
||||
engine:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>char buffer[BUFSIZE];</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>unsigned actualLength;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>for(;;)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>switch
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>switch
|
||||
(blob->getSegment(&status, sizeof(buffer), buffer,
|
||||
&actualLength))</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>case
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>case
|
||||
IStatus::RESULT_OK:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in">
|
||||
<FONT SIZE=4><I>userFunctionAcceptingBlobData(buffer, actualLength,
|
||||
true);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>continue;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>case
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>userFunctionAcceptingBlobData(buffer,
|
||||
actualLength, true);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>continue;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>case
|
||||
IStatus::RESULT_SEGMENT:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in">
|
||||
<FONT SIZE=4><I>userFunctionAcceptingBlobData(buffer, actualLength,
|
||||
false);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>continue;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>default:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>break;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>userFunctionAcceptingBlobData(buffer,
|
||||
actualLength, false);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>continue;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>default:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>break;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Last parameter in
|
||||
userFunctionAcceptingBlobData() is a flag that end of segment is
|
||||
@ -751,23 +748,25 @@ completed in FB3, we expect to have something more interesting in
|
||||
next version. The minimum existing support is as follows: <A HREF="#Attachment">IAttachment</A>
|
||||
contains call queEvents() which performs almost same functions as
|
||||
isc_que_events() call. Instead the pair of parameters
|
||||
<I>FPTR_EVENT_CALLBACK ast</I> and <I>void* arg</I>, required to
|
||||
invoke user code when event happens in firebird engine, callback
|
||||
interface IEventCallback is used. This is traditional approach which
|
||||
helps to avoid non-safe casts from void* in user function. Another
|
||||
important difference is that instead event identifier (a kind of
|
||||
handler) this function returns reference counted interface <A HREF="#Events">IEvents</A>
|
||||
having method cancel() used when waiting for event should be stopped.
|
||||
Unlike identifier which is automatically destroyed when event arrives
|
||||
</FONT><FONT SIZE=4><I>FPTR_EVENT_CALLBACK ast</I></FONT><FONT SIZE=4>
|
||||
and </FONT><FONT SIZE=4><I>void* arg</I></FONT><FONT SIZE=4>,
|
||||
required to invoke user code when event happens in firebird engine,
|
||||
callback interface IEventCallback is used. This is traditional
|
||||
approach which helps to avoid non-safe casts from void* in user
|
||||
function. Another important difference is that instead event
|
||||
identifier (a kind of handler) this function returns reference
|
||||
counted interface <A HREF="#Events">IEvents</A> having method
|
||||
cancel() used when waiting for event should be stopped. Unlike
|
||||
identifier which is automatically destroyed when event arrives
|
||||
interface can not be automatically destroyed – in case when event
|
||||
is received right before canceling interface call to cancel() would
|
||||
cause segfault when interface is already destroyed. Therefore
|
||||
interface <A HREF="#Events">IEvents</A> must be explicitly released
|
||||
after receiving an event. This may be done for example right before
|
||||
queuing for an event next time:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>events->release();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>events = NULL;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>events =
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>events->release();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>events = NULL;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>events =
|
||||
attachment->queEvents(&status, this, eveLen, eveBuffer);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Setting interface pointer
|
||||
to NULL is useful in case of exception during queEvents. In other
|
||||
@ -780,7 +779,7 @@ use for additional details our sample 08.events.cpp. </FONT>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>To begin to use services
|
||||
one should first of all connect to service manager. This is done
|
||||
using attachServiceManager() method of <A HREF="#Provider">IProvider</A>.
|
||||
This method returns <A HREF="#Service">IService</A> interface which
|
||||
This method returns <A HREF="#Service">IService</A> interface which
|
||||
is used later to talk to service. To prepare SPB to attach to service
|
||||
manager one can use IXpbBuilder:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>IXpbBuilder* spb1 =
|
||||
@ -830,7 +829,7 @@ spb2->getBufferLength(&status), spb2->getBuffer(&status));</FONT><
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Many started services
|
||||
(including mentioned here gstat) return text information during
|
||||
execution. To display it one should query started service anout that
|
||||
information line by line. This is done by calling query() method of
|
||||
information line by line. This is done by calling query() method of
|
||||
<A HREF="#Service">IService</A> interface with appropriate send and
|
||||
receive blocks of parameters. Send block may contain various helper
|
||||
information (like timeout when querying service) or information to be
|
||||
@ -848,7 +847,7 @@ steps we are ready to query service in a loop (each line returned in
|
||||
a single call to query()):</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>do</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>svc->query(&status,
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>svc->query(&status,
|
||||
0, NULL, sizeof(receiveItems1), receiveItems1, sizeof(results),
|
||||
results);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>} while
|
||||
@ -890,13 +889,13 @@ services tasks do not forget to close an interface:</FONT></P>
|
||||
<FONT FACE="Albany, sans-serif"><FONT SIZE=5>Writing plugins.</FONT></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>To write a plugin means to
|
||||
implement some interfaces and place your implementation into dynamic
|
||||
library (.dll in windows or .so in linux) later referenced as <I>plugin
|
||||
module</I> or just <I>module</I>. In most cases single plugin is
|
||||
place in dynamic library but in common case. One of that interfaces –
|
||||
<A HREF="#PluginModule">IPluginModule</A> – is module-wide (as more
|
||||
or less clear from it's name), others are per plugin. Also each
|
||||
plugin module should contain special exported entrypoint
|
||||
firebird_plugin() which name is defined in include file
|
||||
library (.dll in windows or .so in linux) later referenced as </FONT><FONT SIZE=4><I>plugin
|
||||
module</I></FONT><FONT SIZE=4> or just </FONT><FONT SIZE=4><I>module</I></FONT><FONT SIZE=4>.
|
||||
In most cases single plugin is place in dynamic library but in common
|
||||
case. One of that interfaces – <A HREF="#PluginModule">IPluginModule</A>
|
||||
– is module-wide (as more or less clear from it's name), others are
|
||||
per plugin. Also each plugin module should contain special exported
|
||||
entrypoint firebird_plugin() which name is defined in include file
|
||||
firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>In previous part of this
|
||||
text we were mostly describing how to use existing interfaces, here
|
||||
@ -915,12 +914,13 @@ sample yourself and learn it when reading later.</FONT></P>
|
||||
</P>
|
||||
<H1><FONT SIZE=4>Implementation of plugin module.</FONT></H1>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Plugins actively interact
|
||||
with special firebird component called <I>plugin manager</I>. In
|
||||
particular plugin manager should be aware what plugin modules were
|
||||
loaded and must be notified if operating system tries to unload one
|
||||
of that modules without explicit plugin manager command (this may
|
||||
happen first of all when using embedded access – when exit() is
|
||||
called in a program or main firebird library <I><SPAN STYLE="font-weight: normal">fbclient</SPAN></I>
|
||||
with special firebird component called </FONT><FONT SIZE=4><I>plugin
|
||||
manager</I></FONT><FONT SIZE=4>. In particular plugin manager should
|
||||
be aware what plugin modules were loaded and must be notified if
|
||||
operating system tries to unload one of that modules without explicit
|
||||
plugin manager command (this may happen first of all when using
|
||||
embedded access – when exit() is called in a program or main
|
||||
firebird library </FONT><FONT SIZE=4><I><SPAN STYLE="font-weight: normal">fbclient</SPAN></I></FONT><FONT SIZE=4>
|
||||
is unloaded). Primary task of IPluginModule interface is that
|
||||
notification. First of all one must decide - how to detect that
|
||||
module is going to be unloaded? When dynamic library is unloaded for
|
||||
@ -943,44 +943,38 @@ looks as follows:</FONT></P>
|
||||
public IPluginModuleImpl<PluginModule, CheckStatusWrapper></I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>private:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>IPluginManager*
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>IPluginManager*
|
||||
pluginManager;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>public:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>PluginModule()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>:
|
||||
pluginManager(NULL)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{ }</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>PluginModule()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>: pluginManager(NULL)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{ }</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>~PluginModule()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>if
|
||||
(pluginManager)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in">
|
||||
<FONT SIZE=4><I>pluginManager->unregisterModule(this);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>doClean();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>~PluginModule()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>if (pluginManager)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>pluginManager->unregisterModule(this);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>doClean();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>void
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>void
|
||||
registerMe(IPluginManager* m)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>pluginManager =
|
||||
m;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in">
|
||||
<FONT SIZE=4><I>pluginManager->registerModule(this);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>pluginManager = m;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>pluginManager->registerModule(this);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>void doClean()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>pluginManager
|
||||
= NULL;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>void doClean()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>pluginManager = NULL;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>};</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
@ -989,15 +983,15 @@ plugin manager interface <A HREF="#PluginManager">IPluginManager</A>.
|
||||
It's passed to registerModule() function and saved in private
|
||||
variable, at the same time module is registered in plugin manager by
|
||||
the call to registerModule() method with own address as a single
|
||||
parameter. Variable <I>pluginManager </I>not only stores pointer to
|
||||
interface, at the same time it serves as a flag that module is
|
||||
registered. When destructor of registered module is invoked it
|
||||
notifies plugin manager (yes, this is what for this class exists!)
|
||||
about unexpected unload by the call to unregisterModule() passing
|
||||
pointer to itself. When plugin manager is going to unload module in
|
||||
regular way in first of all calls doClean() method changing module
|
||||
state to unregistered and this avoiding call to unregisterModule()
|
||||
when OS performs actual unload.</FONT></P>
|
||||
parameter. Variable </FONT><FONT SIZE=4><I>pluginManager </I></FONT><FONT SIZE=4>not
|
||||
only stores pointer to interface, at the same time it serves as a
|
||||
flag that module is registered. When destructor of registered module
|
||||
is invoked it notifies plugin manager (yes, this is what for this
|
||||
class exists!) about unexpected unload by the call to
|
||||
unregisterModule() passing pointer to itself. When plugin manager is
|
||||
going to unload module in regular way in first of all calls doClean()
|
||||
method changing module state to unregistered and this avoiding call
|
||||
to unregisterModule() when OS performs actual unload.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Implementing plugin's
|
||||
@ -1031,13 +1025,13 @@ non-existent type SomePlugin):</FONT></P>
|
||||
ISomePluginImpl<MyPlugin, CheckStatusWrapper></I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>public:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>explicit
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>explicit
|
||||
MyPlugin(<A HREF="#PluginConfig">IPluginConfig</A>* cnf) throw()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>:
|
||||
config(cnf), refCounter(0), owner(NULL)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>config->addRef();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>: config(cnf),
|
||||
refCounter(0), owner(NULL)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>config->addRef();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><FONT SIZE=4>Constructor
|
||||
gets as parameter plugin configuration interface. If you are going to
|
||||
have you plugin configured in some way it's good idea to save this
|
||||
@ -1047,74 +1041,67 @@ familiar configuration and minimize code written. Certainly when
|
||||
saving any reference counted interface it's better not forget to add
|
||||
reference to it. Also set reference counter to 0 and plugin owner to
|
||||
NULL.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><BR>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>~MyPlugin()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>config->release();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>~MyPlugin()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>config->release();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><FONT SIZE=4>Destructor
|
||||
releases config interface. Pay attention – we do not change
|
||||
reference counter of our owner cause it owns us, not we own it.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>// IRefCounted
|
||||
implementation</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>int release()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>if
|
||||
(--refCounter == 0)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>delete
|
||||
this;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>return
|
||||
0;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>return
|
||||
1;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>void addRef()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>++refCounter;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>// IRefCounted
|
||||
implementation</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>int release()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>if (--refCounter == 0)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>delete this;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>return 0;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>return 1;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>void addRef()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>++refCounter;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><FONT SIZE=4>Absolutely
|
||||
typical implementation of reference counted object.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>// IPluginBase
|
||||
implementation</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>void
|
||||
setOwner(IReferenceCounted* o)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>owner =
|
||||
o;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>IReferenceCounted*
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>// IPluginBase
|
||||
implementation</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>void
|
||||
setOwner(IReferenceCounted* o)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>owner = o;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>IReferenceCounted*
|
||||
getOwner()</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>return
|
||||
owner;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>return owner;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><FONT SIZE=4>As it
|
||||
was promised implementation of IPluginBase is trivial.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><BR>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>// ISomePlugin
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>// ISomePlugin
|
||||
implementation</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>// … here go
|
||||
various methods required for particular plugin type</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>// … here go various
|
||||
methods required for particular plugin type</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>private:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>IPluginConfig*
|
||||
config;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>FbSampleAtomic
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>IPluginConfig* config;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>FbSampleAtomic
|
||||
refCounter;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>IReferenceCounted*
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>IReferenceCounted*
|
||||
owner;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>};</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
@ -1134,17 +1121,17 @@ manager. Factory typically looks this way:</FONT></P>
|
||||
IPluginFactoryImpl<Factory, CheckStatusWrapper></I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>public:</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>IPluginBase*
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>IPluginBase*
|
||||
createPlugin(CheckStatusWrapper* status, IPluginConfig*
|
||||
factoryParameter)</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>MyPlugin* p =
|
||||
new MyPlugin(factoryParameter);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>p->addRef();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>return p;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>MyPlugin* p = new
|
||||
MyPlugin(factoryParameter);</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>p->addRef();</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>return p;</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>}</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>};</I></FONT></P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><BR>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in; font-style: normal"><FONT SIZE=4>Here
|
||||
attention should be payed to the fact that even in a case when code
|
||||
@ -1182,13 +1169,12 @@ plugin manager:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>extern "C" void
|
||||
FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* master)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>{</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>IPluginManager*
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>IPluginManager*
|
||||
pluginManager = master->getPluginManager();</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>module.registerMe(pluginManager);</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in">
|
||||
<FONT SIZE=4>pluginManager->registerPluginFactory(IPluginManager::TYPE_DB_CRYPT,
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>module.registerMe(pluginManager);</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>pluginManager->registerPluginFactory(IPluginManager::TYPE_DB_CRYPT,
|
||||
"DbCrypt_example", &factory);</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>}</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>First of all we call
|
||||
@ -1396,39 +1382,38 @@ objects:</FONT></P>
|
||||
</OL>
|
||||
<P STYLE="margin-bottom: 0in"><A NAME="Directory codes"></A><FONT SIZE=4>Directory
|
||||
codes:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_BIN – bin
|
||||
(utilities like isql, gbak, gstat)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_SBIN – sbin
|
||||
(fbguard and firebird server)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_CONF –
|
||||
configuration files (firebird.conf, databases.conf, plugins.conf)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_LIB – lib
|
||||
(fbclient, ib_util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_INC – include
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_BIN – bin (utilities
|
||||
like isql, gbak, gstat)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_SBIN – sbin (fbguard
|
||||
and firebird server)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_CONF – configuration
|
||||
files (firebird.conf, databases.conf, plugins.conf)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_LIB – lib (fbclient,
|
||||
ib_util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_INC – include
|
||||
(ibase.h, firebird/Interfaces.h)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_DOC -
|
||||
documentation</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_UDF – UDF
|
||||
(ib_udf, fbudf)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_SAMPLE - samples</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_SAMPLEDB –
|
||||
samples database (employee.fdb)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_HELP – qli help
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_DOC - documentation</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_UDF – UDF (ib_udf,
|
||||
fbudf)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_SAMPLE - samples</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_SAMPLEDB – samples
|
||||
database (employee.fdb)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_HELP – qli help
|
||||
(help.fdb)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_INTL –
|
||||
international libraries (fbintl)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_MISC –
|
||||
miscellaneous files (like uninstall manifest and something else)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_SECDB – where
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_INTL – international
|
||||
libraries (fbintl)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_MISC – miscellaneous
|
||||
files (like uninstall manifest and something else)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_SECDB – where
|
||||
security database is stored (securityN.fdb)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_MSG – where
|
||||
messages file is stored (firebird.msg)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_LOG – where log
|
||||
file is stored (firebird.log)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_GUARD – where
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_MSG – where messages
|
||||
file is stored (firebird.msg)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_LOG – where log file
|
||||
is stored (firebird.log)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_GUARD – where
|
||||
guardian lock is stored (fb_guard)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>DIR_PLUGINS –
|
||||
plugins directory ([lib]Engine13.{dll|so})</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>DIR_PLUGINS – plugins
|
||||
directory ([lib]Engine13.{dll|so})</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
@ -1757,15 +1742,15 @@ interface – API of plugin manager.</FONT></P>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>Constants defined by
|
||||
PluginManager interface (plugin types):</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_PROVIDER</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_AUTH_SERVER</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_AUTH_CLIENT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_AUTH_USER_MANAGEMENT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_EXTERNAL_ENGINE</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_TRACE</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_WIRE_CRYPT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_DB_CRYPT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>TYPE_KEY_HOLDER</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_PROVIDER</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_AUTH_SERVER</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_AUTH_CLIENT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_AUTH_USER_MANAGEMENT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_EXTERNAL_ENGINE</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_TRACE</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_WIRE_CRYPT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_DB_CRYPT</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>TYPE_KEY_HOLDER</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
@ -1966,7 +1951,7 @@ Statement interface:</FONT></P>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>IAttachment::prepare()
|
||||
flags:</FONT></P>
|
||||
<P STYLE="margin-left: 0.38in; text-indent: -0.01in; margin-bottom: 0in; page-break-before: auto">
|
||||
<P STYLE="margin-left: 0.38in; text-indent: -0.01in; margin-bottom: 0in; page-break-before: auto; page-break-after: auto">
|
||||
<FONT SIZE=4>PREPARE_PREFETCH_NONE – constant to pass no flags, 0
|
||||
value.</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>The following flags may be
|
||||
@ -2088,8 +2073,8 @@ moment when given timer should alarm.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void
|
||||
start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds) –
|
||||
start <A HREF="#Timer">ITimer</A> to alarm after given delay (in
|
||||
microseconds, 10<SUP>-6</SUP> seconds). Timer will be waked up only
|
||||
once after this call.</FONT></P>
|
||||
microseconds, 10</FONT><SUP><FONT SIZE=4>-6</FONT></SUP><FONT SIZE=4>
|
||||
seconds). Timer will be waked up only once after this call.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void stop(StatusType*
|
||||
status, ITimer* timer) – stop <A HREF="#Timer">ITimer</A>. It's
|
||||
not an error to stop not started timer thus avoiding problems with
|
||||
@ -2190,7 +2175,8 @@ interface – various helper methods required here or there.</FONT></P>
|
||||
unsigned fractions) – replaces isc_encode_sql_time().</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned
|
||||
formatStatus(char* buffer, unsigned bufferSize, IStatus* status) –
|
||||
replaces fb_interpret().</FONT></P>
|
||||
replaces fb_interpret(). Size of buffer, passed into this method,
|
||||
should not be less than 50 bytes.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned
|
||||
getClientVersion() – returns integer, containing major version in
|
||||
byte 0 and minor version in byte 1.</FONT></P>
|
||||
@ -2311,13 +2297,13 @@ be selected by firebird and passed to that interface.</FONT></P>
|
||||
<OL>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void
|
||||
setSymmetric(StatusType* status, const char* type, unsigned
|
||||
keyLength, const void* key) – make it store symmetric key of
|
||||
given type.</FONT></P>
|
||||
keyLength, const void* key) – make it store symmetric key of given
|
||||
type.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void
|
||||
setAsymmetric(StatusType* status, const char* type, unsigned
|
||||
encryptKeyLength, const void* encryptKey, unsigned decryptKeyLength,
|
||||
const void* decryptKey) – make it store pair of asymmetric keys of
|
||||
given type.</FONT></P>
|
||||
given type.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>const void*
|
||||
getEncryptKey(unsigned* length) – get a key for encryption.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>const void*
|
||||
@ -2502,7 +2488,7 @@ interface:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><A NAME="IntUserField"></A><FONT SIZE=4>IntUserField
|
||||
interface:</FONT></P>
|
||||
<OL>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>int get() - get
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>int get() - get
|
||||
field's value.</FONT></P>
|
||||
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void set(StatusType*
|
||||
status, int newValue) – assigns value to the field. Sets entered
|
||||
@ -2663,7 +2649,7 @@ plugin or key holder plugin.</FONT></P>
|
||||
bufferLength, void* buffer) – when performing callback information
|
||||
is passed in both directions. The source of a key receives
|
||||
dataLength bytes of data and may send up to bufferLength bytes into
|
||||
buffer returning actual number of bytes placed into buffer.</FONT></P>
|
||||
buffer returning actual number of bytes placed into buffer.</FONT></P>
|
||||
</OL>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
@ -2737,40 +2723,40 @@ static messages.</FONT></P>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><A NAME="FbDate"></A><FONT SIZE=4>class
|
||||
FbDate methods:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>void decode(IUtil*
|
||||
util, unsigned* year, unsigned* month, unsigned* day)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned
|
||||
getYear(IUtil* util) </FONT>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>void decode(IUtil* util,
|
||||
unsigned* year, unsigned* month, unsigned* day)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned getYear(IUtil*
|
||||
util) </FONT>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned
|
||||
getMonth(IUtil* util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned getDay(IUtil*
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned getMonth(IUtil*
|
||||
util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>void encode(IUtil*
|
||||
util, unsigned year, unsigned month, unsigned day)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned getDay(IUtil*
|
||||
util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>void encode(IUtil* util,
|
||||
unsigned year, unsigned month, unsigned day)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><A NAME="FbTime"></A><FONT SIZE=4>class
|
||||
FbTime methods:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>void decode(IUtil*
|
||||
util, unsigned* hours, unsigned* minutes, unsigned* seconds,
|
||||
unsigned* fractions)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned
|
||||
getHours(IUtil* util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned
|
||||
getMinutes(IUtil* util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned
|
||||
getSeconds(IUtil* util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>unsigned
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>void decode(IUtil* util,
|
||||
unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned*
|
||||
fractions)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned getHours(IUtil*
|
||||
util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned getMinutes(IUtil*
|
||||
util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned getSeconds(IUtil*
|
||||
util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>unsigned
|
||||
getFractions(IUtil* util)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>void encode(IUtil*
|
||||
util, unsigned hours, unsigned minutes, unsigned seconds, unsigned
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>void encode(IUtil* util,
|
||||
unsigned hours, unsigned minutes, unsigned seconds, unsigned
|
||||
fractions)</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>class FbTimestamp members:</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>FbDate date;</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>FbTime time;</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>FbDate date;</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>FbTime time;</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
@ -2785,7 +2771,7 @@ fields. </FONT>
|
||||
<P STYLE="margin-bottom: 0in"><A NAME="FbChar"></A><FONT SIZE=4>struct
|
||||
FbChar</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>{</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>char str[N];</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>char str[N];</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>};</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
@ -2793,10 +2779,9 @@ FbChar</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><A NAME="FbVarChar"></A><FONT SIZE=4>struct
|
||||
FbVarChar</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>{</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>ISC_USHORT length;</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>char str[N];</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4>void set(const char*
|
||||
s);</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>ISC_USHORT length;</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>char str[N];</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>void set(const char* s);</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>};</FONT></P>
|
||||
<P STYLE="margin-bottom: 0in"><BR>
|
||||
</P>
|
||||
|
@ -12,7 +12,7 @@ CURRENT_CONNECTION / CURRENT_TRANSACTION (FB 1.5)
|
||||
statement is executed.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <yemanov@yandex.ru>
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
CURRENT_CONNECTION / CURRENT_TRANSACTION
|
||||
@ -40,7 +40,7 @@ ROW_COUNT (FB 1.5)
|
||||
Returns number of rows, affected by the last SQL statement.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <yemanov@yandex.ru>
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
ROW_COUNT
|
||||
@ -75,7 +75,7 @@ SQLCODE / GDSCODE (FB 1.5)
|
||||
Returns numeric error code for the active exception.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <yemanov@yandex.ru>
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
SQLCODE / GDSCODE
|
||||
@ -112,9 +112,6 @@ SQLCODE / GDSCODE (FB 1.5)
|
||||
variables contain zero, regardless of the exception handling
|
||||
block type.
|
||||
|
||||
See also:
|
||||
README.exception_handling
|
||||
|
||||
|
||||
INSERTING / UPDATING / DELETING (FB 1.5)
|
||||
----------------------------------------
|
||||
@ -123,7 +120,7 @@ INSERTING / UPDATING / DELETING (FB 1.5)
|
||||
Determines type of row operation being executed.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <yemanov@yandex.ru>
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
INSERTING / UPDATING / DELETING
|
||||
@ -140,3 +137,96 @@ INSERTING / UPDATING / DELETING (FB 1.5)
|
||||
|
||||
See also:
|
||||
README.universal_triggers
|
||||
|
||||
|
||||
SQLSTATE (FB 2.5)
|
||||
-----------------
|
||||
|
||||
Function:
|
||||
Returns character-encoded state for the active exception.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
SQLSTATE
|
||||
|
||||
Type:
|
||||
CHAR(5) CHARACTER SET ASCII
|
||||
|
||||
Scope:
|
||||
PSQL, context of the exception handling block.
|
||||
|
||||
Example(s):
|
||||
BEGIN
|
||||
...
|
||||
WHEN SQLSTATE '44000' DO
|
||||
EXCEPTION E_CHECK_VIOLATION;
|
||||
WHEN ANY DO
|
||||
EXECUTE PROCEDURE P_ANY_EXCEPTION(SQLSTATE);
|
||||
END
|
||||
|
||||
Note(s):
|
||||
1. SQLSTATE always evaluates to NULL outside the exception handling block.
|
||||
|
||||
|
||||
EXCEPTION (FB 4.0)
|
||||
------------------
|
||||
|
||||
Function:
|
||||
Returns name of the active user-defined exception.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
EXCEPTION
|
||||
|
||||
Type:
|
||||
VARCHAR(63) CHARACTER SET UTF8
|
||||
|
||||
Scope:
|
||||
PSQL, context of the exception handling block.
|
||||
|
||||
Example(s):
|
||||
BEGIN
|
||||
...
|
||||
WHEN ANY DO
|
||||
IF (EXCEPTION IS NOT NULL)
|
||||
EXECUTE PROCEDURE P_USR_EXCEPTION(EXCEPTION);
|
||||
ELSE
|
||||
EXECUTE PROCEDURE P_SYS_EXCEPTION;
|
||||
END
|
||||
|
||||
Note(s):
|
||||
1. EXCEPTION always contains NULL outside the exception handling block.
|
||||
2. If system exception is thrown, EXCEPTION also contains NULL.
|
||||
|
||||
|
||||
ERROR_MESSAGE (FB 4.0)
|
||||
----------------------
|
||||
|
||||
Function:
|
||||
Returns interpreted text for the active exception.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <dimitr@firebirdsql.org>
|
||||
|
||||
Syntax rules:
|
||||
ERROR_MESSAGE
|
||||
|
||||
Type:
|
||||
VARCHAR(1024) CHARACTER SET UTF8
|
||||
|
||||
Scope:
|
||||
PSQL, context of the exception handling block.
|
||||
|
||||
Example(s):
|
||||
BEGIN
|
||||
...
|
||||
WHEN ANY DO
|
||||
EXECUTE PROCEDURE P_LOG_EXCEPTION(ERROR_MESSAGE);
|
||||
END
|
||||
|
||||
Note(s):
|
||||
1. ERROR_MESSAGE always contains NULL outside the exception handling block.
|
||||
|
@ -58,25 +58,6 @@ Exception re-raise semantics (FB 1.5)
|
||||
Evaluates to no-op if used outside the exception handling block.
|
||||
|
||||
|
||||
Run-time error codes (FB 1.5)
|
||||
-----------------------------
|
||||
|
||||
Function:
|
||||
Allows to get a numeric error code for the catched exception.
|
||||
|
||||
Author:
|
||||
Dmitry Yemanov <yemanov@yandex.ru>
|
||||
|
||||
Syntax rules:
|
||||
SQLCODE / GDSCODE;
|
||||
|
||||
Scope:
|
||||
PSQL, context of the exception handling block
|
||||
|
||||
See also:
|
||||
README.context_variables
|
||||
|
||||
|
||||
Parameterized exceptions (FB 3.0)
|
||||
---------------------------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -2,21 +2,41 @@
|
||||
|
||||
By the SQL specification, window functions (also know as analytical functions) are a kind of aggregation, but which does not "filter" the result set of a query. The aggregated data is mixed with the query result set.
|
||||
|
||||
That sort of functions are used with the `OVER` clause. Window functions may appear only in the select list or the order by clause of a query.
|
||||
That sort of functions are used with the `OVER` clause. Window functions may appear only in the select list or the `ORDER BY` clause of a query.
|
||||
|
||||
Additional to the `OVER` clause, Firebird window functions may be partitioned and ordered.
|
||||
Additional to the `OVER` clause, Firebird window functions may use partitions, order and frames (FB 4.0).
|
||||
|
||||
Syntax:
|
||||
|
||||
```
|
||||
<window function> ::= <window function name>([<expr> [, <expr> ...]]) OVER (
|
||||
[PARTITION BY <expr> [, <expr> ...]]
|
||||
[ORDER BY <expr> [<direction>] [<nulls placement>] [, <expr> [<direction>] [<nulls placement>] ...]
|
||||
)
|
||||
<window function> ::=
|
||||
<window function name>([<expr> [, <expr> ...]])
|
||||
OVER ([<window partition>] [<window order>] [<window frame>])
|
||||
|
||||
<direction> ::= {ASC | DESC}
|
||||
<window partition> ::=
|
||||
PARTITION BY <expr> [, <expr> ...]
|
||||
|
||||
<nulls placement> ::= NULLS {FIRST | LAST}
|
||||
<window order> ::=
|
||||
ORDER BY <expr> [<direction>] [<nulls placement>] [, <expr> [<direction>] [<nulls placement>]] ...
|
||||
|
||||
<window frame> ::=
|
||||
{RANGE | ROWS} <window frame extent>
|
||||
|
||||
<window frame extent> ::=
|
||||
{<window frame start> | <window frame between>}
|
||||
|
||||
<window frame start> ::=
|
||||
{UNBOUNDED PRECEDING | <expr> PRECEDING | CURRENT ROW}
|
||||
|
||||
<window frame between> ::=
|
||||
BETWEEN {UNBOUNDED PRECEDING | <expr> PRECEDING | <expr> FOLLOWING | CURRENT ROW} AND
|
||||
{UNBOUNDED FOLLOWING | <expr> PRECEDING | <expr> FOLLOWING | CURRENT ROW}
|
||||
|
||||
<direction> ::=
|
||||
{ASC | DESC}
|
||||
|
||||
<nulls placement> ::=
|
||||
NULLS {FIRST | LAST}
|
||||
```
|
||||
|
||||
## 1. Aggregate functions used as window functions
|
||||
@ -63,10 +83,6 @@ Here, `sum(salary) over ()` is computed with the sum of all SALARY from the quer
|
||||
|
||||
Like aggregate functions, that may operate alone or in relation to a group, window functions may also operate on a group, which is called "partition". Its syntax is:
|
||||
|
||||
```
|
||||
<window function name>(...) OVER (PARTITION BY <expr> [, <expr> ...])
|
||||
```
|
||||
|
||||
When aggregation is done over a group, it could produce more than one row. So the result set generated by a partition is joined with the main query using the same expression list of the partition.
|
||||
|
||||
Continuing the employee example, instead of get the percentage of the employee salary over all employees, we would like to get the percentage based only on the employees in the same department:
|
||||
@ -93,7 +109,7 @@ Results:
|
||||
|
||||
## 3. Ordering
|
||||
|
||||
The `ORDER BY` sub-clause can be used with or without partitions, and used with the standard aggregate functions, make them return the partial aggregations as the records are being processed. Example:
|
||||
The `ORDER BY` sub-clause can be used with or without partitions, and used with the standard aggregate functions and without frames, make them return the partial aggregations as the records are being processed. Example:
|
||||
|
||||
```sql
|
||||
select
|
||||
@ -116,6 +132,8 @@ The result set produced will be:
|
||||
|
||||
Then running_salary returns the partial/accumulated (or running) aggregation (of the `SUM` function). It may appear strange that 37.00 is repeated for the ids 1 and 5, but that is how it should work. The `ORDER BY` keys are grouped together and the aggregation is computed once (but summing the two 10.00). To avoid this, you can add the ID field to the end of the `ORDER BY` clause.
|
||||
|
||||
The fact of running aggregations and grouping results by the order key happens because when an explicit frame is not used, it computes the window functions as a `RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW` frame. More on frames later in this document.
|
||||
|
||||
It's possible to use multiple windows with different orders, and `ORDER BY` parts like `ASC` / `DESC` and `NULLS FIRST` / `NULLS LAST`.
|
||||
|
||||
With a partition, `ORDER BY` works the same way, but at each partition boundary the aggregation is reset.
|
||||
@ -128,7 +146,7 @@ Beyond aggregate functions, there is also exclusive window functions, currently
|
||||
|
||||
Both set of functions can be used with/without partition/ordering, but the usage does not make much sense without ordering.
|
||||
|
||||
## 4.1 Ranking functions
|
||||
## 4.1. Ranking functions
|
||||
|
||||
Syntax:
|
||||
|
||||
@ -179,7 +197,7 @@ The difference between `DENSE_RANK` and `RANK` is that there is a gap related to
|
||||
|
||||
`NTILE` distributes the rows into a specified number of groups. `NTILE` argument is restricted to integral positive literal, variable (`:var`) and DSQL parameter (`?`).
|
||||
|
||||
## 4.2 Navigational functions
|
||||
## 4.2. Navigational functions
|
||||
|
||||
Syntax:
|
||||
|
||||
@ -194,7 +212,7 @@ Syntax:
|
||||
|
||||
The navigational functions gets the simple (non-aggregated) value of an expression from another row (inside the same partition) of the query.
|
||||
|
||||
It's important to note that `FIRST_VALUE`, `LAST_VALUE` and `NTH_VALUE` also operates on a window frame, and Firebird is currently always framing from the first to the current (and not the last) row of the partition. This is likely to get strange results for `NTH_VALUE` and specially `LAST_VALUE`.
|
||||
It's important to note that `FIRST_VALUE`, `LAST_VALUE` and `NTH_VALUE` also operates on a window frame, and the default frame is `RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`. This is likely to get strange results for `NTH_VALUE` and specially `LAST_VALUE`. More on frames later in this document.
|
||||
|
||||
```sql
|
||||
select
|
||||
@ -225,6 +243,24 @@ And the result set:
|
||||
|
||||
`LAG` and `LEAD` get the value within a distance respect to the current row and the offset (which defaults to 1) passed. In the case the offset points to outside of the partition, the default parameter (which defaults to NULL) is returned. `LAG` looks for a preceding row, and `LEAD` for a following row.
|
||||
|
||||
## 5. Frames (FB 4.0)
|
||||
|
||||
It's possible to specify the frame that some window functions work. The frame is divided in three piecies: unit, start bound and end bound.
|
||||
|
||||
The unit `RANGE` or `ROWS` defines how the bounds `<expr> PRECEDING`, `<expr> FOLLOWING` and `CURRENT ROW` works.
|
||||
|
||||
With `RANGE`, the `ORDER BY` should specify only one expression, and that expression should be of a numeric, date, time or timestamp type. For `<expr> PRECEDING` and `<expr> FOLLOWING` bounds, `<expr>` is respectively subtracted or added to the order expression, and for `CURRENT ROW` only the order expression is used. Then, all rows (inside the partition) between the bounds are considered part of the resulting window frame.
|
||||
|
||||
With `ROWS`, order expressions is not limited by number or types. In this case, `<expr> PRECEDING`, `<expr> FOLLOWING` and `CURRENT ROW` relates to the row position under the partition, and not to the order keys values.
|
||||
|
||||
`UNBOUNDED PRECEDING` and `UNBOUNDED FOLLOWING` work identically with `RANGE` and `ROWS`. `UNBOUNDED PRECEDING` looks for the first row and `UNBOUNDED FOLLOWING` the last one, always inside the partition.
|
||||
|
||||
The frame syntax with `<window frame start>` specifies the start frame, with the end frame being `CURRENT ROW`.
|
||||
|
||||
Some window functions discard frames. `ROW_NUMBER`, `LAG` and `LEAD` always work as `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`. And `DENSE_RANK`, `RANK`, `PERCENT_RANK` and `CUME_DIST` always work as `RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`.
|
||||
|
||||
`FIRST_VALUE`, `LAST_VALUE` and `NTH_VALUE` respect frames, but the `RANGE` unit works identically as `ROWS`.
|
||||
|
||||
|
||||
Author:
|
||||
Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
||||
|
@ -1642,6 +1642,16 @@ C --
|
||||
PARAMETER (GDS__not_dba = 335545114)
|
||||
INTEGER*4 GDS__no_cursor
|
||||
PARAMETER (GDS__no_cursor = 335545115)
|
||||
INTEGER*4 GDS__dsql_window_incompat_frames
|
||||
PARAMETER (GDS__dsql_window_incompat_frames = 335545116)
|
||||
INTEGER*4 GDS__dsql_window_range_multi_key
|
||||
PARAMETER (GDS__dsql_window_range_multi_key = 335545117)
|
||||
INTEGER*4 GDS__dsql_window_range_inv_key_type
|
||||
PARAMETER (GDS__dsql_window_range_inv_key_type = 335545118)
|
||||
INTEGER*4 GDS__dsql_window_frame_value_inv_type
|
||||
PARAMETER (GDS__dsql_window_frame_value_inv_type = 335545119)
|
||||
INTEGER*4 GDS__window_frame_value_invalid
|
||||
PARAMETER (GDS__window_frame_value_invalid = 335545120)
|
||||
INTEGER*4 GDS__gfix_db_name
|
||||
PARAMETER (GDS__gfix_db_name = 335740929)
|
||||
INTEGER*4 GDS__gfix_invalid_sw
|
||||
|
@ -1637,6 +1637,16 @@ const
|
||||
gds_not_dba = 335545114;
|
||||
isc_no_cursor = 335545115;
|
||||
gds_no_cursor = 335545115;
|
||||
isc_dsql_window_incompat_frames = 335545116;
|
||||
gds_dsql_window_incompat_frames = 335545116;
|
||||
isc_dsql_window_range_multi_key = 335545117;
|
||||
gds_dsql_window_range_multi_key = 335545117;
|
||||
isc_dsql_window_range_inv_key_type = 335545118;
|
||||
gds_dsql_window_range_inv_key_type = 335545118;
|
||||
isc_dsql_window_frame_value_inv_type = 335545119;
|
||||
gds_dsql_window_frame_value_inv_type = 335545119;
|
||||
isc_window_frame_value_invalid = 335545120;
|
||||
gds_window_frame_value_invalid = 335545120;
|
||||
isc_gfix_db_name = 335740929;
|
||||
gds_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
|
@ -69,7 +69,7 @@ inline bool DTYPE_IS_APPROX(UCHAR d)
|
||||
|
||||
inline bool DTYPE_IS_NUMERIC(UCHAR d)
|
||||
{
|
||||
return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64;
|
||||
return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64;
|
||||
}
|
||||
|
||||
// Descriptor format
|
||||
@ -135,6 +135,11 @@ typedef struct dsc
|
||||
return dsc_dtype == dtype_int64 || dsc_dtype == dtype_long || dsc_dtype == dtype_short;
|
||||
}
|
||||
|
||||
bool isNumeric() const
|
||||
{
|
||||
return (dsc_dtype >= dtype_byte && dsc_dtype <= dtype_d_float) || dsc_dtype == dtype_int64;
|
||||
}
|
||||
|
||||
bool isText() const
|
||||
{
|
||||
return dsc_dtype >= dtype_text && dsc_dtype <= dtype_varying;
|
||||
@ -394,19 +399,23 @@ inline bool DSC_EQUIV(const dsc* d1, const dsc* d2, bool check_collate)
|
||||
{
|
||||
if (((alt_dsc*) d1)->dsc_combined_type == ((alt_dsc*) d2)->dsc_combined_type)
|
||||
{
|
||||
if (d1->dsc_dtype >= dtype_text && d1->dsc_dtype <= dtype_varying)
|
||||
if ((d1->dsc_dtype >= dtype_text && d1->dsc_dtype <= dtype_varying) ||
|
||||
d1->dsc_dtype == dtype_blob)
|
||||
{
|
||||
if (DSC_GET_CHARSET(d1) == DSC_GET_CHARSET(d2))
|
||||
if (d1->getCharSet() == d2->getCharSet())
|
||||
{
|
||||
if (check_collate) {
|
||||
return (DSC_GET_COLLATE(d1) == DSC_GET_COLLATE(d2));
|
||||
}
|
||||
if (check_collate)
|
||||
return d1->getCollation() == d2->getCollation();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ AggNode::AggNode(MemoryPool& pool, const AggInfo& aAggInfo, bool aDistinct, bool
|
||||
dialect1(aDialect1),
|
||||
arg(aArg),
|
||||
asb(NULL),
|
||||
indexed(false),
|
||||
ordered(false)
|
||||
indexed(false)
|
||||
{
|
||||
addChildNode(arg, arg);
|
||||
}
|
||||
@ -270,7 +269,7 @@ ValueExprNode* AggNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
if (!visitor.window && visitor.dsqlScratch->scopeLevel == aggFinder.deepestLevel)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.orderNode);
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,14 +336,10 @@ AggNode* AggNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void AggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (aggType == AGG_TYPE_GROUP)
|
||||
impure->vlux_count = 0;
|
||||
|
||||
impure->aggType = aggType;
|
||||
impure->vlux_count = 0;
|
||||
|
||||
if (distinct)
|
||||
{
|
||||
@ -627,12 +622,9 @@ string AvgAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "AvgAggNode";
|
||||
}
|
||||
|
||||
void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
@ -652,10 +644,6 @@ void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) con
|
||||
void AvgAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
if (dialect1)
|
||||
@ -760,12 +748,9 @@ string ListAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "ListAggNode";
|
||||
}
|
||||
|
||||
void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
// We don't know here what should be the sub-type and text-type.
|
||||
// Defer blob creation for when first record is found.
|
||||
@ -778,9 +763,6 @@ void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
if (!impure->vlu_blob)
|
||||
{
|
||||
impure->vlu_blob = blb::create(tdbb, request->req_transaction,
|
||||
@ -918,12 +900,9 @@ string CountAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "CountAggNode";
|
||||
}
|
||||
|
||||
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0);
|
||||
@ -933,9 +912,6 @@ void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/)
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
if (dialect1)
|
||||
++impure->vlu_misc.vlu_long;
|
||||
else
|
||||
@ -1152,12 +1128,9 @@ string SumAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "SumAggNode";
|
||||
}
|
||||
|
||||
void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
@ -1174,10 +1147,6 @@ void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) con
|
||||
void SumAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
if (dialect1)
|
||||
@ -1252,12 +1221,9 @@ string MaxMinAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "MaxMinAggNode";
|
||||
}
|
||||
|
||||
void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->vlu_desc.dsc_dtype = 0;
|
||||
@ -1266,11 +1232,8 @@ void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType)
|
||||
void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
if (!impure->vlu_desc.dsc_dtype)
|
||||
{
|
||||
EVL_make_value(tdbb, desc, impure);
|
||||
@ -1366,12 +1329,9 @@ string StdDevAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "StdDevAggNode";
|
||||
}
|
||||
|
||||
void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_double(0);
|
||||
@ -1383,10 +1343,6 @@ void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType)
|
||||
void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return;
|
||||
|
||||
++impure->vlux_count;
|
||||
|
||||
const double d = MOV_get_double(desc);
|
||||
@ -1510,12 +1466,9 @@ string CorrAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "CorrAggNode";
|
||||
}
|
||||
|
||||
void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_double(0);
|
||||
@ -1528,9 +1481,6 @@ bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return true;
|
||||
|
||||
dsc* desc = NULL;
|
||||
dsc* desc2 = NULL;
|
||||
|
||||
@ -1702,12 +1652,9 @@ string RegrAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "RegrAggNode";
|
||||
}
|
||||
|
||||
void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_double(0);
|
||||
@ -1720,9 +1667,6 @@ bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return true;
|
||||
|
||||
dsc* desc = NULL;
|
||||
dsc* desc2 = NULL;
|
||||
|
||||
@ -1882,12 +1826,9 @@ string RegrCountAggNode::internalPrint(NodePrinter& printer) const
|
||||
return "RegrCountAggNode";
|
||||
}
|
||||
|
||||
void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0);
|
||||
@ -1897,9 +1838,6 @@ bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (ordered && impure->aggType != AGG_TYPE_ORDER)
|
||||
return true;
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, arg);
|
||||
if (request->req_flags & req_null)
|
||||
return false;
|
||||
|
@ -37,13 +37,18 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -62,6 +67,11 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual bool setParameterType(DsqlCompilerScratch* dsqlScratch,
|
||||
@ -69,15 +79,7 @@ public:
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void checkOrderedWindowCapable() const
|
||||
{
|
||||
Firebird::status_exception::raise(
|
||||
Firebird::Arg::Gds(isc_wish_list) <<
|
||||
Firebird::Arg::Gds(isc_random) <<
|
||||
"LIST is not supported in ordered windows");
|
||||
}
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -95,13 +97,18 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -116,12 +123,17 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -142,12 +154,17 @@ public:
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -176,6 +193,11 @@ public:
|
||||
|
||||
explicit StdDevAggNode(MemoryPool& pool, StdDevType aType, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -184,7 +206,7 @@ public:
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -216,6 +238,11 @@ public:
|
||||
explicit CorrAggNode(MemoryPool& pool, CorrType aType,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -224,7 +251,7 @@ public:
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
@ -263,6 +290,11 @@ public:
|
||||
explicit RegrAggNode(MemoryPool& pool, RegrType aType,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -271,7 +303,7 @@ public:
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
@ -293,6 +325,11 @@ public:
|
||||
explicit RegrCountAggNode(MemoryPool& pool,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
@ -300,7 +337,7 @@ public:
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
@ -5263,7 +5263,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;
|
||||
@ -6023,6 +6023,14 @@ void InternalInfoNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc)
|
||||
desc->makeText(FB_SQLSTATE_LENGTH, ttype_ascii);
|
||||
break;
|
||||
|
||||
case INFO_TYPE_EXCEPTION:
|
||||
desc->makeVarying(MAX_SQL_IDENTIFIER_LEN, ttype_metadata);
|
||||
break;
|
||||
|
||||
case INFO_TYPE_ERROR_MSG:
|
||||
desc->makeVarying(MAX_ERROR_MSG_LENGTH, ttype_utf8);
|
||||
break;
|
||||
|
||||
case INFO_TYPE_CONNECTION_ID:
|
||||
case INFO_TYPE_TRANSACTION_ID:
|
||||
case INFO_TYPE_ROWS_AFFECTED:
|
||||
@ -6056,6 +6064,14 @@ void InternalInfoNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
|
||||
desc->makeText(FB_SQLSTATE_LENGTH, ttype_ascii);
|
||||
break;
|
||||
|
||||
case INFO_TYPE_EXCEPTION:
|
||||
desc->makeVarying(MAX_SQL_IDENTIFIER_LEN, ttype_metadata);
|
||||
break;
|
||||
|
||||
case INFO_TYPE_ERROR_MSG:
|
||||
desc->makeVarying(MAX_ERROR_MSG_LENGTH, ttype_utf8);
|
||||
break;
|
||||
|
||||
case INFO_TYPE_CONNECTION_ID:
|
||||
case INFO_TYPE_TRANSACTION_ID:
|
||||
case INFO_TYPE_ROWS_AFFECTED:
|
||||
@ -6115,6 +6131,41 @@ dsc* InternalInfoNode::execute(thread_db* tdbb, jrd_req* request) const
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
else if (infoType == INFO_TYPE_EXCEPTION)
|
||||
{
|
||||
if (request->req_last_xcp.success())
|
||||
return NULL;
|
||||
|
||||
const SLONG xcpCode = request->req_last_xcp.as_xcpcode();
|
||||
|
||||
if (!xcpCode)
|
||||
return NULL;
|
||||
|
||||
MetaName xcpName;
|
||||
MET_lookup_exception(tdbb, xcpCode, xcpName, NULL);
|
||||
|
||||
if (xcpName.isEmpty())
|
||||
return NULL;
|
||||
|
||||
dsc desc;
|
||||
desc.makeText(xcpName.length(), ttype_metadata, (UCHAR*) xcpName.c_str());
|
||||
EVL_make_value(tdbb, &desc, impure);
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
else if (infoType == INFO_TYPE_ERROR_MSG)
|
||||
{
|
||||
if (request->req_last_xcp.success())
|
||||
return NULL;
|
||||
|
||||
const string errorText = request->req_last_xcp.as_text();
|
||||
|
||||
dsc desc;
|
||||
desc.makeText(errorText.length(), ttype_utf8, (UCHAR*) errorText.c_str());
|
||||
EVL_make_value(tdbb, &desc, impure);
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
SLONG result32 = 0;
|
||||
SINT64 result64 = 0;
|
||||
@ -6734,7 +6785,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;
|
||||
@ -6929,7 +6980,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);
|
||||
@ -7317,7 +7368,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)
|
||||
@ -7358,16 +7409,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
|
||||
@ -7376,7 +7574,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";
|
||||
}
|
||||
@ -7396,7 +7594,7 @@ bool OverNode::dsqlAggregateFinder(AggregateFinder& visitor)
|
||||
aggregate |= visitor.visit(aggExpr);
|
||||
|
||||
aggregate |= visitor.visit(partition);
|
||||
aggregate |= visitor.visit(order);
|
||||
aggregate |= visitor.visit(window);
|
||||
|
||||
return aggregate;
|
||||
}
|
||||
@ -7411,7 +7609,7 @@ bool OverNode::dsqlAggregate2Finder(Aggregate2Finder& visitor)
|
||||
}
|
||||
|
||||
found |= visitor.visit(partition);
|
||||
found |= visitor.visit(order);
|
||||
found |= visitor.visit(window);
|
||||
|
||||
return found;
|
||||
}
|
||||
@ -7425,7 +7623,7 @@ bool OverNode::dsqlInvalidReferenceFinder(InvalidReferenceFinder& visitor)
|
||||
|
||||
invalid |= visitor.visit(aggExpr);
|
||||
invalid |= visitor.visit(partition);
|
||||
invalid |= visitor.visit(order);
|
||||
invalid |= visitor.visit(window);
|
||||
|
||||
return invalid;
|
||||
}
|
||||
@ -7439,7 +7637,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)
|
||||
{
|
||||
@ -7453,16 +7651,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;
|
||||
@ -7488,7 +7686,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);
|
||||
|
||||
for (auto& child : aggNode->dsqlChildNodes)
|
||||
child->remap(visitor);
|
||||
@ -7499,27 +7697,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7561,10 +7761,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;
|
||||
}
|
||||
|
||||
|
||||
@ -8129,7 +8343,7 @@ bool RecordKeyNode::dsqlFieldFinder(FieldFinder& /*visitor*/)
|
||||
ValueExprNode* RecordKeyNode::dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
return PASS1_post_map(visitor.dsqlScratch, this,
|
||||
visitor.context, visitor.partitionNode, visitor.orderNode);
|
||||
visitor.context, visitor.partitionNode, visitor.windowNode);
|
||||
}
|
||||
|
||||
void RecordKeyNode::setParameterName(dsql_par* parameter) const
|
||||
|
@ -951,7 +951,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class OrderNode : public TypedNode<ValueExprNode, ExprNode::TYPE_ORDER>
|
||||
class OrderNode : public DsqlNode<OrderNode, ExprNode::TYPE_ORDER>
|
||||
{
|
||||
public:
|
||||
enum NullsPlacement
|
||||
@ -967,38 +967,6 @@ public:
|
||||
virtual OrderNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const;
|
||||
|
||||
virtual void setParameterName(dsql_par* /*parameter*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual ValueExprNode* copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
NestConst<ValueExprNode> value;
|
||||
bool descending;
|
||||
@ -1006,13 +974,227 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class WindowClause : public DsqlNode<WindowClause, ExprNode::TYPE_WINDOW_CLAUSE>
|
||||
{
|
||||
public:
|
||||
// ListExprNode has no relation with this but works perfectly here for now.
|
||||
|
||||
class Frame : public TypedNode<ListExprNode, ExprNode::TYPE_WINDOW_CLAUSE_FRAME>
|
||||
{
|
||||
public:
|
||||
enum Bound
|
||||
{
|
||||
// Warning: used in BLR
|
||||
BOUND_PRECEDING = 0,
|
||||
BOUND_FOLLOWING,
|
||||
BOUND_CURRENT_ROW
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Frame(MemoryPool& p, Bound aBound, ValueExprNode* aValue = NULL)
|
||||
: TypedNode(p),
|
||||
bound(aBound),
|
||||
value(aValue)
|
||||
{
|
||||
addChildNode(value, value);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, bound);
|
||||
NODE_PRINT(printer, value);
|
||||
|
||||
return "WindowClause::Frame";
|
||||
}
|
||||
|
||||
virtual Frame* dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
Frame* node = FB_NEW_POOL(getPool()) Frame(getPool(), bound,
|
||||
doDsqlPass(dsqlScratch, value));
|
||||
|
||||
if (node->value)
|
||||
{
|
||||
dsc desc;
|
||||
desc.makeLong(0);
|
||||
|
||||
node->value->setParameterType(dsqlScratch, &desc, false);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ListExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const Frame* o = other->as<Frame>();
|
||||
fb_assert(o);
|
||||
|
||||
return bound == o->bound;
|
||||
}
|
||||
|
||||
virtual Frame* dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
ListExprNode::dsqlFieldRemapper(visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const;
|
||||
virtual Frame* pass1(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual Frame* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual Frame* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
public:
|
||||
Bound bound;
|
||||
NestConst<ValueExprNode> value;
|
||||
};
|
||||
|
||||
class FrameExtent : public TypedNode<ListExprNode, ExprNode::TYPE_WINDOW_CLAUSE_FRAME_EXTENT>
|
||||
{
|
||||
public:
|
||||
enum Unit
|
||||
{
|
||||
// Warning: used in BLR
|
||||
UNIT_RANGE = 0,
|
||||
UNIT_ROWS
|
||||
//// TODO: SQL-2013: GROUPS
|
||||
};
|
||||
|
||||
public:
|
||||
explicit FrameExtent(MemoryPool& p, Unit aUnit, Frame* aFrame1 = NULL, Frame* aFrame2 = NULL)
|
||||
: TypedNode(p),
|
||||
unit(aUnit),
|
||||
frame1(aFrame1),
|
||||
frame2(aFrame2)
|
||||
{
|
||||
addChildNode(frame1, frame1);
|
||||
addChildNode(frame2, frame2);
|
||||
}
|
||||
|
||||
static FrameExtent* createDefault(MemoryPool& p)
|
||||
{
|
||||
FrameExtent* frameExtent = FB_NEW_POOL(p) WindowClause::FrameExtent(p, UNIT_RANGE);
|
||||
frameExtent->frame1 = FB_NEW_POOL(p) WindowClause::Frame(p, Frame::BOUND_PRECEDING);
|
||||
frameExtent->frame2 = FB_NEW_POOL(p) WindowClause::Frame(p, Frame::BOUND_CURRENT_ROW);
|
||||
return frameExtent;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, unit);
|
||||
NODE_PRINT(printer, frame1);
|
||||
NODE_PRINT(printer, frame2);
|
||||
|
||||
return "WindowClause::FrameExtent";
|
||||
}
|
||||
|
||||
virtual FrameExtent* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
|
||||
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ListExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const FrameExtent* o = other->as<FrameExtent>();
|
||||
fb_assert(o);
|
||||
|
||||
return unit == o->unit;
|
||||
}
|
||||
|
||||
virtual FrameExtent* dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
ListExprNode::dsqlFieldRemapper(visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const;
|
||||
virtual FrameExtent* pass1(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual FrameExtent* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual FrameExtent* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
public:
|
||||
Unit unit;
|
||||
NestConst<Frame> frame1;
|
||||
NestConst<Frame> frame2;
|
||||
};
|
||||
|
||||
enum Exclusion
|
||||
{
|
||||
// Warning: used in BLR
|
||||
EXCLUDE_NO_OTHERS = 0,
|
||||
EXCLUDE_CURRENT_ROW,
|
||||
EXCLUDE_GROUP,
|
||||
EXCLUDE_TIES
|
||||
};
|
||||
|
||||
public:
|
||||
explicit WindowClause(MemoryPool& p, ValueListNode* aOrder, FrameExtent* aFrameExtent,
|
||||
Exclusion aExclusion)
|
||||
: DsqlNode(p),
|
||||
order(aOrder),
|
||||
extent(aFrameExtent),
|
||||
exclusion(aExclusion)
|
||||
{
|
||||
addDsqlChildNode(order);
|
||||
addDsqlChildNode(extent);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
NODE_PRINT(printer, order);
|
||||
NODE_PRINT(printer, extent);
|
||||
|
||||
return "WindowClause";
|
||||
}
|
||||
|
||||
virtual WindowClause* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
|
||||
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!DsqlNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const WindowClause* o = other->as<WindowClause>();
|
||||
fb_assert(o);
|
||||
|
||||
return exclusion == o->exclusion;
|
||||
}
|
||||
|
||||
virtual WindowClause* dsqlFieldRemapper(FieldRemapper& visitor)
|
||||
{
|
||||
DsqlNode::dsqlFieldRemapper(visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual WindowClause* pass1(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
fb_assert(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual WindowClause* pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
fb_assert(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public:
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<FrameExtent> extent;
|
||||
Exclusion exclusion;
|
||||
};
|
||||
|
||||
// OVER is used only in DSQL. In the engine, normal aggregate functions are used in partitioned
|
||||
// maps.
|
||||
class OverNode : public TypedNode<ValueExprNode, ExprNode::TYPE_OVER>
|
||||
{
|
||||
public:
|
||||
explicit OverNode(MemoryPool& pool, AggNode* aAggExpr = NULL, ValueListNode* aPartition = NULL,
|
||||
ValueListNode* aOrder = NULL);
|
||||
WindowClause* aWindow = NULL);
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
@ -1034,7 +1216,7 @@ public:
|
||||
public:
|
||||
NestConst<ValueExprNode> aggExpr;
|
||||
NestConst<ValueListNode> partition;
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<WindowClause> window;
|
||||
};
|
||||
|
||||
|
||||
|
@ -434,6 +434,9 @@ public:
|
||||
TYPE_UDF_CALL,
|
||||
TYPE_VALUE_IF,
|
||||
TYPE_VARIABLE,
|
||||
TYPE_WINDOW_CLAUSE,
|
||||
TYPE_WINDOW_CLAUSE_FRAME,
|
||||
TYPE_WINDOW_CLAUSE_FRAME_EXTENT,
|
||||
|
||||
// Bool types
|
||||
TYPE_BINARY_BOOL,
|
||||
@ -820,8 +823,74 @@ public:
|
||||
dsc nodDesc;
|
||||
};
|
||||
|
||||
template <typename T, typename ValueExprNode::Type typeConst>
|
||||
class DsqlNode : public TypedNode<ValueExprNode, typeConst>
|
||||
{
|
||||
public:
|
||||
DsqlNode(MemoryPool& pool)
|
||||
: TypedNode<ValueExprNode, typeConst>(pool)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void setParameterName(dsql_par* /*parameter*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual void getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* /*desc*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
virtual DsqlNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual DsqlNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual ValueExprNode* copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
fb_assert(false);
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
class AggNode : public TypedNode<ValueExprNode, ExprNode::TYPE_AGGREGATE>
|
||||
{
|
||||
public:
|
||||
// Capabilities
|
||||
// works in a window frame
|
||||
static const unsigned CAP_SUPPORTS_WINDOW_FRAME = 0x01;
|
||||
// respects window frame boundaries
|
||||
static const unsigned CAP_RESPECTS_WINDOW_FRAME = 0x02 | CAP_SUPPORTS_WINDOW_FRAME;
|
||||
// wants aggPass/aggExecute calls in a window
|
||||
static const unsigned CAP_WANTS_AGG_CALLS = 0x04;
|
||||
// wants winPass call in a window
|
||||
static const unsigned CAP_WANTS_WIN_PASS_CALL = 0x08;
|
||||
|
||||
protected:
|
||||
struct AggInfo
|
||||
{
|
||||
@ -913,6 +982,7 @@ public:
|
||||
RegisterNode<T> registerNode1, registerNode2;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit AggNode(MemoryPool& pool, const AggInfo& aAggInfo, bool aDistinct, bool aDialect1,
|
||||
ValueExprNode* aArg = NULL);
|
||||
|
||||
@ -955,31 +1025,17 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void checkOrderedWindowCapable() const
|
||||
{
|
||||
if (distinct)
|
||||
{
|
||||
Firebird::status_exception::raise(
|
||||
Firebird::Arg::Gds(isc_wish_list) <<
|
||||
Firebird::Arg::Gds(isc_random) <<
|
||||
"DISTINCT is not supported in ordered windows");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
}
|
||||
|
||||
virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const = 0; // pure, but defined
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const = 0; // pure, but defined
|
||||
virtual void aggFinish(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual unsigned getCapabilities() const = 0;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const = 0;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const = 0;
|
||||
|
||||
@ -1000,7 +1056,6 @@ public:
|
||||
NestConst<ValueExprNode> arg;
|
||||
const AggregateSort* asb;
|
||||
bool indexed;
|
||||
bool ordered;
|
||||
|
||||
private:
|
||||
static Factory* factories;
|
||||
@ -1012,6 +1067,16 @@ class WinFuncNode : public AggNode
|
||||
{
|
||||
public:
|
||||
explicit WinFuncNode(MemoryPool& pool, const AggInfo& aAggInfo, ValueExprNode* aArg = NULL);
|
||||
|
||||
public:
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
|
||||
{
|
||||
}
|
||||
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -37,9 +37,9 @@ namespace
|
||||
{
|
||||
const int HASH_SIZE = 1021;
|
||||
|
||||
struct KeywordVersion
|
||||
struct Keyword
|
||||
{
|
||||
KeywordVersion(int aKeyword, MetaName* aStr)
|
||||
Keyword(int aKeyword, MetaName* aStr)
|
||||
: keyword(aKeyword), str(aStr)
|
||||
{}
|
||||
|
||||
@ -47,16 +47,16 @@ namespace
|
||||
MetaName* str;
|
||||
};
|
||||
|
||||
class KeywordsMap : public GenericMap<Pair<Left<MetaName, KeywordVersion> > >
|
||||
class KeywordsMap : public GenericMap<Pair<Left<MetaName, Keyword> > >
|
||||
{
|
||||
public:
|
||||
explicit KeywordsMap(MemoryPool& pool)
|
||||
: GenericMap<Pair<Left<MetaName, KeywordVersion> > >(pool)
|
||||
: GenericMap<Pair<Left<MetaName, Keyword> > >(pool)
|
||||
{
|
||||
for (const TOK* token = KEYWORD_getTokens(); token->tok_string; ++token)
|
||||
{
|
||||
MetaName* str = FB_NEW_POOL(pool) MetaName(token->tok_string);
|
||||
put(*str, KeywordVersion(token->tok_ident, str));
|
||||
put(*str, Keyword(token->tok_ident, str));
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ int Parser::yylexAux()
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
MemoryPool& pool = *tdbb->getDefaultPool();
|
||||
|
||||
int maxByteLength, maxCharLength;
|
||||
unsigned maxByteLength, maxCharLength;
|
||||
|
||||
if (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST)
|
||||
{
|
||||
@ -542,9 +542,9 @@ int Parser::yylexAux()
|
||||
yyabandon(-104, isc_dyn_zero_len_id);
|
||||
}
|
||||
|
||||
Attachment* attachment = tdbb->getAttachment();
|
||||
MetaName name(attachment->nameToMetaCharSet(tdbb, MetaName(buffer, p - buffer)));
|
||||
int charLength = metadataCharSet->length(
|
||||
Attachment* const attachment = tdbb->getAttachment();
|
||||
const MetaName name(attachment->nameToMetaCharSet(tdbb, MetaName(buffer, p - buffer)));
|
||||
const unsigned charLength = metadataCharSet->length(
|
||||
name.length(), (const UCHAR*) name.c_str(), true);
|
||||
|
||||
if (name.length() > maxByteLength || charLength > maxCharLength)
|
||||
@ -1105,8 +1105,8 @@ int Parser::yylexAux()
|
||||
if (p > &string[maxByteLength] || p > &string[maxCharLength])
|
||||
yyabandon(-104, isc_dyn_name_longer);
|
||||
|
||||
MetaName str(string, p - string);
|
||||
KeywordVersion* keyVer = keywordsMap->get(str);
|
||||
const MetaName str(string, p - string);
|
||||
const Keyword* const keyVer = keywordsMap->get(str);
|
||||
|
||||
if (keyVer && (keyVer->keyword != COMMENT || lex.prev_keyword == -1))
|
||||
{
|
||||
@ -1128,8 +1128,8 @@ int Parser::yylexAux()
|
||||
|
||||
if (lex.last_token + 1 < lex.end && !isspace(UCHAR(lex.last_token[1])))
|
||||
{
|
||||
Firebird::string str(lex.last_token, 2);
|
||||
KeywordVersion* keyVer = keywordsMap->get(str);
|
||||
const MetaName str(lex.last_token, 2);
|
||||
const Keyword* const keyVer = keywordsMap->get(str);
|
||||
|
||||
if (keyVer)
|
||||
{
|
||||
|
@ -2403,6 +2403,9 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger
|
||||
rpb->rpb_runtime_flags &= ~RPB_refetch;
|
||||
}
|
||||
|
||||
if (rpb->rpb_runtime_flags & RPB_undo_deleted)
|
||||
return parentStmt;
|
||||
|
||||
SavepointChangeMarker scMarker(transaction);
|
||||
|
||||
// Handle pre-operation trigger.
|
||||
@ -6200,6 +6203,12 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg
|
||||
orgRpb->rpb_runtime_flags &= ~RPB_refetch;
|
||||
}
|
||||
|
||||
if (orgRpb->rpb_runtime_flags & RPB_undo_deleted)
|
||||
{
|
||||
request->req_operation = jrd_req::req_return;
|
||||
return parentStmt;
|
||||
}
|
||||
|
||||
// Fall thru on evaluate to set up for modify before executing sub-statement.
|
||||
// This involves finding the appropriate format, making sure a record block
|
||||
// exists for the stream and is big enough, and copying fields from the
|
||||
|
@ -151,13 +151,13 @@ class FieldRemapper
|
||||
{
|
||||
public:
|
||||
FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
||||
ValueListNode* aPartitionNode = NULL, ValueListNode* aOrderNode = NULL);
|
||||
ValueListNode* aPartitionNode = NULL, WindowClause* aWindowNode = NULL);
|
||||
|
||||
static ExprNode* remap(DsqlCompilerScratch* dsqlScratch, dsql_ctx* context, bool window,
|
||||
ExprNode* field, ValueListNode* partitionNode = NULL, ValueListNode* orderNode = NULL)
|
||||
ExprNode* field, ValueListNode* partitionNode = NULL, WindowClause* windowNode = NULL)
|
||||
{
|
||||
// The bool value returned by the visitor is completely discarded in this class.
|
||||
return FieldRemapper(dsqlScratch, context, window, partitionNode, orderNode).visit(field);
|
||||
return FieldRemapper(dsqlScratch, context, window, partitionNode, windowNode).visit(field);
|
||||
}
|
||||
|
||||
ExprNode* visit(ExprNode* node);
|
||||
@ -167,7 +167,7 @@ public:
|
||||
dsql_ctx* const context;
|
||||
const bool window;
|
||||
ValueListNode* partitionNode;
|
||||
ValueListNode* orderNode;
|
||||
WindowClause* windowNode;
|
||||
USHORT currentLevel;
|
||||
};
|
||||
|
||||
|
@ -79,12 +79,9 @@ ValueExprNode* DenseRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c
|
||||
return FB_NEW_POOL(*tdbb->getDefaultPool()) DenseRankWinNode(*tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
@ -97,10 +94,7 @@ void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
|
||||
dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlu_misc.vlu_int64;
|
||||
|
||||
++impure->vlu_misc.vlu_int64;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
@ -158,12 +152,9 @@ AggNode* RankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(1, 0);
|
||||
@ -173,9 +164,7 @@ void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) co
|
||||
void RankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlux_count;
|
||||
++impure->vlux_count;
|
||||
}
|
||||
|
||||
dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
@ -206,7 +195,8 @@ AggNode* RankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
|
||||
static WinFuncNode::RegisterFactory0<PercentRankWinNode> percentRankWinInfo("PERCENT_RANK");
|
||||
|
||||
PercentRankWinNode::PercentRankWinNode(MemoryPool& pool)
|
||||
: WinFuncNode(pool, percentRankWinInfo)
|
||||
: WinFuncNode(pool, percentRankWinInfo),
|
||||
tempImpure(0)
|
||||
{
|
||||
fb_assert(dsqlChildNodes.getCount() == 1 && jrdChildNodes.getCount() == 1);
|
||||
dsqlChildNodes.clear();
|
||||
@ -237,44 +227,51 @@ ValueExprNode* PercentRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/)
|
||||
AggNode* PercentRankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
AggNode::pass2(tdbb, csb);
|
||||
tempImpure = CMP_impure(csb, sizeof(impure_value_ex));
|
||||
return this;
|
||||
}
|
||||
|
||||
void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
impure->make_int64(1, 0);
|
||||
impure->vlux_count = 0;
|
||||
|
||||
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
|
||||
impureTemp->make_double(0);
|
||||
impureTemp->vlux_count = 0;
|
||||
}
|
||||
|
||||
void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlux_count;
|
||||
++impure->vlux_count;
|
||||
}
|
||||
|
||||
dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
|
||||
|
||||
impureTemp->vlux_count = impure->vlu_misc.vlu_int64;
|
||||
|
||||
impure->vlu_misc.vlu_int64 += impure->vlux_count;
|
||||
impure->vlux_count = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
|
||||
|
||||
double n = impure->vlux_count;
|
||||
double partitionSize = window->getPartitionSize();
|
||||
double orderSize = window->getOrderSize();
|
||||
|
||||
impure->make_double(n == 1 ? 0 : 1 / (partitionSize - 1) * (n - orderSize));
|
||||
return &impure->vlu_desc;
|
||||
impureTemp->vlu_misc.vlu_double = 1 / (partitionSize - 1) * (impureTemp->vlux_count - 1);
|
||||
return &impureTemp->vlu_desc;
|
||||
}
|
||||
|
||||
AggNode* PercentRankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
|
||||
@ -323,39 +320,22 @@ AggNode* CumeDistWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
impure->vlux_count = 0;
|
||||
}
|
||||
|
||||
void CumeDistWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
|
||||
++impure->vlux_count;
|
||||
}
|
||||
|
||||
dsc* CumeDistWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
impure->make_double(0);
|
||||
}
|
||||
|
||||
dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
double n = impure->vlux_count;
|
||||
double frameSize = window->getFrameSize();
|
||||
double partitionSize = window->getPartitionSize();
|
||||
|
||||
impure->make_double(n / partitionSize);
|
||||
impure->vlu_misc.vlu_double = frameSize / partitionSize;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
@ -402,30 +382,18 @@ ValueExprNode* RowNumberWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c
|
||||
return FB_NEW_POOL(*tdbb->getDefaultPool()) RowNumberWinNode(*tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
}
|
||||
|
||||
void RowNumberWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* RowNumberWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* /*window*/) const
|
||||
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
++impure->vlu_misc.vlu_int64;
|
||||
impure->vlu_misc.vlu_int64 = window->getRecordPosition() - window->getPartitionStart() + 1;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
@ -474,36 +442,17 @@ ValueExprNode* FirstValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) cons
|
||||
return node;
|
||||
}
|
||||
|
||||
void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
}
|
||||
|
||||
void FirstValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* FirstValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
}
|
||||
|
||||
dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
SINT64 records = impure->vlu_misc.vlu_int64++;
|
||||
|
||||
if (!window->move(-records))
|
||||
{
|
||||
window->move(0); // Come back to our row.
|
||||
if (!window->moveWithinFrame(-(window->getRecordPosition() - window->getFrameStart())))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, arg);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
@ -557,23 +506,16 @@ ValueExprNode* LastValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
return node;
|
||||
}
|
||||
|
||||
void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
}
|
||||
|
||||
void LastValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* LastValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
}
|
||||
|
||||
dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
if (!window->move(0))
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
if (!window->moveWithinFrame(window->getFrameEnd() - window->getRecordPosition()))
|
||||
return NULL;
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, arg);
|
||||
@ -641,32 +583,18 @@ ValueExprNode* NthValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
return node;
|
||||
}
|
||||
|
||||
void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
impure->make_int64(0, 0);
|
||||
}
|
||||
|
||||
void NthValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* NthValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
|
||||
window->move(0); // Come back to our row because row may reference columns.
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, row);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
@ -682,20 +610,12 @@ dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow*
|
||||
const SLONG fromPos = desc ? MOV_get_long(desc, 0) : FROM_FIRST;
|
||||
|
||||
if (fromPos == FROM_FIRST)
|
||||
{
|
||||
if (records > ++impure->vlu_misc.vlu_int64)
|
||||
return NULL;
|
||||
|
||||
records -= impure->vlu_misc.vlu_int64;
|
||||
}
|
||||
records += -(window->getRecordPosition() - window->getFrameStart()) - 1;
|
||||
else
|
||||
records = impure->vlu_misc.vlu_int64 - records + 1;
|
||||
records = window->getFrameEnd() - window->getRecordPosition() - records + 1;
|
||||
|
||||
if (!window->move(records))
|
||||
{
|
||||
window->move(0); // Come back to our row.
|
||||
if (!window->moveWithinFrame(records))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = EVL_expr(tdbb, request, arg);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
@ -757,24 +677,13 @@ void LagLeadWinNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
|
||||
arg->getDesc(tdbb, csb, desc);
|
||||
}
|
||||
|
||||
void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
}
|
||||
|
||||
void LagLeadWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* LagLeadWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
}
|
||||
|
||||
dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
window->move(0); // Come back to our row because rows may reference columns.
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, rows);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
@ -786,10 +695,8 @@ dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* w
|
||||
Arg::Num(2) << Arg::Str(aggInfo.name));
|
||||
}
|
||||
|
||||
if (!window->move(records * direction))
|
||||
if (!window->moveWithinPartition(records * direction))
|
||||
{
|
||||
window->move(0); // Come back to our row because outExpr may reference columns.
|
||||
|
||||
desc = EVL_expr(tdbb, request, outExpr);
|
||||
if (!desc || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
@ -924,12 +831,9 @@ AggNode* NTileWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
return this;
|
||||
}
|
||||
|
||||
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
AggNode::aggInit(tdbb, request, aggType);
|
||||
|
||||
if (aggType != AGG_TYPE_GROUP)
|
||||
return;
|
||||
AggNode::aggInit(tdbb, request);
|
||||
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
ThisImpure* thisImpure = request->getImpure<ThisImpure>(thisImpureOffset);
|
||||
@ -956,15 +860,6 @@ void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) c
|
||||
}
|
||||
}
|
||||
|
||||
void NTileWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
|
||||
{
|
||||
}
|
||||
|
||||
dsc* NTileWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
|
||||
{
|
||||
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
|
||||
@ -986,7 +881,7 @@ dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow*
|
||||
|
||||
++n;
|
||||
|
||||
impure->make_int64(result);
|
||||
impure->vlu_misc.vlu_int64 = result;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,17 @@ class DenseRankWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit DenseRankWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -55,13 +60,18 @@ class RankWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit RankWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
@ -78,25 +88,28 @@ class PercentRankWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit PercentRankWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
protected:
|
||||
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
|
||||
|
||||
private:
|
||||
USHORT tempImpure;
|
||||
};
|
||||
|
||||
// CUME_DIST function.
|
||||
@ -105,20 +118,18 @@ class CumeDistWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit CumeDistWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -132,19 +143,17 @@ class RowNumberWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit RowNumberWinNode(MemoryPool& pool);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -158,19 +167,17 @@ class FirstValueWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit FirstValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -186,19 +193,17 @@ class LastValueWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit LastValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -222,19 +227,17 @@ public:
|
||||
explicit NthValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL,
|
||||
ValueExprNode* aRow = NULL, ValueExprNode* aFrom = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
@ -255,19 +258,16 @@ public:
|
||||
explicit LagLeadWinNode(MemoryPool& pool, const AggInfo& aAggInfo, int aDirection,
|
||||
ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, ValueExprNode* aOutExpr = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const = 0;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
protected:
|
||||
@ -323,20 +323,18 @@ class NTileWinNode : public WinFuncNode
|
||||
public:
|
||||
explicit NTileWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
|
||||
|
||||
virtual unsigned getCapabilities() const
|
||||
{
|
||||
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
|
||||
}
|
||||
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
|
||||
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
|
||||
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
|
||||
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual void aggSetup(bool& wantWinPass) const
|
||||
{
|
||||
wantWinPass = true;
|
||||
}
|
||||
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
|
||||
|
||||
|
@ -82,6 +82,7 @@ namespace Jrd
|
||||
class TransactionNode;
|
||||
class ValueExprNode;
|
||||
class ValueListNode;
|
||||
class WindowClause;
|
||||
class jrd_tra;
|
||||
class jrd_req;
|
||||
class blb;
|
||||
@ -674,10 +675,10 @@ public:
|
||||
|
||||
struct PartitionMap
|
||||
{
|
||||
PartitionMap(ValueListNode* aPartition, ValueListNode* aOrder)
|
||||
PartitionMap(ValueListNode* aPartition, WindowClause* aWindow)
|
||||
: partition(aPartition),
|
||||
partitionRemapped(NULL),
|
||||
order(aOrder),
|
||||
window(aWindow),
|
||||
map(NULL),
|
||||
context(0)
|
||||
{
|
||||
@ -685,7 +686,7 @@ struct PartitionMap
|
||||
|
||||
NestConst<ValueListNode> partition;
|
||||
NestConst<ValueListNode> partitionRemapped;
|
||||
NestConst<ValueListNode> order;
|
||||
NestConst<WindowClause> window;
|
||||
dsql_map* map;
|
||||
USHORT context;
|
||||
};
|
||||
@ -756,7 +757,7 @@ public:
|
||||
|
||||
bool getImplicitJoinField(const Firebird::MetaName& name, NestConst<ValueExprNode>& node);
|
||||
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch,
|
||||
ValueListNode* partitionNode, ValueListNode* orderNode);
|
||||
ValueListNode* partitionNode, WindowClause* windowNode);
|
||||
};
|
||||
|
||||
// Flag values for ctx_flags
|
||||
|
@ -561,7 +561,7 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse)
|
||||
}
|
||||
|
||||
if (rse->dsqlOrder)
|
||||
GEN_sort(dsqlScratch, rse->dsqlOrder);
|
||||
GEN_sort(dsqlScratch, blr_sort, rse->dsqlOrder);
|
||||
|
||||
if (rse->dsqlDistinct)
|
||||
{
|
||||
@ -587,28 +587,32 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse)
|
||||
|
||||
|
||||
// Generate a sort clause.
|
||||
void GEN_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* list)
|
||||
void GEN_sort(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, ValueListNode* list)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_sort);
|
||||
dsqlScratch->appendUChar(list->items.getCount());
|
||||
dsqlScratch->appendUChar(blrVerb);
|
||||
dsqlScratch->appendUChar(list ? list->items.getCount() : 0);
|
||||
|
||||
NestConst<ValueExprNode>* ptr = list->items.begin();
|
||||
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
|
||||
if (list)
|
||||
{
|
||||
OrderNode* orderNode = (*ptr)->as<OrderNode>();
|
||||
NestConst<ValueExprNode>* ptr = list->items.begin();
|
||||
|
||||
switch (orderNode->nullsPlacement)
|
||||
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
|
||||
{
|
||||
case OrderNode::NULLS_FIRST:
|
||||
dsqlScratch->appendUChar(blr_nullsfirst);
|
||||
break;
|
||||
case OrderNode::NULLS_LAST:
|
||||
dsqlScratch->appendUChar(blr_nullslast);
|
||||
break;
|
||||
}
|
||||
OrderNode* orderNode = (*ptr)->as<OrderNode>();
|
||||
|
||||
dsqlScratch->appendUChar((orderNode->descending ? blr_descending : blr_ascending));
|
||||
GEN_expr(dsqlScratch, orderNode->value);
|
||||
switch (orderNode->nullsPlacement)
|
||||
{
|
||||
case OrderNode::NULLS_FIRST:
|
||||
dsqlScratch->appendUChar(blr_nullsfirst);
|
||||
break;
|
||||
case OrderNode::NULLS_LAST:
|
||||
dsqlScratch->appendUChar(blr_nullslast);
|
||||
break;
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar((orderNode->descending ? blr_descending : blr_ascending));
|
||||
GEN_expr(dsqlScratch, orderNode->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ void GEN_parameter(Jrd::DsqlCompilerScratch*, const Jrd::dsql_par*);
|
||||
void GEN_port(Jrd::DsqlCompilerScratch*, Jrd::dsql_msg*);
|
||||
void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*);
|
||||
void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*);
|
||||
void GEN_sort(Jrd::DsqlCompilerScratch*, Jrd::ValueListNode*);
|
||||
void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*);
|
||||
void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*);
|
||||
|
||||
#endif // DSQL_GEN_PROTO_H
|
||||
|
127
src/dsql/parse.y
127
src/dsql/parse.y
@ -592,12 +592,20 @@ using namespace Firebird;
|
||||
// tokens added for Firebird 4.0
|
||||
|
||||
%token <metaNamePtr> CUME_DIST
|
||||
%token <metaNamePtr> ERROR_MESSAGE
|
||||
%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 +663,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 +3971,7 @@ keyword_or_column
|
||||
| UPDATING
|
||||
| VAR_SAMP
|
||||
| VAR_POP
|
||||
| UNBOUNDED // added in FB 4.0
|
||||
;
|
||||
|
||||
col_opt
|
||||
@ -5058,7 +5071,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 +5079,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 +5606,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 +5822,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 +5859,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;
|
||||
@ -6821,6 +6839,10 @@ internal_info
|
||||
{ $$ = newNode<InternalInfoNode>(MAKE_const_slong(INFO_TYPE_SQLSTATE)); }
|
||||
| ROW_COUNT
|
||||
{ $$ = newNode<InternalInfoNode>(MAKE_const_slong(INFO_TYPE_ROWS_AFFECTED)); }
|
||||
| EXCEPTION
|
||||
{ $$ = newNode<InternalInfoNode>(MAKE_const_slong(INFO_TYPE_EXCEPTION)); }
|
||||
| ERROR_MESSAGE
|
||||
{ $$ = newNode<InternalInfoNode>(MAKE_const_slong(INFO_TYPE_ERROR_MSG)); }
|
||||
;
|
||||
|
||||
%type <intlStringPtr> sql_string
|
||||
@ -7053,7 +7075,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 +7085,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,13 +7963,20 @@ 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
|
||||
;
|
||||
| ERROR_MESSAGE
|
||||
| TIES
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
@ -300,12 +300,12 @@ bool InvalidReferenceFinder::visit(ExprNode* node)
|
||||
|
||||
|
||||
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
||||
ValueListNode* aPartitionNode, ValueListNode* aOrderNode)
|
||||
ValueListNode* aPartitionNode, WindowClause* aWindowNode)
|
||||
: dsqlScratch(aDsqlScratch),
|
||||
context(aContext),
|
||||
window(aWindow),
|
||||
partitionNode(aPartitionNode),
|
||||
orderNode(aOrderNode),
|
||||
windowNode(aWindowNode),
|
||||
currentLevel(dsqlScratch->scopeLevel)
|
||||
{
|
||||
DEV_BLKCHK(dsqlScratch, dsql_type_req);
|
||||
@ -2213,7 +2213,7 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod
|
||||
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch, partitionMap->partition);
|
||||
|
||||
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->partition,
|
||||
partitionMap->order);
|
||||
partitionMap->window);
|
||||
ExprNode::doDsqlFieldRemapper(remapper2, partitionMap->partitionRemapped);
|
||||
}
|
||||
}
|
||||
@ -2802,7 +2802,7 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in
|
||||
|
||||
// Post an item to a map for a context.
|
||||
DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
|
||||
ValueListNode* partitionNode, ValueListNode* orderNode)
|
||||
ValueListNode* partitionNode, WindowClause* windowNode)
|
||||
{
|
||||
DEV_BLKCHK(node, dsql_type_nod);
|
||||
DEV_BLKCHK(context, dsql_type_ctx);
|
||||
@ -2814,7 +2814,7 @@ DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* nod
|
||||
|
||||
if (dsqlScratch->processingWindow)
|
||||
{
|
||||
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, orderNode);
|
||||
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, windowNode);
|
||||
map = partitionMap->map;
|
||||
}
|
||||
else
|
||||
@ -2931,7 +2931,7 @@ bool dsql_ctx::getImplicitJoinField(const MetaName& name, NestConst<ValueExprNod
|
||||
|
||||
// Returns (creating, if necessary) the PartitionMap of a given partition (that may be NULL).
|
||||
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueListNode* partitionNode,
|
||||
ValueListNode* orderNode)
|
||||
WindowClause* windowNode)
|
||||
{
|
||||
thread_db* tdbb = JRD_get_thread_data();
|
||||
|
||||
@ -2942,7 +2942,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
++i)
|
||||
{
|
||||
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
|
||||
PASS1_node_match((*i)->order, orderNode, false))
|
||||
PASS1_node_match((*i)->window, windowNode, false))
|
||||
{
|
||||
partitionMap = *i;
|
||||
}
|
||||
@ -2950,7 +2950,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
|
||||
|
||||
if (!partitionMap)
|
||||
{
|
||||
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, orderNode);
|
||||
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, windowNode);
|
||||
ctx_win_maps.add(partitionMap);
|
||||
partitionMap->context = dsqlScratch->contextNumber++;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ Jrd::ValueExprNode* PASS1_lookup_alias(Jrd::DsqlCompilerScratch*, const Firebird
|
||||
Jrd::dsql_ctx* PASS1_make_context(Jrd::DsqlCompilerScratch* statement, Jrd::RecordSourceNode* relationNode);
|
||||
bool PASS1_node_match(const Jrd::ExprNode*, const Jrd::ExprNode*, bool);
|
||||
Jrd::DsqlMapNode* PASS1_post_map(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, Jrd::dsql_ctx*,
|
||||
Jrd::ValueListNode*, Jrd::ValueListNode*);
|
||||
Jrd::ValueListNode*, Jrd::WindowClause*);
|
||||
Jrd::RecordSourceNode* PASS1_relation(Jrd::DsqlCompilerScratch*, Jrd::RecordSourceNode*);
|
||||
Jrd::RseNode* PASS1_rse(Jrd::DsqlCompilerScratch*, Jrd::SelectExprNode*, bool);
|
||||
bool PASS1_set_parameter_type(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, const dsc*, bool);
|
||||
|
@ -817,6 +817,11 @@ static const struct {
|
||||
{"crypt_checksum", 335545113},
|
||||
{"not_dba", 335545114},
|
||||
{"no_cursor", 335545115},
|
||||
{"dsql_window_incompat_frames", 335545116},
|
||||
{"dsql_window_range_multi_key", 335545117},
|
||||
{"dsql_window_range_inv_key_type", 335545118},
|
||||
{"dsql_window_frame_value_inv_type", 335545119},
|
||||
{"window_frame_value_invalid", 335545120},
|
||||
{"gfix_db_name", 335740929},
|
||||
{"gfix_invalid_sw", 335740930},
|
||||
{"gfix_incmp_sw", 335740932},
|
||||
|
@ -851,6 +851,11 @@ const ISC_STATUS isc_miss_prvlg = 335545112L;
|
||||
const ISC_STATUS isc_crypt_checksum = 335545113L;
|
||||
const ISC_STATUS isc_not_dba = 335545114L;
|
||||
const ISC_STATUS isc_no_cursor = 335545115L;
|
||||
const ISC_STATUS isc_dsql_window_incompat_frames = 335545116L;
|
||||
const ISC_STATUS isc_dsql_window_range_multi_key = 335545117L;
|
||||
const ISC_STATUS isc_dsql_window_range_inv_key_type = 335545118L;
|
||||
const ISC_STATUS isc_dsql_window_frame_value_inv_type = 335545119L;
|
||||
const ISC_STATUS isc_window_frame_value_invalid = 335545120L;
|
||||
const ISC_STATUS isc_gfix_db_name = 335740929L;
|
||||
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
|
||||
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
|
||||
@ -1322,7 +1327,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
|
||||
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
|
||||
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
|
||||
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
|
||||
const ISC_STATUS isc_err_max = 1266;
|
||||
const ISC_STATUS isc_err_max = 1271;
|
||||
|
||||
#else /* c definitions */
|
||||
|
||||
@ -2143,6 +2148,11 @@ const ISC_STATUS isc_err_max = 1266;
|
||||
#define isc_crypt_checksum 335545113L
|
||||
#define isc_not_dba 335545114L
|
||||
#define isc_no_cursor 335545115L
|
||||
#define isc_dsql_window_incompat_frames 335545116L
|
||||
#define isc_dsql_window_range_multi_key 335545117L
|
||||
#define isc_dsql_window_range_inv_key_type 335545118L
|
||||
#define isc_dsql_window_frame_value_inv_type 335545119L
|
||||
#define isc_window_frame_value_invalid 335545120L
|
||||
#define isc_gfix_db_name 335740929L
|
||||
#define isc_gfix_invalid_sw 335740930L
|
||||
#define isc_gfix_incmp_sw 335740932L
|
||||
@ -2614,7 +2624,7 @@ const ISC_STATUS isc_err_max = 1266;
|
||||
#define isc_trace_switch_param_miss 337182758L
|
||||
#define isc_trace_param_act_notcompat 337182759L
|
||||
#define isc_trace_mandatory_switch_miss 337182760L
|
||||
#define isc_err_max 1266
|
||||
#define isc_err_max 1271
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -820,6 +820,11 @@ Data source : @4"}, /* eds_statement */
|
||||
{335545113, "Invalid or missing checksum of encrypted database"}, /* crypt_checksum */
|
||||
{335545114, "You must have SYSDBA rights at this server"}, /* not_dba */
|
||||
{335545115, "Cannot open cursor for non-SELECT statement"}, /* no_cursor */
|
||||
{335545116, "If <window frame bound 1> specifies @1, then <window frame bound 2> shall not specify @2"}, /* dsql_window_incompat_frames */
|
||||
{335545117, "RANGE based window with <expr> {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value"}, /* dsql_window_range_multi_key */
|
||||
{335545118, "RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types"}, /* dsql_window_range_inv_key_type */
|
||||
{335545119, "Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type"}, /* dsql_window_frame_value_inv_type */
|
||||
{335545120, "Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative"}, /* window_frame_value_invalid */
|
||||
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
|
||||
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
|
||||
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */
|
||||
|
@ -816,6 +816,11 @@ static const struct {
|
||||
{335545113, -902}, /* 793 crypt_checksum */
|
||||
{335545114, -902}, /* 794 not_dba */
|
||||
{335545115, -901}, /* 795 no_cursor */
|
||||
{335545116, -104}, /* 796 dsql_window_incompat_frames */
|
||||
{335545117, -104}, /* 797 dsql_window_range_multi_key */
|
||||
{335545118, -104}, /* 798 dsql_window_range_inv_key_type */
|
||||
{335545119, -104}, /* 799 dsql_window_frame_value_inv_type */
|
||||
{335545120, -833}, /* 800 window_frame_value_invalid */
|
||||
{335740929, -901}, /* 1 gfix_db_name */
|
||||
{335740930, -901}, /* 2 gfix_invalid_sw */
|
||||
{335740932, -901}, /* 4 gfix_incmp_sw */
|
||||
|
@ -816,6 +816,11 @@ static const struct {
|
||||
{335545113, "XX000"}, // 793 crypt_checksum
|
||||
{335545114, "28000"}, // 794 not_dba
|
||||
{335545115, "07005"}, // 795 no_cursor
|
||||
{335545116, "42000"}, // 796 dsql_window_incompat_frames
|
||||
{335545117, "42000"}, // 797 dsql_window_range_multi_key
|
||||
{335545118, "42000"}, // 798 dsql_window_range_inv_key_type
|
||||
{335545119, "42000"}, // 799 dsql_window_frame_value_inv_type
|
||||
{335545120, "42000"}, // 800 window_frame_value_invalid
|
||||
{335740929, "00000"}, // 1 gfix_db_name
|
||||
{335740930, "00000"}, // 2 gfix_invalid_sw
|
||||
{335740932, "00000"}, // 4 gfix_incmp_sw
|
||||
|
@ -45,7 +45,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);
|
||||
@ -1249,7 +1249,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;
|
||||
}
|
||||
@ -1329,18 +1329,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();
|
||||
@ -1348,21 +1355,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
|
||||
@ -1382,12 +1426,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;
|
||||
|
||||
@ -1397,7 +1441,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)
|
||||
@ -1631,7 +1675,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;
|
||||
@ -1978,10 +2022,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;
|
||||
}
|
||||
@ -1997,27 +2056,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
|
||||
@ -2030,27 +2211,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;
|
||||
@ -2063,25 +2251,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;
|
||||
@ -2098,11 +2287,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;
|
||||
}
|
||||
@ -2112,26 +2301,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;
|
||||
@ -2141,21 +2331,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
|
||||
}
|
||||
|
||||
@ -2167,26 +2357,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);
|
||||
@ -2204,11 +2394,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3295,12 +3485,16 @@ static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, Recor
|
||||
}
|
||||
|
||||
// Parse a MAP clause for a union or global aggregate expression.
|
||||
static MapNode* parseMap(thread_db* tdbb, CompilerScratch* csb, StreamType stream)
|
||||
static MapNode* parseMap(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
bool parseHeader)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
if (csb->csb_blr_reader.getByte() != blr_map)
|
||||
PAR_syntax_error(csb, "blr_map");
|
||||
if (parseHeader)
|
||||
{
|
||||
if (csb->csb_blr_reader.getByte() != blr_map)
|
||||
PAR_syntax_error(csb, "blr_map");
|
||||
}
|
||||
|
||||
unsigned int count = csb->csb_blr_reader.getWord();
|
||||
MapNode* node = FB_NEW_POOL(csb->csb_pool) MapNode(csb->csb_pool);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "../common/classes/objects_array.h"
|
||||
#include "../common/classes/NestConst.h"
|
||||
#include "../common/classes/QualifiedName.h"
|
||||
#include "../dsql/ExprNodes.h"
|
||||
#include "../jrd/jrd.h"
|
||||
#include "../jrd/exe.h"
|
||||
#include "../dsql/Visitors.h"
|
||||
@ -70,6 +71,14 @@ public:
|
||||
bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream);
|
||||
void findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList);
|
||||
|
||||
int getEffectiveNullOrder(unsigned index) const
|
||||
{
|
||||
if (descending[index])
|
||||
return nullOrder[index] == rse_nulls_default ? rse_nulls_last : nullOrder[index];
|
||||
else
|
||||
return nullOrder[index] == rse_nulls_default ? rse_nulls_first : nullOrder[index];
|
||||
}
|
||||
|
||||
public:
|
||||
bool unique; // sorts using unique key - for distinct and group by
|
||||
NestValueArray expressions; // sort expressions
|
||||
@ -460,7 +469,7 @@ public:
|
||||
virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream);
|
||||
|
||||
private:
|
||||
void genMap(DsqlCompilerScratch* dsqlScratch, dsql_map* map);
|
||||
void genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map);
|
||||
|
||||
RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, BoolExprNodeStack* parentStack,
|
||||
StreamType shellStream);
|
||||
@ -543,10 +552,11 @@ private:
|
||||
class WindowSourceNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_WINDOW>
|
||||
{
|
||||
public:
|
||||
struct Partition
|
||||
struct Window
|
||||
{
|
||||
explicit Partition(MemoryPool&)
|
||||
: stream(INVALID_STREAM)
|
||||
explicit Window(MemoryPool&)
|
||||
: stream(INVALID_STREAM),
|
||||
exclusion(WindowClause::EXCLUDE_NO_OTHERS)
|
||||
{
|
||||
}
|
||||
|
||||
@ -555,19 +565,22 @@ public:
|
||||
NestConst<SortNode> regroup;
|
||||
NestConst<SortNode> order;
|
||||
NestConst<MapNode> map;
|
||||
NestConst<WindowClause::FrameExtent> frameExtent;
|
||||
WindowClause::Exclusion exclusion;
|
||||
};
|
||||
|
||||
explicit WindowSourceNode(MemoryPool& pool)
|
||||
: TypedNode<RecordSourceNode, RecordSourceNode::TYPE_WINDOW>(pool),
|
||||
rse(NULL),
|
||||
partitions(pool)
|
||||
windows(pool)
|
||||
{
|
||||
}
|
||||
|
||||
static WindowSourceNode* parse(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
private:
|
||||
void parsePartitionBy(thread_db* tdbb, CompilerScratch* csb);
|
||||
void parseLegacyPartitionBy(thread_db* tdbb, CompilerScratch* csb);
|
||||
void parseWindow(thread_db* tdbb, CompilerScratch* csb);
|
||||
|
||||
public:
|
||||
virtual StreamType getStream() const
|
||||
@ -602,7 +615,7 @@ public:
|
||||
|
||||
private:
|
||||
NestConst<RseNode> rse;
|
||||
Firebird::ObjectsArray<Partition> partitions;
|
||||
Firebird::ObjectsArray<Window> windows;
|
||||
};
|
||||
|
||||
class RseNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_RSE>
|
||||
|
@ -241,5 +241,6 @@ static const struct
|
||||
{"subfunc", function},
|
||||
{"record_version2", byte_line},
|
||||
{"gen_id2", gen_id2}, // 210
|
||||
{"window_win", window_win},
|
||||
{0, 0}
|
||||
};
|
||||
|
@ -407,4 +407,17 @@
|
||||
#define blr_record_version2 (unsigned char) 209
|
||||
#define blr_gen_id2 (unsigned char) 210 // NEXT VALUE FOR generator
|
||||
|
||||
// FB 4.0 specific BLR
|
||||
|
||||
#define blr_window_win (unsigned char) 211
|
||||
|
||||
// subcodes of blr_window_win
|
||||
#define blr_window_win_partition (unsigned char) 1
|
||||
#define blr_window_win_order (unsigned char) 2
|
||||
#define blr_window_win_map (unsigned char) 3
|
||||
#define blr_window_win_extent_unit (unsigned char) 4
|
||||
#define blr_window_win_extent_frame_bound (unsigned char) 5
|
||||
#define blr_window_win_extent_frame_value (unsigned char) 6
|
||||
#define blr_window_win_exclusion (unsigned char) 7
|
||||
|
||||
#endif // JRD_BLR_H
|
||||
|
@ -3,16 +3,16 @@
|
||||
*** DO NOT EDIT ***
|
||||
TO CHANGE ANY INFORMATION IN HERE PLEASE
|
||||
EDIT src/misc/writeBuildNum.sh
|
||||
FORMAL BUILD NUMBER:354
|
||||
FORMAL BUILD NUMBER:366
|
||||
*/
|
||||
|
||||
#define PRODUCT_VER_STRING "4.0.0.354"
|
||||
#define FILE_VER_STRING "WI-T4.0.0.354"
|
||||
#define LICENSE_VER_STRING "WI-T4.0.0.354"
|
||||
#define FILE_VER_NUMBER 4, 0, 0, 354
|
||||
#define PRODUCT_VER_STRING "4.0.0.366"
|
||||
#define FILE_VER_STRING "WI-T4.0.0.366"
|
||||
#define LICENSE_VER_STRING "WI-T4.0.0.366"
|
||||
#define FILE_VER_NUMBER 4, 0, 0, 366
|
||||
#define FB_MAJOR_VER "4"
|
||||
#define FB_MINOR_VER "0"
|
||||
#define FB_REV_NO "0"
|
||||
#define FB_BUILD_NO "354"
|
||||
#define FB_BUILD_NO "366"
|
||||
#define FB_BUILD_TYPE "T"
|
||||
#define FB_BUILD_SUFFIX "Firebird 4.0 Unstable"
|
||||
|
@ -287,6 +287,8 @@ enum InfoType
|
||||
INFO_TYPE_ROWS_AFFECTED = 5,
|
||||
INFO_TYPE_TRIGGER_ACTION = 6,
|
||||
INFO_TYPE_SQLSTATE = 7,
|
||||
INFO_TYPE_EXCEPTION = 8,
|
||||
INFO_TYPE_ERROR_MSG = 9,
|
||||
MAX_INFO_TYPE
|
||||
};
|
||||
|
||||
@ -452,4 +454,7 @@ const int OPT_STATIC_ITEMS = 64;
|
||||
const int WITH_GRANT_OPTION = 1;
|
||||
const int WITH_ADMIN_OPTION = 2;
|
||||
|
||||
// Max length of the string returned by ERROR_TEXT context variable
|
||||
const USHORT MAX_ERROR_MSG_LENGTH = 1024 * METADATA_BYTES_PER_CHAR; // 1024 UTF-8 characters
|
||||
|
||||
#endif // JRD_CONSTANTS_H
|
||||
|
@ -192,6 +192,30 @@ void StatusXcp::as_sqlstate(char* sqlstate) const
|
||||
fb_sqlstate(sqlstate, status->getErrors());
|
||||
}
|
||||
|
||||
SLONG StatusXcp::as_xcpcode() const
|
||||
{
|
||||
return (status->getErrors()[1] == isc_except) ? (SLONG) status->getErrors()[3] : 0;
|
||||
}
|
||||
|
||||
string StatusXcp::as_text() const
|
||||
{
|
||||
const ISC_STATUS* status_ptr = status->getErrors();
|
||||
|
||||
string errorText;
|
||||
|
||||
TEXT buffer[BUFFER_LARGE];
|
||||
while (fb_interpret(buffer, sizeof(buffer), &status_ptr))
|
||||
{
|
||||
if (errorText.hasData())
|
||||
errorText += "\n";
|
||||
|
||||
errorText += buffer;
|
||||
}
|
||||
|
||||
return errorText;
|
||||
}
|
||||
|
||||
|
||||
static void execute_looper(thread_db*, jrd_req*, jrd_tra*, const StmtNode*, jrd_req::req_s);
|
||||
static void looper_seh(thread_db*, jrd_req*, const StmtNode*);
|
||||
static void release_blobs(thread_db*, jrd_req*);
|
||||
|
@ -631,6 +631,8 @@ public:
|
||||
SLONG as_gdscode() const;
|
||||
SLONG as_sqlcode() const;
|
||||
void as_sqlstate(char*) const;
|
||||
SLONG as_xcpcode() const;
|
||||
Firebird::string as_text() const;
|
||||
};
|
||||
|
||||
// must correspond to the declared size of RDB$EXCEPTIONS.RDB$MESSAGE
|
||||
|
@ -1402,7 +1402,7 @@ SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
|
||||
if (count == 0 && nullForEmpty)
|
||||
return NULL;
|
||||
|
||||
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp, count);
|
||||
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp == blr_sort, count);
|
||||
|
||||
if (blrOp != blr_sort)
|
||||
sort->unique = true;
|
||||
@ -1413,8 +1413,7 @@ SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
|
||||
|
||||
// Parse the internals of a sort clause. This is used for blr_sort, blr_project, blr_group_by
|
||||
// and blr_partition_by.
|
||||
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
|
||||
USHORT count)
|
||||
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, bool allClauses, USHORT count)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
@ -1427,7 +1426,7 @@ SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
if (blrOp == blr_sort)
|
||||
if (allClauses)
|
||||
{
|
||||
UCHAR code = csb->csb_blr_reader.getByte();
|
||||
|
||||
|
@ -70,8 +70,7 @@ void PAR_procedure_parms(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*
|
||||
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*, SSHORT);
|
||||
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*);
|
||||
Jrd::SortNode* PAR_sort(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR, bool);
|
||||
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR blrOp,
|
||||
USHORT);
|
||||
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, bool, USHORT);
|
||||
SLONG PAR_symbol_to_gdscode(const Firebird::string&);
|
||||
|
||||
typedef Jrd::DmlNode* (*NodeParseFunc)(Jrd::thread_db* tdbb, MemoryPool& pool,
|
||||
|
@ -37,71 +37,51 @@ using namespace Jrd;
|
||||
// Data access: aggregation
|
||||
// ------------------------
|
||||
|
||||
// Note that we can have NULL order here, in case of window function with shouldCallWinPass
|
||||
// returning true, with partition, and without order. Example: ROW_NUMBER() OVER (PARTITION BY N).
|
||||
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, BaseBufferedStream* next,
|
||||
const NestValueArray* order)
|
||||
template <typename ThisType, typename NextType>
|
||||
BaseAggWinStream<ThisType, NextType>::BaseAggWinStream(thread_db* tdbb, CompilerScratch* csb,
|
||||
StreamType stream, const NestValueArray* group, MapNode* groupMap,
|
||||
bool oneRowWhenEmpty, NextType* next)
|
||||
: RecordStream(csb, stream),
|
||||
m_bufferedStream(next),
|
||||
m_next(m_bufferedStream),
|
||||
m_group(group),
|
||||
m_map(map),
|
||||
m_order(order),
|
||||
m_winPassSources(csb->csb_pool),
|
||||
m_winPassTargets(csb->csb_pool)
|
||||
{
|
||||
init(tdbb, csb);
|
||||
}
|
||||
|
||||
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next)
|
||||
: RecordStream(csb, stream),
|
||||
m_bufferedStream(NULL),
|
||||
m_next(next),
|
||||
m_group(group),
|
||||
m_map(map),
|
||||
m_order(NULL),
|
||||
m_winPassSources(csb->csb_pool),
|
||||
m_winPassTargets(csb->csb_pool)
|
||||
m_groupMap(groupMap),
|
||||
m_oneRowWhenEmpty(oneRowWhenEmpty)
|
||||
{
|
||||
init(tdbb, csb);
|
||||
fb_assert(m_next);
|
||||
m_impure = CMP_impure(csb, sizeof(typename ThisType::Impure));
|
||||
}
|
||||
|
||||
void AggregatedStream::open(thread_db* tdbb) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::open(thread_db* tdbb) const
|
||||
{
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
impure->irsb_flags = irsb_open;
|
||||
|
||||
impure->state = STATE_GROUPING;
|
||||
impure->lastGroup = false;
|
||||
impure->partitionBlock.startPosition = impure->partitionBlock.endPosition =
|
||||
impure->partitionBlock.pending = 0;
|
||||
impure->orderBlock = impure->partitionBlock;
|
||||
|
||||
VIO_record(tdbb, &request->req_rpb[m_stream], m_format, tdbb->getDefaultPool());
|
||||
|
||||
unsigned impureCount = m_group ? m_group->getCount() : 0;
|
||||
impureCount += m_order ? m_order->getCount() : 0;
|
||||
|
||||
if (!impure->impureValues && impureCount > 0)
|
||||
if (!impure->groupValues && impureCount > 0)
|
||||
{
|
||||
impure->impureValues = FB_NEW_POOL(*tdbb->getDefaultPool()) impure_value[impureCount];
|
||||
memset(impure->impureValues, 0, sizeof(impure_value) * impureCount);
|
||||
impure->groupValues = FB_NEW_POOL(*tdbb->getDefaultPool()) impure_value[impureCount];
|
||||
memset(impure->groupValues, 0, sizeof(impure_value) * impureCount);
|
||||
}
|
||||
|
||||
m_next->open(tdbb);
|
||||
}
|
||||
|
||||
void AggregatedStream::close(thread_db* tdbb) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::close(thread_db* tdbb) const
|
||||
{
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
|
||||
invalidateRecords(request);
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
if (impure->irsb_flags & irsb_open)
|
||||
{
|
||||
@ -111,232 +91,51 @@ void AggregatedStream::close(thread_db* tdbb) const
|
||||
}
|
||||
}
|
||||
|
||||
bool AggregatedStream::getRecord(thread_db* tdbb) const
|
||||
{
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, 0, true);
|
||||
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
record_param* const rpb = &request->req_rpb[m_stream];
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
if (!(impure->irsb_flags & irsb_open))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_bufferedStream) // Is that a window stream?
|
||||
{
|
||||
const FB_UINT64 position = m_bufferedStream->getPosition(request);
|
||||
|
||||
if (impure->orderBlock.pending == 0)
|
||||
{
|
||||
if (impure->partitionBlock.pending == 0)
|
||||
{
|
||||
if (impure->lastGroup || !evaluateGroup(tdbb, AGG_TYPE_GROUP, MAX_UINT64))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
impure->partitionBlock.startPosition = position;
|
||||
impure->partitionBlock.endPosition = m_bufferedStream->getPosition(request) - 1 -
|
||||
(impure->state == STATE_FETCHED ? 1 : 0);
|
||||
impure->partitionBlock.pending =
|
||||
impure->partitionBlock.endPosition - impure->partitionBlock.startPosition + 1;
|
||||
|
||||
fb_assert(impure->partitionBlock.pending > 0);
|
||||
|
||||
m_bufferedStream->locate(tdbb, position);
|
||||
impure->state = STATE_GROUPING;
|
||||
|
||||
impure->lastGroup = impure->state == STATE_EOF;
|
||||
}
|
||||
|
||||
// Check if we need to re-aggregate by the ORDER BY clause.
|
||||
if (!m_order && m_winPassSources.isEmpty())
|
||||
impure->orderBlock = impure->partitionBlock;
|
||||
else
|
||||
{
|
||||
if (!evaluateGroup(tdbb, AGG_TYPE_ORDER, impure->partitionBlock.pending))
|
||||
fb_assert(false);
|
||||
|
||||
impure->orderBlock.startPosition = position;
|
||||
impure->orderBlock.endPosition = m_bufferedStream->getPosition(request) - 1 -
|
||||
(impure->state == STATE_FETCHED ? 1 : 0);
|
||||
impure->orderBlock.pending =
|
||||
impure->orderBlock.endPosition - impure->orderBlock.startPosition + 1;
|
||||
|
||||
fb_assert(impure->orderBlock.pending > 0);
|
||||
}
|
||||
|
||||
m_bufferedStream->locate(tdbb, position);
|
||||
impure->state = STATE_GROUPING;
|
||||
}
|
||||
|
||||
fb_assert(impure->orderBlock.pending > 0 && impure->partitionBlock.pending > 0);
|
||||
|
||||
--impure->orderBlock.pending;
|
||||
--impure->partitionBlock.pending;
|
||||
|
||||
if (m_winPassSources.hasData())
|
||||
{
|
||||
SlidingWindow window(tdbb, m_bufferedStream, m_group, request,
|
||||
impure->partitionBlock.startPosition, impure->partitionBlock.endPosition,
|
||||
impure->orderBlock.startPosition, impure->orderBlock.endPosition);
|
||||
dsc* desc;
|
||||
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_winPassSources.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_winPassSources.begin(),
|
||||
*target = m_winPassTargets.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
const AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
const FieldNode* field = (*target)->as<FieldNode>();
|
||||
const USHORT id = field->fieldId;
|
||||
Record* record = request->req_rpb[field->fieldStream].rpb_record;
|
||||
|
||||
desc = aggNode->winPass(tdbb, request, &window);
|
||||
|
||||
if (!desc)
|
||||
record->setNull(id);
|
||||
else
|
||||
{
|
||||
MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
|
||||
record->clearNull(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bufferedStream->getRecord(tdbb))
|
||||
fb_assert(false);
|
||||
|
||||
// If there is no group, we should reassign the map items.
|
||||
if (!m_group)
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
const AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
if (!aggNode)
|
||||
EXE_assignment(tdbb, *source, *target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!evaluateGroup(tdbb, AGG_TYPE_GROUP, MAX_UINT64))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rpb->rpb_number.setValid(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AggregatedStream::refetchRecord(thread_db* tdbb) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::refetchRecord(thread_db* tdbb) const
|
||||
{
|
||||
return m_next->refetchRecord(tdbb);
|
||||
}
|
||||
|
||||
bool AggregatedStream::lockRecord(thread_db* /*tdbb*/) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::lockRecord(thread_db* /*tdbb*/) const
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
return false; // compiler silencer
|
||||
}
|
||||
|
||||
void AggregatedStream::print(thread_db* tdbb, string& plan,
|
||||
bool detailed, unsigned level) const
|
||||
{
|
||||
if (detailed)
|
||||
plan += printIndent(++level) + (m_bufferedStream ? "Window" : "Aggregate");
|
||||
|
||||
m_next->print(tdbb, plan, detailed, level);
|
||||
}
|
||||
|
||||
void AggregatedStream::markRecursive()
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::markRecursive()
|
||||
{
|
||||
m_next->markRecursive();
|
||||
}
|
||||
|
||||
void AggregatedStream::invalidateRecords(jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::invalidateRecords(jrd_req* request) const
|
||||
{
|
||||
m_next->invalidateRecords(request);
|
||||
}
|
||||
|
||||
void AggregatedStream::findUsedStreams(StreamList& streams, bool expandAll) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::findUsedStreams(StreamList& streams,
|
||||
bool expandAll) const
|
||||
{
|
||||
RecordStream::findUsedStreams(streams);
|
||||
|
||||
if (expandAll)
|
||||
m_next->findUsedStreams(streams, true);
|
||||
|
||||
if (m_bufferedStream)
|
||||
m_bufferedStream->findUsedStreams(streams, expandAll);
|
||||
}
|
||||
|
||||
void AggregatedStream::nullRecords(thread_db* tdbb) const
|
||||
{
|
||||
RecordStream::nullRecords(tdbb);
|
||||
|
||||
if (m_bufferedStream)
|
||||
m_bufferedStream->nullRecords(tdbb);
|
||||
}
|
||||
|
||||
void AggregatedStream::init(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
fb_assert(m_map && m_next);
|
||||
m_impure = CMP_impure(csb, sizeof(Impure));
|
||||
|
||||
// Separate nodes that requires the winPass call.
|
||||
|
||||
NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
|
||||
for (NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
if (aggNode)
|
||||
{
|
||||
aggNode->ordered = m_order != NULL;
|
||||
|
||||
bool wantWinPass = false;
|
||||
aggNode->aggSetup(wantWinPass);
|
||||
|
||||
if (wantWinPass)
|
||||
{
|
||||
m_winPassSources.add(*source);
|
||||
m_winPassTargets.add(*target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the next aggregated record of a value group.
|
||||
bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64 limit) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::evaluateGroup(thread_db* tdbb) const
|
||||
{
|
||||
const NestValueArray* const group = aggType == AGG_TYPE_GROUP ? m_group : m_order;
|
||||
unsigned groupOffset = aggType == AGG_TYPE_GROUP || !m_group ? 0 : m_group->getCount();
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, 0, true);
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
// if we found the last record last time, we're all done
|
||||
if (impure->state == STATE_EOF)
|
||||
@ -344,48 +143,50 @@ bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64
|
||||
|
||||
try
|
||||
{
|
||||
if (aggType == AGG_TYPE_GROUP || group != NULL)
|
||||
aggInit(tdbb, request, aggType);
|
||||
if (m_groupMap)
|
||||
aggInit(tdbb, request, m_groupMap);
|
||||
|
||||
// If there isn't a record pending, open the stream and get one
|
||||
|
||||
if (!getNextRecord(tdbb, request, limit))
|
||||
if (!getNextRecord(tdbb, request))
|
||||
{
|
||||
impure->state = STATE_EOF;
|
||||
|
||||
if (group || m_bufferedStream)
|
||||
if (!m_oneRowWhenEmpty)
|
||||
{
|
||||
finiDistinct(tdbb, request);
|
||||
if (m_groupMap)
|
||||
aggFinish(tdbb, request, m_groupMap);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
cacheValues(tdbb, request, group, groupOffset);
|
||||
cacheValues(tdbb, request, m_group, impure->groupValues, DummyAdjustFunctor());
|
||||
|
||||
// Loop thru records until either a value change or EOF
|
||||
|
||||
while (impure->state == STATE_GROUPING)
|
||||
{
|
||||
if ((aggType == AGG_TYPE_GROUP || group != NULL) && !aggPass(tdbb, request))
|
||||
if (m_groupMap && !aggPass(tdbb, request, m_groupMap->sourceList, m_groupMap->targetList))
|
||||
impure->state = STATE_EOF;
|
||||
else if (getNextRecord(tdbb, request, limit))
|
||||
else if (getNextRecord(tdbb, request))
|
||||
{
|
||||
// In the case of a group by, look for a change in value of any of
|
||||
// the columns; if we find one, stop aggregating and return what we have.
|
||||
|
||||
if (lookForChange(tdbb, request, group, groupOffset))
|
||||
if (lookForChange(tdbb, request, m_group, NULL, impure->groupValues))
|
||||
impure->state = STATE_FETCHED;
|
||||
}
|
||||
else
|
||||
impure->state = STATE_EOF;
|
||||
}
|
||||
|
||||
if (aggType == AGG_TYPE_GROUP || group != NULL)
|
||||
aggExecute(tdbb, request);
|
||||
if (m_groupMap)
|
||||
aggExecute(tdbb, request, m_groupMap->sourceList, m_groupMap->targetList);
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
finiDistinct(tdbb, request);
|
||||
if (m_groupMap)
|
||||
aggFinish(tdbb, request, m_groupMap);
|
||||
throw;
|
||||
}
|
||||
|
||||
@ -393,32 +194,36 @@ bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64
|
||||
}
|
||||
|
||||
// Initialize the aggregate record
|
||||
void AggregatedStream::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::aggInit(thread_db* tdbb, jrd_req* request,
|
||||
const MapNode* map) const
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = map->sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = map->sourceList.begin(),
|
||||
*target = map->targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
const AggNode* aggNode = (*source)->as<AggNode>();
|
||||
|
||||
if (aggNode)
|
||||
aggNode->aggInit(tdbb, request, aggType);
|
||||
aggNode->aggInit(tdbb, request);
|
||||
else if ((*source)->is<LiteralNode>())
|
||||
EXE_assignment(tdbb, *source, *target);
|
||||
}
|
||||
}
|
||||
|
||||
// Go through and compute all the aggregates on this record
|
||||
bool AggregatedStream::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::aggPass(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray& sourceList, const NestValueArray& targetList) const
|
||||
{
|
||||
bool ret = true;
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = sourceList.begin(),
|
||||
*target = targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
@ -440,12 +245,14 @@ bool AggregatedStream::aggPass(thread_db* tdbb, jrd_req* request) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AggregatedStream::aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::aggExecute(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray& sourceList, const NestValueArray& targetList) const
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
|
||||
*target = m_map->targetList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = sourceList.begin(),
|
||||
*target = targetList.begin();
|
||||
source != sourceEnd;
|
||||
++source, ++target)
|
||||
{
|
||||
@ -469,87 +276,14 @@ void AggregatedStream::aggExecute(thread_db* tdbb, jrd_req* request) const
|
||||
}
|
||||
}
|
||||
|
||||
bool AggregatedStream::getNextRecord(thread_db* tdbb, jrd_req* request, FB_UINT64& limit) const
|
||||
{
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
if (limit == 0)
|
||||
return false;
|
||||
else if (impure->state == STATE_FETCHED)
|
||||
{
|
||||
impure->state = STATE_GROUPING;
|
||||
return true;
|
||||
}
|
||||
else if (m_next->getRecord(tdbb))
|
||||
{
|
||||
--limit;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache the values of a group/order in the impure.
|
||||
inline void AggregatedStream::cacheValues(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, unsigned impureOffset) const
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue, ++impureOffset)
|
||||
{
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* target = &impure->impureValues[impureOffset];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
target->vlu_desc.dsc_address = NULL;
|
||||
else
|
||||
EVL_make_value(tdbb, desc, target);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for change in the values of a group/order.
|
||||
inline bool AggregatedStream::lookForChange(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, unsigned impureOffset) const
|
||||
{
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Impure* const impure = request->getImpure<Impure>(m_impure);
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue, ++impureOffset)
|
||||
{
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* vtemp = &impure->impureValues[impureOffset];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
{
|
||||
if (vtemp->vlu_desc.dsc_address)
|
||||
return true;
|
||||
}
|
||||
else if (!vtemp->vlu_desc.dsc_address || MOV_compare(&vtemp->vlu_desc, desc) != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finalize a sort for distinct aggregate
|
||||
void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
|
||||
template <typename ThisType, typename NextType>
|
||||
void BaseAggWinStream<ThisType, NextType>::aggFinish(thread_db* tdbb, jrd_req* request,
|
||||
const MapNode* map) const
|
||||
{
|
||||
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
|
||||
const NestConst<ValueExprNode>* const sourceEnd = map->sourceList.end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin();
|
||||
for (const NestConst<ValueExprNode>* source = map->sourceList.begin();
|
||||
source != sourceEnd;
|
||||
++source)
|
||||
{
|
||||
@ -560,131 +294,108 @@ void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SlidingWindow::SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
|
||||
const NestValueArray* aGroup, jrd_req* aRequest,
|
||||
FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd,
|
||||
FB_UINT64 aOrderStart, FB_UINT64 aOrderEnd)
|
||||
: tdbb(aTdbb), // Note: instanciate the class only as local variable
|
||||
stream(aStream),
|
||||
group(aGroup),
|
||||
request(aRequest),
|
||||
partitionStart(aPartitionStart),
|
||||
partitionEnd(aPartitionEnd),
|
||||
orderStart(aOrderStart),
|
||||
orderEnd(aOrderEnd),
|
||||
moved(false)
|
||||
// Look for change in the values of a group/order.
|
||||
template <typename ThisType, typename NextType>
|
||||
int BaseAggWinStream<ThisType, NextType>::lookForChange(thread_db* tdbb, jrd_req* request,
|
||||
const NestValueArray* group, const SortNode* sort, impure_value* values) const
|
||||
{
|
||||
savedPosition = stream->getPosition(request);
|
||||
}
|
||||
|
||||
SlidingWindow::~SlidingWindow()
|
||||
{
|
||||
if (!moved)
|
||||
return;
|
||||
|
||||
for (impure_value* impure = partitionKeys.begin(); impure != partitionKeys.end(); ++impure)
|
||||
delete impure->vlu_string;
|
||||
|
||||
// Position the stream where we received it.
|
||||
stream->locate(tdbb, savedPosition);
|
||||
}
|
||||
|
||||
// Move in the window without pass partition boundaries.
|
||||
bool SlidingWindow::move(SINT64 delta)
|
||||
{
|
||||
const SINT64 newPosition = SINT64(savedPosition) + delta;
|
||||
|
||||
// If we try to go out of bounds, no need to check the partition.
|
||||
if (newPosition < 0 || newPosition >= (SINT64) stream->getCount(tdbb))
|
||||
return false;
|
||||
|
||||
if (!group)
|
||||
{
|
||||
// No partition, we may go everywhere.
|
||||
|
||||
moved = true;
|
||||
|
||||
stream->locate(tdbb, newPosition);
|
||||
|
||||
if (!stream->getRecord(tdbb))
|
||||
{
|
||||
fb_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!moved)
|
||||
{
|
||||
// This is our first move. We should cache the partition values, so subsequente moves didn't
|
||||
// need to evaluate them again.
|
||||
|
||||
if (!stream->getRecord(tdbb))
|
||||
{
|
||||
fb_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
impure_value* impure = partitionKeys.getBuffer(group->getCount());
|
||||
memset(impure, 0, sizeof(impure_value) * group->getCount());
|
||||
|
||||
const NestConst<ValueExprNode>* const end = group->end();
|
||||
dsc* desc;
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr < end; ++ptr, ++impure)
|
||||
{
|
||||
const ValueExprNode* from = *ptr;
|
||||
desc = EVL_expr(tdbb, request, from);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
impure->vlu_desc.dsc_address = NULL;
|
||||
else
|
||||
EVL_make_value(tdbb, desc, impure);
|
||||
}
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
stream->locate(tdbb, savedPosition); // Reposition for a new try.
|
||||
throw;
|
||||
}
|
||||
|
||||
moved = true;
|
||||
}
|
||||
|
||||
stream->locate(tdbb, newPosition);
|
||||
|
||||
if (!stream->getRecord(tdbb))
|
||||
{
|
||||
fb_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify if we're still inside the same partition.
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
impure_value* impure = partitionKeys.begin();
|
||||
dsc* desc;
|
||||
const NestConst<ValueExprNode>* const end = group->end();
|
||||
|
||||
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr != end; ++ptr, ++impure)
|
||||
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
|
||||
ptrValue != endValue;
|
||||
++ptrValue)
|
||||
{
|
||||
const ValueExprNode* from = *ptr;
|
||||
desc = EVL_expr(tdbb, request, from);
|
||||
int direction = 1;
|
||||
int nullDirection = 1;
|
||||
|
||||
if (sort)
|
||||
{
|
||||
unsigned index = ptrValue - group->begin();
|
||||
|
||||
if (sort->descending[index])
|
||||
direction = -1;
|
||||
|
||||
nullDirection = (sort->getEffectiveNullOrder(index) == rse_nulls_first ? 1 : -1);
|
||||
}
|
||||
|
||||
const ValueExprNode* from = *ptrValue;
|
||||
impure_value* vtemp = &values[ptrValue - group->begin()];
|
||||
|
||||
dsc* desc = EVL_expr(tdbb, request, from);
|
||||
int n;
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
{
|
||||
if (impure->vlu_desc.dsc_address)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!impure->vlu_desc.dsc_address || MOV_compare(&impure->vlu_desc, desc) != 0)
|
||||
return false;
|
||||
if (vtemp->vlu_desc.dsc_address)
|
||||
return -1 * nullDirection;
|
||||
}
|
||||
else if (!vtemp->vlu_desc.dsc_address)
|
||||
return 1 * nullDirection;
|
||||
else if ((n = MOV_compare(desc, &vtemp->vlu_desc)) != 0)
|
||||
return n * direction;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename ThisType, typename NextType>
|
||||
bool BaseAggWinStream<ThisType, NextType>::getNextRecord(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
if (impure->state == STATE_FETCHED)
|
||||
{
|
||||
impure->state = STATE_GROUPING;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return m_next->getRecord(tdbb);
|
||||
}
|
||||
|
||||
// Export the template for WindowedStream::WindowStream.
|
||||
template class Jrd::BaseAggWinStream<WindowedStream::WindowStream, BaseBufferedStream>;
|
||||
|
||||
// ------------------------------
|
||||
|
||||
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next)
|
||||
: BaseAggWinStream(tdbb, csb, stream, group, map, !group, next)
|
||||
{
|
||||
fb_assert(map);
|
||||
}
|
||||
|
||||
void AggregatedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const
|
||||
{
|
||||
if (detailed)
|
||||
plan += printIndent(++level) + "Aggregate";
|
||||
|
||||
m_next->print(tdbb, plan, detailed, level);
|
||||
}
|
||||
|
||||
bool AggregatedStream::getRecord(thread_db* tdbb) const
|
||||
{
|
||||
if (--tdbb->tdbb_quantum < 0)
|
||||
JRD_reschedule(tdbb, 0, true);
|
||||
|
||||
jrd_req* const request = tdbb->getRequest();
|
||||
record_param* const rpb = &request->req_rpb[m_stream];
|
||||
Impure* const impure = getImpure(request);
|
||||
|
||||
if (!(impure->irsb_flags & irsb_open))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!evaluateGroup(tdbb))
|
||||
{
|
||||
rpb->rpb_number.setValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
rpb->rpb_number.setValid(true);
|
||||
return true;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "../jrd/req.h"
|
||||
#include "../jrd/rse.h"
|
||||
#include "../jrd/inf_pub.h"
|
||||
#include "../jrd/evl_proto.h"
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
@ -601,10 +602,9 @@ namespace Jrd
|
||||
class SlidingWindow
|
||||
{
|
||||
public:
|
||||
SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
|
||||
const NestValueArray* aGroup, jrd_req* aRequest,
|
||||
SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, jrd_req* request,
|
||||
FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd,
|
||||
FB_UINT64 aOrderStart, FB_UINT64 aOrderEnd);
|
||||
FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd);
|
||||
~SlidingWindow();
|
||||
|
||||
FB_UINT64 getPartitionStart() const
|
||||
@ -622,39 +622,44 @@ namespace Jrd
|
||||
return partitionEnd - partitionStart + 1;
|
||||
}
|
||||
|
||||
FB_UINT64 getOrderStart() const
|
||||
FB_UINT64 getFrameStart() const
|
||||
{
|
||||
return orderStart;
|
||||
return frameStart;
|
||||
}
|
||||
|
||||
FB_UINT64 getOrderEnd() const
|
||||
FB_UINT64 getFrameEnd() const
|
||||
{
|
||||
return orderEnd;
|
||||
return frameEnd;
|
||||
}
|
||||
|
||||
FB_UINT64 getOrderSize() const
|
||||
FB_UINT64 getFrameSize() const
|
||||
{
|
||||
return orderEnd - orderStart + 1;
|
||||
return frameEnd - frameStart + 1;
|
||||
}
|
||||
|
||||
bool move(SINT64 delta);
|
||||
FB_UINT64 getRecordPosition() const
|
||||
{
|
||||
return savedPosition;
|
||||
}
|
||||
|
||||
bool moveWithinPartition(SINT64 delta);
|
||||
bool moveWithinFrame(SINT64 delta);
|
||||
|
||||
private:
|
||||
thread_db* tdbb;
|
||||
const BaseBufferedStream* const stream;
|
||||
const NestValueArray* group;
|
||||
jrd_req* request;
|
||||
Firebird::Array<impure_value> partitionKeys;
|
||||
FB_UINT64 partitionStart;
|
||||
FB_UINT64 partitionEnd;
|
||||
FB_UINT64 orderStart;
|
||||
FB_UINT64 orderEnd;
|
||||
FB_UINT64 frameStart;
|
||||
FB_UINT64 frameEnd;
|
||||
FB_UINT64 savedPosition;
|
||||
bool moved;
|
||||
};
|
||||
|
||||
class AggregatedStream : public RecordStream
|
||||
template <typename ThisType, typename NextType>
|
||||
class BaseAggWinStream : public RecordStream
|
||||
{
|
||||
protected:
|
||||
enum State
|
||||
{
|
||||
STATE_EOF, // We processed everything now process EOF
|
||||
@ -662,74 +667,196 @@ namespace Jrd
|
||||
STATE_GROUPING // Entering EVL group before fetching the first record
|
||||
};
|
||||
|
||||
struct Block
|
||||
{
|
||||
FB_UINT64 startPosition;
|
||||
FB_UINT64 endPosition;
|
||||
FB_UINT64 pending;
|
||||
};
|
||||
|
||||
struct Impure : public RecordSource::Impure
|
||||
{
|
||||
impure_value* impureValues;
|
||||
Block partitionBlock;
|
||||
Block orderBlock;
|
||||
impure_value* groupValues;
|
||||
State state;
|
||||
bool lastGroup;
|
||||
};
|
||||
|
||||
struct DummyAdjustFunctor
|
||||
{
|
||||
void operator ()(impure_value* target)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, BaseBufferedStream* next,
|
||||
const NestValueArray* order);
|
||||
|
||||
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next);
|
||||
BaseAggWinStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
|
||||
const NestValueArray* group, MapNode* groupMap, bool oneRowWhenEmpty, NextType* next);
|
||||
|
||||
public:
|
||||
void open(thread_db* tdbb) const override;
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
bool getRecord(thread_db* tdbb) const override;
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
bool lockRecord(thread_db* tdbb) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(jrd_req* request) const override;
|
||||
|
||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
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 override;
|
||||
void close(thread_db* tdbb) const override;
|
||||
@ -739,7 +866,7 @@ namespace Jrd
|
||||
bool lockRecord(thread_db* tdbb) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level) const override;
|
||||
bool detailed, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(jrd_req* request) const override;
|
||||
@ -822,7 +949,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
@ -126,9 +126,12 @@ const USHORT RPB_s_sweeper = 0x04; // garbage collector - skip swept pages
|
||||
|
||||
// Runtime flags
|
||||
|
||||
const USHORT RPB_refetch = 0x01; // re-fetch is required
|
||||
const USHORT RPB_undo_data = 0x02; // data got from undo log
|
||||
const USHORT RPB_undo_read = 0x04; // read was performed using the undo log
|
||||
const USHORT RPB_refetch = 0x01; // re-fetch is required
|
||||
const USHORT RPB_undo_data = 0x02; // data got from undo log
|
||||
const USHORT RPB_undo_read = 0x04; // read was performed using the undo log
|
||||
const USHORT RPB_undo_deleted = 0x08; // read was performed using the undo log, primary version is deleted
|
||||
|
||||
const USHORT RPB_UNDO_FLAGS = (RPB_undo_data | RPB_undo_read | RPB_undo_deleted);
|
||||
|
||||
const unsigned int MAX_DIFFERENCES = 1024; // Max length of generated Differences string
|
||||
// between two records
|
||||
|
@ -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
|
||||
|
@ -719,7 +719,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
|
||||
|
||||
// Take care about modifications performed by our own transaction
|
||||
|
||||
rpb->rpb_runtime_flags &= ~(RPB_undo_data | RPB_undo_read);
|
||||
rpb->rpb_runtime_flags &= ~RPB_UNDO_FLAGS;
|
||||
int forceBack = 0;
|
||||
|
||||
if (state == tra_us && !noundo && !(transaction->tra_flags & TRA_system))
|
||||
@ -4624,6 +4624,8 @@ static UndoDataRet get_undo_data(thread_db* tdbb, jrd_tra* transaction,
|
||||
return udNone;
|
||||
|
||||
rpb->rpb_runtime_flags |= RPB_undo_read;
|
||||
if (rpb->rpb_flags & rpb_deleted)
|
||||
rpb->rpb_runtime_flags |= RPB_undo_deleted;
|
||||
|
||||
if (!action->vct_undo || !action->vct_undo->locate(recno))
|
||||
return udForceBack;
|
||||
|
@ -9,7 +9,7 @@ BuildType=T
|
||||
MajorVer=4
|
||||
MinorVer=0
|
||||
RevNo=0
|
||||
BuildNum=354
|
||||
BuildNum=366
|
||||
|
||||
NowAt=`pwd`
|
||||
cd `dirname $0`
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
|
||||
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
|
||||
--
|
||||
('2016-07-28 14:55:12', 'JRD', 0, 796)
|
||||
('2016-09-02 12:40:00', 'JRD', 0, 801)
|
||||
('2015-03-17 18:33:00', 'QLI', 1, 533)
|
||||
('2015-01-07 18:01:51', 'GFIX', 3, 134)
|
||||
('1996-11-07 13:39:40', 'GPRE', 4, 1)
|
||||
|
@ -903,6 +903,11 @@ Data source : @4', NULL, NULL)
|
||||
('crypt_checksum', 'CryptoManager::checkDigitalSignature', 'CryptoManager.cpp', NULL, 0, 793, NULL, 'Invalid or missing checksum of encrypted database', NULL, NULL);
|
||||
('not_dba', 'Service::start', 'svc.cpp', NULL, 0, 794, NULL, 'You must have SYSDBA rights at this server', NULL, NULL);
|
||||
('no_cursor', 'DSQL_open', 'dsql.cpp', NULL, 0, 795, NULL, 'Cannot open cursor for non-SELECT statement', NULL, NULL);
|
||||
('dsql_window_incompat_frames', NULL, 'ExprNodes.cpp', NULL, 0, 796, NULL, 'If <window frame bound 1> specifies @1, then <window frame bound 2> shall not specify @2', NULL, NULL);
|
||||
('dsql_window_range_multi_key', NULL, 'ExprNodes.cpp', NULL, 0, 797, NULL, 'RANGE based window with <expr> {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value', NULL, NULL);
|
||||
('dsql_window_range_inv_key_type', NULL, 'ExprNodes.cpp', NULL, 0, 798, NULL, 'RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types', NULL, NULL);
|
||||
('dsql_window_frame_value_inv_type', NULL, 'ExprNodes.cpp', NULL, 0, 799, NULL, 'Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type', NULL, NULL);
|
||||
('window_frame_value_invalid', NULL, 'ExprNodes.cpp', NULL, 0, 800, NULL, 'Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative', NULL, NULL);
|
||||
-- QLI
|
||||
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
|
||||
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);
|
||||
|
@ -802,6 +802,11 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
|
||||
(-902, 'XX', '000', 0, 793, 'crypt_checksum', NULL, NULL)
|
||||
(-902, '28', '000', 0, 794, 'not_dba', NULL, NULL)
|
||||
(-901, '07', '005', 0, 795, 'no_cursor', NULL, NULL)
|
||||
(-104, '42', '000', 0, 796, 'dsql_window_incompat_frames', NULL, NULL)
|
||||
(-104, '42', '000', 0, 797, 'dsql_window_range_multi_key', NULL, NULL)
|
||||
(-104, '42', '000', 0, 798, 'dsql_window_range_inv_key_type', NULL, NULL)
|
||||
(-104, '42', '000', 0, 799, 'dsql_window_frame_value_inv_type', NULL, NULL)
|
||||
(-833, '42', '000', 0, 800, 'window_frame_value_invalid', NULL, NULL)
|
||||
-- GFIX
|
||||
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
|
||||
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)
|
||||
|
@ -6300,7 +6300,10 @@ static void authReceiveResponse(bool havePacket, ClntAuthBlock& cBlock, rem_port
|
||||
d->cstr_length, n->cstr_length,
|
||||
n->cstr_length, n->cstr_address, n->cstr_address ? n->cstr_address[0] : 0));
|
||||
if (packet->p_acpd.p_acpt_type & pflag_compress)
|
||||
{
|
||||
port->initCompression();
|
||||
port->port_flags |= PORT_compressed;
|
||||
}
|
||||
packet->p_acpd.p_acpt_type &= ptype_MASK;
|
||||
break;
|
||||
|
||||
|
@ -702,7 +702,10 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock,
|
||||
}
|
||||
|
||||
if (compress)
|
||||
{
|
||||
port->initCompression();
|
||||
port->port_flags |= PORT_compressed;
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "firebird/Interface.h"
|
||||
#include "../common/os/mod_loader.h"
|
||||
#include "../jrd/license.h"
|
||||
#include "../common/classes/ImplementHelper.h"
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
Firebird::AtomicCounter rem_port::portCounter;
|
||||
@ -1495,7 +1496,7 @@ bool REMOTE_deflate(XDR* xdrs, ProtoWrite* proto_write, PacketSend* packet_send,
|
||||
{
|
||||
#ifdef WIRE_COMPRESS_SUPPORT
|
||||
rem_port* port = (rem_port*) xdrs->x_public;
|
||||
if (!port->port_compressed)
|
||||
if (!(port->port_compressed && (port->port_flags & PORT_compressed)))
|
||||
return proto_write(xdrs);
|
||||
|
||||
z_stream& strm = port->port_send_stream;
|
||||
|
@ -859,6 +859,7 @@ const USHORT PORT_detached = 0x0100; // op_detach, op_drop_database or op_servi
|
||||
const USHORT PORT_rdb_shutdown = 0x0200; // Database is shut down
|
||||
const USHORT PORT_connecting = 0x0400; // Aux connection waits for a channel to be activated by client
|
||||
const USHORT PORT_z_data = 0x0800; // Zlib incoming buffer has data left after decompression
|
||||
const USHORT PORT_compressed = 0x1000; // Compress outgoing stream (does not affect incoming)
|
||||
|
||||
// Port itself
|
||||
|
||||
|
@ -501,9 +501,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
authPort->send(send);
|
||||
if (send->p_acpt.p_acpt_type & pflag_compress)
|
||||
authPort->initCompression();
|
||||
authPort->send(send);
|
||||
if (send->p_acpt.p_acpt_type & pflag_compress)
|
||||
authPort->port_flags |= PORT_compressed;
|
||||
memset(&send->p_auth_cont, 0, sizeof send->p_auth_cont);
|
||||
return false;
|
||||
|
||||
@ -1909,9 +1911,11 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send)
|
||||
HANDSHAKE_DEBUG(fprintf(stderr, "Srv: accept_connection: accepted ud=%d protocol=%x\n", returnData, port->port_protocol));
|
||||
|
||||
send->p_operation = returnData ? op_accept_data : op_accept;
|
||||
port->send(send);
|
||||
if (send->p_acpt.p_acpt_type & pflag_compress)
|
||||
port->initCompression();
|
||||
port->send(send);
|
||||
if (send->p_acpt.p_acpt_type & pflag_compress)
|
||||
port->port_flags |= PORT_compressed;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1936,9 +1940,11 @@ void ConnectAuth::accept(PACKET* send, Auth::WriterImplementation*)
|
||||
CSTRING* const s = &send->p_acpd.p_acpt_keys;
|
||||
authPort->extractNewKeys(s);
|
||||
send->p_acpd.p_acpt_authenticated = 1;
|
||||
authPort->send(send);
|
||||
if (send->p_acpt.p_acpt_type & pflag_compress)
|
||||
authPort->initCompression();
|
||||
authPort->send(send);
|
||||
if (send->p_acpt.p_acpt_type & pflag_compress)
|
||||
authPort->port_flags |= PORT_compressed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -181,8 +181,10 @@ static const TOK tokens[] =
|
||||
{END, "END", false},
|
||||
{ENGINE, "ENGINE", true},
|
||||
{ENTRY_POINT, "ENTRY_POINT", false},
|
||||
{ERROR_MESSAGE, "ERROR_MESSAGE", true},
|
||||
{ESCAPE, "ESCAPE", false},
|
||||
{EXCEPTION, "EXCEPTION", false},
|
||||
{EXCLUDE, "EXCLUDE", false},
|
||||
{EXECUTE, "EXECUTE", false},
|
||||
{EXISTS, "EXISTS", false},
|
||||
{EXIT, "EXIT", false},
|
||||
@ -198,6 +200,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 +299,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 +321,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 +331,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 +426,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 +440,7 @@ static const TOK tokens[] =
|
||||
{TRUSTED, "TRUSTED", false},
|
||||
{TWO_PHASE, "TWO_PHASE", true},
|
||||
{KW_TYPE, "TYPE", true},
|
||||
{UNBOUNDED, "UNBOUNDED", false},
|
||||
{UNCOMMITTED, "UNCOMMITTED", false},
|
||||
{UNDO, "UNDO", true},
|
||||
{UNION, "UNION", false},
|
||||
|
Loading…
Reference in New Issue
Block a user