8
0
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:
Adriano dos Santos Fernandes 2016-09-10 14:18:27 -03:00
commit 62d2a001f8
58 changed files with 3054 additions and 1692 deletions

View File

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

View File

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

View File

@ -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-&gt;fetchNext(&amp;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(&amp;status, master);</I></FONT></P>
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>) output(&amp;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-&gt;openCursor(&amp;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-&gt;fetchNext(&amp;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(&quot;%4d
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>{</I></FONT></P>
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>printf(&quot;%4d
%31.31s %*.*s\n&quot;, output-&gt;relationId,
output-&gt;relationName.str,</I></FONT></P>
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>output-&gt;descriptionNull
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>output-&gt;descriptionNull
? 0 : output-&gt;description.length,</I></FONT></P>
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>output-&gt;descriptionNull
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>output-&gt;descriptionNull
? 0 : output-&gt;description.length, output-&gt;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(&amp;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(&amp;status,
master);</I></FONT></P>
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>ISC_QUAD* blobPtr =
&amp;message-&gt;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(&amp;segmentData, &amp;segmentLength))</I></FONT></P>
<P STYLE="margin-bottom: 0in"> <FONT SIZE=4><I>blob-&gt;putSegment(&amp;status,
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>blob-&gt;putSegment(&amp;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-&gt;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-&gt;getSegment(&amp;status, sizeof(buffer), buffer,
&amp;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-&gt;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-&gt;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-&gt;queEvents(&amp;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-&gt;getBufferLength(&amp;status), spb2-&gt;getBuffer(&amp;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-&gt;query(&amp;status,
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>svc-&gt;query(&amp;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&lt;PluginModule, CheckStatusWrapper&gt;</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-&gt;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-&gt;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-&gt;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-&gt;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&lt;MyPlugin, CheckStatusWrapper&gt;</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-&gt;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-&gt;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-&gt;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-&gt;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&lt;Factory, CheckStatusWrapper&gt;</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-&gt;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-&gt;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 &quot;C&quot; 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-&gt;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-&gt;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-&gt;registerPluginFactory(IPluginManager::TYPE_DB_CRYPT,
&quot;DbCrypt_example&quot;, &amp;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>

View File

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

View File

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

View File

@ -285,3 +285,27 @@ Firebird 3.0
TAGS *
TRUSTED *
USAGE
Firebird 4.0
------------
Added as reserved words:
UNBOUNDED
Added as non-reserved words:
CUME_DIST *
EXCLUDE
FOLLOWING
NTILE *
OTHERS
PERCENT_RANK *
PRECEDING
PRIVILEGE *
RANGE *
RDB$ROLE_IN_USE *
RDB$SYSTEM_PRIVILEGE *
SYSTEM *
TIES

View File

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

View File

@ -1642,6 +1642,16 @@ C --
PARAMETER (GDS__not_dba = 335545114)
INTEGER*4 GDS__no_cursor
PARAMETER (GDS__no_cursor = 335545115)
INTEGER*4 GDS__dsql_window_incompat_frames
PARAMETER (GDS__dsql_window_incompat_frames = 335545116)
INTEGER*4 GDS__dsql_window_range_multi_key
PARAMETER (GDS__dsql_window_range_multi_key = 335545117)
INTEGER*4 GDS__dsql_window_range_inv_key_type
PARAMETER (GDS__dsql_window_range_inv_key_type = 335545118)
INTEGER*4 GDS__dsql_window_frame_value_inv_type
PARAMETER (GDS__dsql_window_frame_value_inv_type = 335545119)
INTEGER*4 GDS__window_frame_value_invalid
PARAMETER (GDS__window_frame_value_invalid = 335545120)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw

View File

@ -1637,6 +1637,16 @@ const
gds_not_dba = 335545114;
isc_no_cursor = 335545115;
gds_no_cursor = 335545115;
isc_dsql_window_incompat_frames = 335545116;
gds_dsql_window_incompat_frames = 335545116;
isc_dsql_window_range_multi_key = 335545117;
gds_dsql_window_range_multi_key = 335545117;
isc_dsql_window_range_inv_key_type = 335545118;
gds_dsql_window_range_inv_key_type = 335545118;
isc_dsql_window_frame_value_inv_type = 335545119;
gds_dsql_window_frame_value_inv_type = 335545119;
isc_window_frame_value_invalid = 335545120;
gds_window_frame_value_invalid = 335545120;
isc_gfix_db_name = 335740929;
gds_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;

View File

@ -69,7 +69,7 @@ inline bool DTYPE_IS_APPROX(UCHAR d)
inline bool DTYPE_IS_NUMERIC(UCHAR d)
{
return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64;
return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64;
}
// Descriptor format
@ -135,6 +135,11 @@ typedef struct dsc
return dsc_dtype == dtype_int64 || dsc_dtype == dtype_long || dsc_dtype == dtype_short;
}
bool isNumeric() const
{
return (dsc_dtype >= dtype_byte && dsc_dtype <= dtype_d_float) || dsc_dtype == dtype_int64;
}
bool isText() const
{
return dsc_dtype >= dtype_text && dsc_dtype <= dtype_varying;
@ -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;
}

View File

@ -60,8 +60,7 @@ AggNode::AggNode(MemoryPool& pool, const AggInfo& aAggInfo, bool aDistinct, bool
dialect1(aDialect1),
arg(aArg),
asb(NULL),
indexed(false),
ordered(false)
indexed(false)
{
addChildNode(arg, arg);
}
@ -270,7 +269,7 @@ ValueExprNode* AggNode::dsqlFieldRemapper(FieldRemapper& visitor)
if (!visitor.window && visitor.dsqlScratch->scopeLevel == aggFinder.deepestLevel)
{
return PASS1_post_map(visitor.dsqlScratch, this,
visitor.context, visitor.partitionNode, visitor.orderNode);
visitor.context, visitor.partitionNode, visitor.windowNode);
}
}
@ -337,14 +336,10 @@ AggNode* AggNode::pass2(thread_db* tdbb, CompilerScratch* csb)
return this;
}
void AggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (aggType == AGG_TYPE_GROUP)
impure->vlux_count = 0;
impure->aggType = aggType;
impure->vlux_count = 0;
if (distinct)
{
@ -627,12 +622,9 @@ string AvgAggNode::internalPrint(NodePrinter& printer) const
return "AvgAggNode";
}
void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -652,10 +644,6 @@ void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) con
void AvgAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return;
++impure->vlux_count;
if (dialect1)
@ -760,12 +748,9 @@ string ListAggNode::internalPrint(NodePrinter& printer) const
return "ListAggNode";
}
void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
// We don't know here what should be the sub-type and text-type.
// Defer blob creation for when first record is found.
@ -778,9 +763,6 @@ void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return;
if (!impure->vlu_blob)
{
impure->vlu_blob = blb::create(tdbb, request->req_transaction,
@ -918,12 +900,9 @@ string CountAggNode::internalPrint(NodePrinter& printer) const
return "CountAggNode";
}
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0);
@ -933,9 +912,6 @@ void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/)
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return;
if (dialect1)
++impure->vlu_misc.vlu_long;
else
@ -1152,12 +1128,9 @@ string SumAggNode::internalPrint(NodePrinter& printer) const
return "SumAggNode";
}
void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -1174,10 +1147,6 @@ void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) con
void SumAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* desc) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return;
++impure->vlux_count;
if (dialect1)
@ -1252,12 +1221,9 @@ string MaxMinAggNode::internalPrint(NodePrinter& printer) const
return "MaxMinAggNode";
}
void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->vlu_desc.dsc_dtype = 0;
@ -1266,11 +1232,8 @@ void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType)
void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return;
++impure->vlux_count;
if (!impure->vlu_desc.dsc_dtype)
{
EVL_make_value(tdbb, desc, impure);
@ -1366,12 +1329,9 @@ string StdDevAggNode::internalPrint(NodePrinter& printer) const
return "StdDevAggNode";
}
void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_double(0);
@ -1383,10 +1343,6 @@ void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType)
void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return;
++impure->vlux_count;
const double d = MOV_get_double(desc);
@ -1510,12 +1466,9 @@ string CorrAggNode::internalPrint(NodePrinter& printer) const
return "CorrAggNode";
}
void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_double(0);
@ -1528,9 +1481,6 @@ bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return true;
dsc* desc = NULL;
dsc* desc2 = NULL;
@ -1702,12 +1652,9 @@ string RegrAggNode::internalPrint(NodePrinter& printer) const
return "RegrAggNode";
}
void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_double(0);
@ -1720,9 +1667,6 @@ bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return true;
dsc* desc = NULL;
dsc* desc2 = NULL;
@ -1882,12 +1826,9 @@ string RegrCountAggNode::internalPrint(NodePrinter& printer) const
return "RegrCountAggNode";
}
void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0);
@ -1897,9 +1838,6 @@ bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (ordered && impure->aggType != AGG_TYPE_ORDER)
return true;
dsc* desc = EVL_expr(tdbb, request, arg);
if (request->req_flags & req_null)
return false;

View File

@ -37,13 +37,18 @@ public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -62,6 +67,11 @@ public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual unsigned getCapabilities() const
{
return CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual bool setParameterType(DsqlCompilerScratch* dsqlScratch,
@ -69,15 +79,7 @@ public:
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void checkOrderedWindowCapable() const
{
Firebird::status_exception::raise(
Firebird::Arg::Gds(isc_wish_list) <<
Firebird::Arg::Gds(isc_random) <<
"LIST is not supported in ordered windows");
}
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -95,13 +97,18 @@ public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -116,12 +123,17 @@ public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -142,12 +154,17 @@ public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -176,6 +193,11 @@ public:
explicit StdDevAggNode(MemoryPool& pool, StdDevType aType, ValueExprNode* aArg = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
@ -184,7 +206,7 @@ public:
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -216,6 +238,11 @@ public:
explicit CorrAggNode(MemoryPool& pool, CorrType aType,
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
@ -224,7 +251,7 @@ public:
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -263,6 +290,11 @@ public:
explicit RegrAggNode(MemoryPool& pool, RegrType aType,
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
@ -271,7 +303,7 @@ public:
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -293,6 +325,11 @@ public:
explicit RegrCountAggNode(MemoryPool& pool,
ValueExprNode* aArg = NULL, ValueExprNode* aArg2 = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
@ -300,7 +337,7 @@ public:
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;

View File

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

View File

@ -951,7 +951,7 @@ public:
};
class OrderNode : public TypedNode<ValueExprNode, ExprNode::TYPE_ORDER>
class OrderNode : public DsqlNode<OrderNode, ExprNode::TYPE_ORDER>
{
public:
enum NullsPlacement
@ -967,38 +967,6 @@ public:
virtual OrderNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
virtual bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const;
virtual void setParameterName(dsql_par* /*parameter*/) const
{
fb_assert(false);
}
virtual void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
{
fb_assert(false);
}
virtual void make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* /*desc*/)
{
fb_assert(false);
}
virtual void getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* /*desc*/)
{
fb_assert(false);
}
virtual ValueExprNode* copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const
{
fb_assert(false);
return NULL;
}
virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
fb_assert(false);
return NULL;
}
public:
NestConst<ValueExprNode> value;
bool descending;
@ -1006,13 +974,227 @@ public:
};
class WindowClause : public DsqlNode<WindowClause, ExprNode::TYPE_WINDOW_CLAUSE>
{
public:
// ListExprNode has no relation with this but works perfectly here for now.
class Frame : public TypedNode<ListExprNode, ExprNode::TYPE_WINDOW_CLAUSE_FRAME>
{
public:
enum Bound
{
// Warning: used in BLR
BOUND_PRECEDING = 0,
BOUND_FOLLOWING,
BOUND_CURRENT_ROW
};
public:
explicit Frame(MemoryPool& p, Bound aBound, ValueExprNode* aValue = NULL)
: TypedNode(p),
bound(aBound),
value(aValue)
{
addChildNode(value, value);
}
public:
virtual Firebird::string internalPrint(NodePrinter& printer) const
{
NODE_PRINT(printer, bound);
NODE_PRINT(printer, value);
return "WindowClause::Frame";
}
virtual Frame* dsqlPass(DsqlCompilerScratch* dsqlScratch)
{
Frame* node = FB_NEW_POOL(getPool()) Frame(getPool(), bound,
doDsqlPass(dsqlScratch, value));
if (node->value)
{
dsc desc;
desc.makeLong(0);
node->value->setParameterType(dsqlScratch, &desc, false);
}
return node;
}
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
{
if (!ListExprNode::dsqlMatch(other, ignoreMapCast))
return false;
const Frame* o = other->as<Frame>();
fb_assert(o);
return bound == o->bound;
}
virtual Frame* dsqlFieldRemapper(FieldRemapper& visitor)
{
ListExprNode::dsqlFieldRemapper(visitor);
return this;
}
virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const;
virtual Frame* pass1(thread_db* tdbb, CompilerScratch* csb);
virtual Frame* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual Frame* copy(thread_db* tdbb, NodeCopier& copier) const;
public:
Bound bound;
NestConst<ValueExprNode> value;
};
class FrameExtent : public TypedNode<ListExprNode, ExprNode::TYPE_WINDOW_CLAUSE_FRAME_EXTENT>
{
public:
enum Unit
{
// Warning: used in BLR
UNIT_RANGE = 0,
UNIT_ROWS
//// TODO: SQL-2013: GROUPS
};
public:
explicit FrameExtent(MemoryPool& p, Unit aUnit, Frame* aFrame1 = NULL, Frame* aFrame2 = NULL)
: TypedNode(p),
unit(aUnit),
frame1(aFrame1),
frame2(aFrame2)
{
addChildNode(frame1, frame1);
addChildNode(frame2, frame2);
}
static FrameExtent* createDefault(MemoryPool& p)
{
FrameExtent* frameExtent = FB_NEW_POOL(p) WindowClause::FrameExtent(p, UNIT_RANGE);
frameExtent->frame1 = FB_NEW_POOL(p) WindowClause::Frame(p, Frame::BOUND_PRECEDING);
frameExtent->frame2 = FB_NEW_POOL(p) WindowClause::Frame(p, Frame::BOUND_CURRENT_ROW);
return frameExtent;
}
public:
virtual Firebird::string internalPrint(NodePrinter& printer) const
{
NODE_PRINT(printer, unit);
NODE_PRINT(printer, frame1);
NODE_PRINT(printer, frame2);
return "WindowClause::FrameExtent";
}
virtual FrameExtent* dsqlPass(DsqlCompilerScratch* dsqlScratch);
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
{
if (!ListExprNode::dsqlMatch(other, ignoreMapCast))
return false;
const FrameExtent* o = other->as<FrameExtent>();
fb_assert(o);
return unit == o->unit;
}
virtual FrameExtent* dsqlFieldRemapper(FieldRemapper& visitor)
{
ListExprNode::dsqlFieldRemapper(visitor);
return this;
}
virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const;
virtual FrameExtent* pass1(thread_db* tdbb, CompilerScratch* csb);
virtual FrameExtent* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual FrameExtent* copy(thread_db* tdbb, NodeCopier& copier) const;
public:
Unit unit;
NestConst<Frame> frame1;
NestConst<Frame> frame2;
};
enum Exclusion
{
// Warning: used in BLR
EXCLUDE_NO_OTHERS = 0,
EXCLUDE_CURRENT_ROW,
EXCLUDE_GROUP,
EXCLUDE_TIES
};
public:
explicit WindowClause(MemoryPool& p, ValueListNode* aOrder, FrameExtent* aFrameExtent,
Exclusion aExclusion)
: DsqlNode(p),
order(aOrder),
extent(aFrameExtent),
exclusion(aExclusion)
{
addDsqlChildNode(order);
addDsqlChildNode(extent);
}
public:
virtual Firebird::string internalPrint(NodePrinter& printer) const
{
NODE_PRINT(printer, order);
NODE_PRINT(printer, extent);
return "WindowClause";
}
virtual WindowClause* dsqlPass(DsqlCompilerScratch* dsqlScratch);
bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
{
if (!DsqlNode::dsqlMatch(other, ignoreMapCast))
return false;
const WindowClause* o = other->as<WindowClause>();
fb_assert(o);
return exclusion == o->exclusion;
}
virtual WindowClause* dsqlFieldRemapper(FieldRemapper& visitor)
{
DsqlNode::dsqlFieldRemapper(visitor);
return this;
}
virtual WindowClause* pass1(thread_db* tdbb, CompilerScratch* csb)
{
fb_assert(false);
return this;
}
virtual WindowClause* pass2(thread_db* tdbb, CompilerScratch* csb)
{
fb_assert(false);
return this;
}
public:
NestConst<ValueListNode> order;
NestConst<FrameExtent> extent;
Exclusion exclusion;
};
// OVER is used only in DSQL. In the engine, normal aggregate functions are used in partitioned
// maps.
class OverNode : public TypedNode<ValueExprNode, ExprNode::TYPE_OVER>
{
public:
explicit OverNode(MemoryPool& pool, AggNode* aAggExpr = NULL, ValueListNode* aPartition = NULL,
ValueListNode* aOrder = NULL);
WindowClause* aWindow = NULL);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
@ -1034,7 +1216,7 @@ public:
public:
NestConst<ValueExprNode> aggExpr;
NestConst<ValueListNode> partition;
NestConst<ValueListNode> order;
NestConst<WindowClause> window;
};

View File

@ -434,6 +434,9 @@ public:
TYPE_UDF_CALL,
TYPE_VALUE_IF,
TYPE_VARIABLE,
TYPE_WINDOW_CLAUSE,
TYPE_WINDOW_CLAUSE_FRAME,
TYPE_WINDOW_CLAUSE_FRAME_EXTENT,
// Bool types
TYPE_BINARY_BOOL,
@ -820,8 +823,74 @@ public:
dsc nodDesc;
};
template <typename T, typename ValueExprNode::Type typeConst>
class DsqlNode : public TypedNode<ValueExprNode, typeConst>
{
public:
DsqlNode(MemoryPool& pool)
: TypedNode<ValueExprNode, typeConst>(pool)
{
}
public:
virtual void setParameterName(dsql_par* /*parameter*/) const
{
fb_assert(false);
}
virtual void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
{
fb_assert(false);
}
virtual void make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* /*desc*/)
{
fb_assert(false);
}
virtual void getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* /*desc*/)
{
fb_assert(false);
}
virtual DsqlNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
{
fb_assert(false);
return NULL;
}
virtual DsqlNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
{
fb_assert(false);
return NULL;
}
virtual ValueExprNode* copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const
{
fb_assert(false);
return NULL;
}
virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
fb_assert(false);
return NULL;
}
};
class AggNode : public TypedNode<ValueExprNode, ExprNode::TYPE_AGGREGATE>
{
public:
// Capabilities
// works in a window frame
static const unsigned CAP_SUPPORTS_WINDOW_FRAME = 0x01;
// respects window frame boundaries
static const unsigned CAP_RESPECTS_WINDOW_FRAME = 0x02 | CAP_SUPPORTS_WINDOW_FRAME;
// wants aggPass/aggExecute calls in a window
static const unsigned CAP_WANTS_AGG_CALLS = 0x04;
// wants winPass call in a window
static const unsigned CAP_WANTS_WIN_PASS_CALL = 0x08;
protected:
struct AggInfo
{
@ -913,6 +982,7 @@ public:
RegisterNode<T> registerNode1, registerNode2;
};
public:
explicit AggNode(MemoryPool& pool, const AggInfo& aAggInfo, bool aDistinct, bool aDialect1,
ValueExprNode* aArg = NULL);
@ -955,31 +1025,17 @@ public:
return false;
}
virtual void checkOrderedWindowCapable() const
{
if (distinct)
{
Firebird::status_exception::raise(
Firebird::Arg::Gds(isc_wish_list) <<
Firebird::Arg::Gds(isc_random) <<
"DISTINCT is not supported in ordered windows");
}
}
virtual void aggSetup(bool& wantWinPass) const
{
}
virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/) const
{
return NULL;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const = 0; // pure, but defined
virtual void aggInit(thread_db* tdbb, jrd_req* request) const = 0; // pure, but defined
virtual void aggFinish(thread_db* tdbb, jrd_req* request) const;
virtual bool aggPass(thread_db* tdbb, jrd_req* request) const;
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
virtual unsigned getCapabilities() const = 0;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const = 0;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const = 0;
@ -1000,7 +1056,6 @@ public:
NestConst<ValueExprNode> arg;
const AggregateSort* asb;
bool indexed;
bool ordered;
private:
static Factory* factories;
@ -1012,6 +1067,16 @@ class WinFuncNode : public AggNode
{
public:
explicit WinFuncNode(MemoryPool& pool, const AggInfo& aAggInfo, ValueExprNode* aArg = NULL);
public:
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const
{
}
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const
{
return NULL;
}
};

View File

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

View File

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

View File

@ -151,13 +151,13 @@ class FieldRemapper
{
public:
FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
ValueListNode* aPartitionNode = NULL, ValueListNode* aOrderNode = NULL);
ValueListNode* aPartitionNode = NULL, WindowClause* aWindowNode = NULL);
static ExprNode* remap(DsqlCompilerScratch* dsqlScratch, dsql_ctx* context, bool window,
ExprNode* field, ValueListNode* partitionNode = NULL, ValueListNode* orderNode = NULL)
ExprNode* field, ValueListNode* partitionNode = NULL, WindowClause* windowNode = NULL)
{
// The bool value returned by the visitor is completely discarded in this class.
return FieldRemapper(dsqlScratch, context, window, partitionNode, orderNode).visit(field);
return FieldRemapper(dsqlScratch, context, window, partitionNode, windowNode).visit(field);
}
ExprNode* visit(ExprNode* node);
@ -167,7 +167,7 @@ public:
dsql_ctx* const context;
const bool window;
ValueListNode* partitionNode;
ValueListNode* orderNode;
WindowClause* windowNode;
USHORT currentLevel;
};

View File

@ -79,12 +79,9 @@ ValueExprNode* DenseRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c
return FB_NEW_POOL(*tdbb->getDefaultPool()) DenseRankWinNode(*tdbb->getDefaultPool());
}
void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
@ -97,10 +94,7 @@ void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /
dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
++impure->vlu_misc.vlu_int64;
++impure->vlu_misc.vlu_int64;
return &impure->vlu_desc;
}
@ -158,12 +152,9 @@ AggNode* RankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
return this;
}
void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(1, 0);
@ -173,9 +164,7 @@ void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) co
void RankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
++impure->vlux_count;
++impure->vlux_count;
}
dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request) const
@ -206,7 +195,8 @@ AggNode* RankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
static WinFuncNode::RegisterFactory0<PercentRankWinNode> percentRankWinInfo("PERCENT_RANK");
PercentRankWinNode::PercentRankWinNode(MemoryPool& pool)
: WinFuncNode(pool, percentRankWinInfo)
: WinFuncNode(pool, percentRankWinInfo),
tempImpure(0)
{
fb_assert(dsqlChildNodes.getCount() == 1 && jrdChildNodes.getCount() == 1);
dsqlChildNodes.clear();
@ -237,44 +227,51 @@ ValueExprNode* PercentRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/)
AggNode* PercentRankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
AggNode::pass2(tdbb, csb);
tempImpure = CMP_impure(csb, sizeof(impure_value_ex));
return this;
}
void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
impure->make_int64(1, 0);
impure->vlux_count = 0;
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
impureTemp->make_double(0);
impureTemp->vlux_count = 0;
}
void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
++impure->vlux_count;
++impure->vlux_count;
}
dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
impureTemp->vlux_count = impure->vlu_misc.vlu_int64;
impure->vlu_misc.vlu_int64 += impure->vlux_count;
impure->vlux_count = 0;
return NULL;
}
dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure_value_ex* impureTemp = request->getImpure<impure_value_ex>(tempImpure);
double n = impure->vlux_count;
double partitionSize = window->getPartitionSize();
double orderSize = window->getOrderSize();
impure->make_double(n == 1 ? 0 : 1 / (partitionSize - 1) * (n - orderSize));
return &impure->vlu_desc;
impureTemp->vlu_misc.vlu_double = 1 / (partitionSize - 1) * (impureTemp->vlux_count - 1);
return &impureTemp->vlu_desc;
}
AggNode* PercentRankWinNode::dsqlCopy(DsqlCompilerScratch* /*dsqlScratch*/) /*const*/
@ -323,39 +320,22 @@ AggNode* CumeDistWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
return this;
}
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
impure->vlux_count = 0;
}
void CumeDistWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (!ordered || impure->aggType == AGG_TYPE_ORDER)
++impure->vlux_count;
}
dsc* CumeDistWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
return NULL;
impure->make_double(0);
}
dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
double n = impure->vlux_count;
double frameSize = window->getFrameSize();
double partitionSize = window->getPartitionSize();
impure->make_double(n / partitionSize);
impure->vlu_misc.vlu_double = frameSize / partitionSize;
return &impure->vlu_desc;
}
@ -402,30 +382,18 @@ ValueExprNode* RowNumberWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c
return FB_NEW_POOL(*tdbb->getDefaultPool()) RowNumberWinNode(*tdbb->getDefaultPool());
}
void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
}
void RowNumberWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
{
}
dsc* RowNumberWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const
{
return NULL;
}
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* /*window*/) const
dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
++impure->vlu_misc.vlu_int64;
impure->vlu_misc.vlu_int64 = window->getRecordPosition() - window->getPartitionStart() + 1;
return &impure->vlu_desc;
}
@ -474,36 +442,17 @@ ValueExprNode* FirstValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) cons
return node;
}
void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
}
void FirstValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
{
}
dsc* FirstValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
return NULL;
AggNode::aggInit(tdbb, request);
}
dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
SINT64 records = impure->vlu_misc.vlu_int64++;
if (!window->move(-records))
{
window->move(0); // Come back to our row.
if (!window->moveWithinFrame(-(window->getRecordPosition() - window->getFrameStart())))
return NULL;
}
dsc* desc = EVL_expr(tdbb, request, arg);
if (!desc || (request->req_flags & req_null))
@ -557,23 +506,16 @@ ValueExprNode* LastValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
return node;
}
void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
}
void LastValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
{
}
dsc* LastValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
return NULL;
AggNode::aggInit(tdbb, request);
}
dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
{
if (!window->move(0))
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
if (!window->moveWithinFrame(window->getFrameEnd() - window->getRecordPosition()))
return NULL;
dsc* desc = EVL_expr(tdbb, request, arg);
@ -641,32 +583,18 @@ ValueExprNode* NthValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const
return node;
}
void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
impure->make_int64(0, 0);
}
void NthValueWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
{
}
dsc* NthValueWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
return NULL;
}
dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
window->move(0); // Come back to our row because row may reference columns.
dsc* desc = EVL_expr(tdbb, request, row);
if (!desc || (request->req_flags & req_null))
return NULL;
@ -682,20 +610,12 @@ dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow*
const SLONG fromPos = desc ? MOV_get_long(desc, 0) : FROM_FIRST;
if (fromPos == FROM_FIRST)
{
if (records > ++impure->vlu_misc.vlu_int64)
return NULL;
records -= impure->vlu_misc.vlu_int64;
}
records += -(window->getRecordPosition() - window->getFrameStart()) - 1;
else
records = impure->vlu_misc.vlu_int64 - records + 1;
records = window->getFrameEnd() - window->getRecordPosition() - records + 1;
if (!window->move(records))
{
window->move(0); // Come back to our row.
if (!window->moveWithinFrame(records))
return NULL;
}
desc = EVL_expr(tdbb, request, arg);
if (!desc || (request->req_flags & req_null))
@ -757,24 +677,13 @@ void LagLeadWinNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
arg->getDesc(tdbb, csb, desc);
}
void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
}
void LagLeadWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
{
}
dsc* LagLeadWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
return NULL;
AggNode::aggInit(tdbb, request);
}
dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const
{
window->move(0); // Come back to our row because rows may reference columns.
dsc* desc = EVL_expr(tdbb, request, rows);
if (!desc || (request->req_flags & req_null))
return NULL;
@ -786,10 +695,8 @@ dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* w
Arg::Num(2) << Arg::Str(aggInfo.name));
}
if (!window->move(records * direction))
if (!window->moveWithinPartition(records * direction))
{
window->move(0); // Come back to our row because outExpr may reference columns.
desc = EVL_expr(tdbb, request, outExpr);
if (!desc || (request->req_flags & req_null))
return NULL;
@ -924,12 +831,9 @@ AggNode* NTileWinNode::pass2(thread_db* tdbb, CompilerScratch* csb)
return this;
}
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const
{
AggNode::aggInit(tdbb, request, aggType);
if (aggType != AGG_TYPE_GROUP)
return;
AggNode::aggInit(tdbb, request);
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
ThisImpure* thisImpure = request->getImpure<ThisImpure>(thisImpureOffset);
@ -956,15 +860,6 @@ void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) c
}
}
void NTileWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const
{
}
dsc* NTileWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* /*request*/) const
{
return NULL;
}
dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const
{
impure_value_ex* impure = request->getImpure<impure_value_ex>(impureOffset);
@ -986,7 +881,7 @@ dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow*
++n;
impure->make_int64(result);
impure->vlu_misc.vlu_int64 = result;
return &impure->vlu_desc;
}

View File

@ -36,12 +36,17 @@ class DenseRankWinNode : public WinFuncNode
public:
explicit DenseRankWinNode(MemoryPool& pool);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -55,13 +60,18 @@ class RankWinNode : public WinFuncNode
public:
explicit RankWinNode(MemoryPool& pool);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
@ -78,25 +88,28 @@ class PercentRankWinNode : public WinFuncNode
public:
explicit PercentRankWinNode(MemoryPool& pool);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_AGG_CALLS | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
protected:
virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/;
private:
USHORT tempImpure;
};
// CUME_DIST function.
@ -105,20 +118,18 @@ class CumeDistWinNode : public WinFuncNode
public:
explicit CumeDistWinNode(MemoryPool& pool);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
@ -132,19 +143,17 @@ class RowNumberWinNode : public WinFuncNode
public:
explicit RowNumberWinNode(MemoryPool& pool);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
@ -158,19 +167,17 @@ class FirstValueWinNode : public WinFuncNode
public:
explicit FirstValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
@ -186,19 +193,17 @@ class LastValueWinNode : public WinFuncNode
public:
explicit LastValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
@ -222,19 +227,17 @@ public:
explicit NthValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL,
ValueExprNode* aRow = NULL, ValueExprNode* aFrom = NULL);
virtual unsigned getCapabilities() const
{
return CAP_RESPECTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
@ -255,19 +258,16 @@ public:
explicit LagLeadWinNode(MemoryPool& pool, const AggInfo& aAggInfo, int aDirection,
ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, ValueExprNode* aOutExpr = NULL);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const = 0;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;
protected:
@ -323,20 +323,18 @@ class NTileWinNode : public WinFuncNode
public:
explicit NTileWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL);
virtual unsigned getCapabilities() const
{
return CAP_SUPPORTS_WINDOW_FRAME | CAP_WANTS_WIN_PASS_CALL;
}
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual void aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const;
virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const;
virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const;
virtual void aggSetup(bool& wantWinPass) const
{
wantWinPass = true;
}
virtual void aggInit(thread_db* tdbb, jrd_req* request) const;
virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const;

View File

@ -82,6 +82,7 @@ namespace Jrd
class TransactionNode;
class ValueExprNode;
class ValueListNode;
class WindowClause;
class jrd_tra;
class jrd_req;
class blb;
@ -674,10 +675,10 @@ public:
struct PartitionMap
{
PartitionMap(ValueListNode* aPartition, ValueListNode* aOrder)
PartitionMap(ValueListNode* aPartition, WindowClause* aWindow)
: partition(aPartition),
partitionRemapped(NULL),
order(aOrder),
window(aWindow),
map(NULL),
context(0)
{
@ -685,7 +686,7 @@ struct PartitionMap
NestConst<ValueListNode> partition;
NestConst<ValueListNode> partitionRemapped;
NestConst<ValueListNode> order;
NestConst<WindowClause> window;
dsql_map* map;
USHORT context;
};
@ -756,7 +757,7 @@ public:
bool getImplicitJoinField(const Firebird::MetaName& name, NestConst<ValueExprNode>& node);
PartitionMap* getPartitionMap(DsqlCompilerScratch* dsqlScratch,
ValueListNode* partitionNode, ValueListNode* orderNode);
ValueListNode* partitionNode, WindowClause* windowNode);
};
// Flag values for ctx_flags

View File

@ -561,7 +561,7 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse)
}
if (rse->dsqlOrder)
GEN_sort(dsqlScratch, rse->dsqlOrder);
GEN_sort(dsqlScratch, blr_sort, rse->dsqlOrder);
if (rse->dsqlDistinct)
{
@ -587,28 +587,32 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse)
// Generate a sort clause.
void GEN_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* list)
void GEN_sort(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, ValueListNode* list)
{
dsqlScratch->appendUChar(blr_sort);
dsqlScratch->appendUChar(list->items.getCount());
dsqlScratch->appendUChar(blrVerb);
dsqlScratch->appendUChar(list ? list->items.getCount() : 0);
NestConst<ValueExprNode>* ptr = list->items.begin();
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
if (list)
{
OrderNode* orderNode = (*ptr)->as<OrderNode>();
NestConst<ValueExprNode>* ptr = list->items.begin();
switch (orderNode->nullsPlacement)
for (const NestConst<ValueExprNode>* const end = list->items.end(); ptr != end; ++ptr)
{
case OrderNode::NULLS_FIRST:
dsqlScratch->appendUChar(blr_nullsfirst);
break;
case OrderNode::NULLS_LAST:
dsqlScratch->appendUChar(blr_nullslast);
break;
}
OrderNode* orderNode = (*ptr)->as<OrderNode>();
dsqlScratch->appendUChar((orderNode->descending ? blr_descending : blr_ascending));
GEN_expr(dsqlScratch, orderNode->value);
switch (orderNode->nullsPlacement)
{
case OrderNode::NULLS_FIRST:
dsqlScratch->appendUChar(blr_nullsfirst);
break;
case OrderNode::NULLS_LAST:
dsqlScratch->appendUChar(blr_nullslast);
break;
}
dsqlScratch->appendUChar((orderNode->descending ? blr_descending : blr_ascending));
GEN_expr(dsqlScratch, orderNode->value);
}
}
}

View File

@ -37,7 +37,7 @@ void GEN_parameter(Jrd::DsqlCompilerScratch*, const Jrd::dsql_par*);
void GEN_port(Jrd::DsqlCompilerScratch*, Jrd::dsql_msg*);
void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*);
void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*);
void GEN_sort(Jrd::DsqlCompilerScratch*, Jrd::ValueListNode*);
void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*);
void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*);
#endif // DSQL_GEN_PROTO_H

View File

@ -592,12 +592,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
;
%%

View File

@ -300,12 +300,12 @@ bool InvalidReferenceFinder::visit(ExprNode* node)
FieldRemapper::FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
ValueListNode* aPartitionNode, ValueListNode* aOrderNode)
ValueListNode* aPartitionNode, WindowClause* aWindowNode)
: dsqlScratch(aDsqlScratch),
context(aContext),
window(aWindow),
partitionNode(aPartitionNode),
orderNode(aOrderNode),
windowNode(aWindowNode),
currentLevel(dsqlScratch->scopeLevel)
{
DEV_BLKCHK(dsqlScratch, dsql_type_req);
@ -2213,7 +2213,7 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod
partitionMap->partitionRemapped = Node::doDsqlPass(dsqlScratch, partitionMap->partition);
FieldRemapper remapper2(dsqlScratch, parent_context, true, partitionMap->partition,
partitionMap->order);
partitionMap->window);
ExprNode::doDsqlFieldRemapper(remapper2, partitionMap->partitionRemapped);
}
}
@ -2802,7 +2802,7 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in
// Post an item to a map for a context.
DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* node, dsql_ctx* context,
ValueListNode* partitionNode, ValueListNode* orderNode)
ValueListNode* partitionNode, WindowClause* windowNode)
{
DEV_BLKCHK(node, dsql_type_nod);
DEV_BLKCHK(context, dsql_type_ctx);
@ -2814,7 +2814,7 @@ DsqlMapNode* PASS1_post_map(DsqlCompilerScratch* dsqlScratch, ValueExprNode* nod
if (dsqlScratch->processingWindow)
{
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, orderNode);
partitionMap = context->getPartitionMap(dsqlScratch, partitionNode, windowNode);
map = partitionMap->map;
}
else
@ -2931,7 +2931,7 @@ bool dsql_ctx::getImplicitJoinField(const MetaName& name, NestConst<ValueExprNod
// Returns (creating, if necessary) the PartitionMap of a given partition (that may be NULL).
PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueListNode* partitionNode,
ValueListNode* orderNode)
WindowClause* windowNode)
{
thread_db* tdbb = JRD_get_thread_data();
@ -2942,7 +2942,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
++i)
{
if (PASS1_node_match((*i)->partition, partitionNode, false) &&
PASS1_node_match((*i)->order, orderNode, false))
PASS1_node_match((*i)->window, windowNode, false))
{
partitionMap = *i;
}
@ -2950,7 +2950,7 @@ PartitionMap* dsql_ctx::getPartitionMap(DsqlCompilerScratch* dsqlScratch, ValueL
if (!partitionMap)
{
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, orderNode);
partitionMap = FB_NEW_POOL(*tdbb->getDefaultPool()) PartitionMap(partitionNode, windowNode);
ctx_win_maps.add(partitionMap);
partitionMap->context = dsqlScratch->contextNumber++;
}

View File

@ -51,7 +51,7 @@ Jrd::ValueExprNode* PASS1_lookup_alias(Jrd::DsqlCompilerScratch*, const Firebird
Jrd::dsql_ctx* PASS1_make_context(Jrd::DsqlCompilerScratch* statement, Jrd::RecordSourceNode* relationNode);
bool PASS1_node_match(const Jrd::ExprNode*, const Jrd::ExprNode*, bool);
Jrd::DsqlMapNode* PASS1_post_map(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, Jrd::dsql_ctx*,
Jrd::ValueListNode*, Jrd::ValueListNode*);
Jrd::ValueListNode*, Jrd::WindowClause*);
Jrd::RecordSourceNode* PASS1_relation(Jrd::DsqlCompilerScratch*, Jrd::RecordSourceNode*);
Jrd::RseNode* PASS1_rse(Jrd::DsqlCompilerScratch*, Jrd::SelectExprNode*, bool);
bool PASS1_set_parameter_type(Jrd::DsqlCompilerScratch*, Jrd::ValueExprNode*, const dsc*, bool);

View File

@ -817,6 +817,11 @@ static const struct {
{"crypt_checksum", 335545113},
{"not_dba", 335545114},
{"no_cursor", 335545115},
{"dsql_window_incompat_frames", 335545116},
{"dsql_window_range_multi_key", 335545117},
{"dsql_window_range_inv_key_type", 335545118},
{"dsql_window_frame_value_inv_type", 335545119},
{"window_frame_value_invalid", 335545120},
{"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932},

View File

@ -851,6 +851,11 @@ const ISC_STATUS isc_miss_prvlg = 335545112L;
const ISC_STATUS isc_crypt_checksum = 335545113L;
const ISC_STATUS isc_not_dba = 335545114L;
const ISC_STATUS isc_no_cursor = 335545115L;
const ISC_STATUS isc_dsql_window_incompat_frames = 335545116L;
const ISC_STATUS isc_dsql_window_range_multi_key = 335545117L;
const ISC_STATUS isc_dsql_window_range_inv_key_type = 335545118L;
const ISC_STATUS isc_dsql_window_frame_value_inv_type = 335545119L;
const ISC_STATUS isc_window_frame_value_invalid = 335545120L;
const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1322,7 +1327,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1266;
const ISC_STATUS isc_err_max = 1271;
#else /* c definitions */
@ -2143,6 +2148,11 @@ const ISC_STATUS isc_err_max = 1266;
#define isc_crypt_checksum 335545113L
#define isc_not_dba 335545114L
#define isc_no_cursor 335545115L
#define isc_dsql_window_incompat_frames 335545116L
#define isc_dsql_window_range_multi_key 335545117L
#define isc_dsql_window_range_inv_key_type 335545118L
#define isc_dsql_window_frame_value_inv_type 335545119L
#define isc_window_frame_value_invalid 335545120L
#define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L
@ -2614,7 +2624,7 @@ const ISC_STATUS isc_err_max = 1266;
#define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1266
#define isc_err_max 1271
#endif

View File

@ -820,6 +820,11 @@ Data source : @4"}, /* eds_statement */
{335545113, "Invalid or missing checksum of encrypted database"}, /* crypt_checksum */
{335545114, "You must have SYSDBA rights at this server"}, /* not_dba */
{335545115, "Cannot open cursor for non-SELECT statement"}, /* no_cursor */
{335545116, "If <window frame bound 1> specifies @1, then <window frame bound 2> shall not specify @2"}, /* dsql_window_incompat_frames */
{335545117, "RANGE based window with <expr> {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value"}, /* dsql_window_range_multi_key */
{335545118, "RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types"}, /* dsql_window_range_inv_key_type */
{335545119, "Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type"}, /* dsql_window_frame_value_inv_type */
{335545120, "Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative"}, /* window_frame_value_invalid */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -816,6 +816,11 @@ static const struct {
{335545113, -902}, /* 793 crypt_checksum */
{335545114, -902}, /* 794 not_dba */
{335545115, -901}, /* 795 no_cursor */
{335545116, -104}, /* 796 dsql_window_incompat_frames */
{335545117, -104}, /* 797 dsql_window_range_multi_key */
{335545118, -104}, /* 798 dsql_window_range_inv_key_type */
{335545119, -104}, /* 799 dsql_window_frame_value_inv_type */
{335545120, -833}, /* 800 window_frame_value_invalid */
{335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -816,6 +816,11 @@ static const struct {
{335545113, "XX000"}, // 793 crypt_checksum
{335545114, "28000"}, // 794 not_dba
{335545115, "07005"}, // 795 no_cursor
{335545116, "42000"}, // 796 dsql_window_incompat_frames
{335545117, "42000"}, // 797 dsql_window_range_multi_key
{335545118, "42000"}, // 798 dsql_window_range_inv_key_type
{335545119, "42000"}, // 799 dsql_window_frame_value_inv_type
{335545120, "42000"}, // 800 window_frame_value_invalid
{335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw

View File

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

View File

@ -26,6 +26,7 @@
#include "../common/classes/objects_array.h"
#include "../common/classes/NestConst.h"
#include "../common/classes/QualifiedName.h"
#include "../dsql/ExprNodes.h"
#include "../jrd/jrd.h"
#include "../jrd/exe.h"
#include "../dsql/Visitors.h"
@ -70,6 +71,14 @@ public:
bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream);
void findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList);
int getEffectiveNullOrder(unsigned index) const
{
if (descending[index])
return nullOrder[index] == rse_nulls_default ? rse_nulls_last : nullOrder[index];
else
return nullOrder[index] == rse_nulls_default ? rse_nulls_first : nullOrder[index];
}
public:
bool unique; // sorts using unique key - for distinct and group by
NestValueArray expressions; // sort expressions
@ -460,7 +469,7 @@ public:
virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream);
private:
void genMap(DsqlCompilerScratch* dsqlScratch, dsql_map* map);
void genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map);
RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, BoolExprNodeStack* parentStack,
StreamType shellStream);
@ -543,10 +552,11 @@ private:
class WindowSourceNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_WINDOW>
{
public:
struct Partition
struct Window
{
explicit Partition(MemoryPool&)
: stream(INVALID_STREAM)
explicit Window(MemoryPool&)
: stream(INVALID_STREAM),
exclusion(WindowClause::EXCLUDE_NO_OTHERS)
{
}
@ -555,19 +565,22 @@ public:
NestConst<SortNode> regroup;
NestConst<SortNode> order;
NestConst<MapNode> map;
NestConst<WindowClause::FrameExtent> frameExtent;
WindowClause::Exclusion exclusion;
};
explicit WindowSourceNode(MemoryPool& pool)
: TypedNode<RecordSourceNode, RecordSourceNode::TYPE_WINDOW>(pool),
rse(NULL),
partitions(pool)
windows(pool)
{
}
static WindowSourceNode* parse(thread_db* tdbb, CompilerScratch* csb);
private:
void parsePartitionBy(thread_db* tdbb, CompilerScratch* csb);
void parseLegacyPartitionBy(thread_db* tdbb, CompilerScratch* csb);
void parseWindow(thread_db* tdbb, CompilerScratch* csb);
public:
virtual StreamType getStream() const
@ -602,7 +615,7 @@ public:
private:
NestConst<RseNode> rse;
Firebird::ObjectsArray<Partition> partitions;
Firebird::ObjectsArray<Window> windows;
};
class RseNode : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_RSE>

View File

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

View File

@ -407,4 +407,17 @@
#define blr_record_version2 (unsigned char) 209
#define blr_gen_id2 (unsigned char) 210 // NEXT VALUE FOR generator
// FB 4.0 specific BLR
#define blr_window_win (unsigned char) 211
// subcodes of blr_window_win
#define blr_window_win_partition (unsigned char) 1
#define blr_window_win_order (unsigned char) 2
#define blr_window_win_map (unsigned char) 3
#define blr_window_win_extent_unit (unsigned char) 4
#define blr_window_win_extent_frame_bound (unsigned char) 5
#define blr_window_win_extent_frame_value (unsigned char) 6
#define blr_window_win_exclusion (unsigned char) 7
#endif // JRD_BLR_H

View File

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

View File

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

View File

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

View File

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

View File

@ -1402,7 +1402,7 @@ SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
if (count == 0 && nullForEmpty)
return NULL;
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp, count);
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp == blr_sort, count);
if (blrOp != blr_sort)
sort->unique = true;
@ -1413,8 +1413,7 @@ SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
// Parse the internals of a sort clause. This is used for blr_sort, blr_project, blr_group_by
// and blr_partition_by.
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
USHORT count)
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, bool allClauses, USHORT count)
{
SET_TDBB(tdbb);
@ -1427,7 +1426,7 @@ SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
while (count-- > 0)
{
if (blrOp == blr_sort)
if (allClauses)
{
UCHAR code = csb->csb_blr_reader.getByte();

View File

@ -70,8 +70,7 @@ void PAR_procedure_parms(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*, SSHORT);
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*);
Jrd::SortNode* PAR_sort(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR, bool);
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR blrOp,
USHORT);
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, bool, USHORT);
SLONG PAR_symbol_to_gdscode(const Firebird::string&);
typedef Jrd::DmlNode* (*NodeParseFunc)(Jrd::thread_db* tdbb, MemoryPool& pool,

View File

@ -37,71 +37,51 @@ using namespace Jrd;
// Data access: aggregation
// ------------------------
// Note that we can have NULL order here, in case of window function with shouldCallWinPass
// returning true, with partition, and without order. Example: ROW_NUMBER() OVER (PARTITION BY N).
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
const NestValueArray* group, MapNode* map, BaseBufferedStream* next,
const NestValueArray* order)
template <typename ThisType, typename NextType>
BaseAggWinStream<ThisType, NextType>::BaseAggWinStream(thread_db* tdbb, CompilerScratch* csb,
StreamType stream, const NestValueArray* group, MapNode* groupMap,
bool oneRowWhenEmpty, NextType* next)
: RecordStream(csb, stream),
m_bufferedStream(next),
m_next(m_bufferedStream),
m_group(group),
m_map(map),
m_order(order),
m_winPassSources(csb->csb_pool),
m_winPassTargets(csb->csb_pool)
{
init(tdbb, csb);
}
AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
const NestValueArray* group, MapNode* map, RecordSource* next)
: RecordStream(csb, stream),
m_bufferedStream(NULL),
m_next(next),
m_group(group),
m_map(map),
m_order(NULL),
m_winPassSources(csb->csb_pool),
m_winPassTargets(csb->csb_pool)
m_groupMap(groupMap),
m_oneRowWhenEmpty(oneRowWhenEmpty)
{
init(tdbb, csb);
fb_assert(m_next);
m_impure = CMP_impure(csb, sizeof(typename ThisType::Impure));
}
void AggregatedStream::open(thread_db* tdbb) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::open(thread_db* tdbb) const
{
jrd_req* const request = tdbb->getRequest();
Impure* const impure = request->getImpure<Impure>(m_impure);
Impure* const impure = getImpure(request);
impure->irsb_flags = irsb_open;
impure->state = STATE_GROUPING;
impure->lastGroup = false;
impure->partitionBlock.startPosition = impure->partitionBlock.endPosition =
impure->partitionBlock.pending = 0;
impure->orderBlock = impure->partitionBlock;
VIO_record(tdbb, &request->req_rpb[m_stream], m_format, tdbb->getDefaultPool());
unsigned impureCount = m_group ? m_group->getCount() : 0;
impureCount += m_order ? m_order->getCount() : 0;
if (!impure->impureValues && impureCount > 0)
if (!impure->groupValues && impureCount > 0)
{
impure->impureValues = FB_NEW_POOL(*tdbb->getDefaultPool()) impure_value[impureCount];
memset(impure->impureValues, 0, sizeof(impure_value) * impureCount);
impure->groupValues = FB_NEW_POOL(*tdbb->getDefaultPool()) impure_value[impureCount];
memset(impure->groupValues, 0, sizeof(impure_value) * impureCount);
}
m_next->open(tdbb);
}
void AggregatedStream::close(thread_db* tdbb) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::close(thread_db* tdbb) const
{
jrd_req* const request = tdbb->getRequest();
invalidateRecords(request);
Impure* const impure = request->getImpure<Impure>(m_impure);
Impure* const impure = getImpure(request);
if (impure->irsb_flags & irsb_open)
{
@ -111,232 +91,51 @@ void AggregatedStream::close(thread_db* tdbb) const
}
}
bool AggregatedStream::getRecord(thread_db* tdbb) const
{
if (--tdbb->tdbb_quantum < 0)
JRD_reschedule(tdbb, 0, true);
jrd_req* const request = tdbb->getRequest();
record_param* const rpb = &request->req_rpb[m_stream];
Impure* const impure = request->getImpure<Impure>(m_impure);
if (!(impure->irsb_flags & irsb_open))
{
rpb->rpb_number.setValid(false);
return false;
}
if (m_bufferedStream) // Is that a window stream?
{
const FB_UINT64 position = m_bufferedStream->getPosition(request);
if (impure->orderBlock.pending == 0)
{
if (impure->partitionBlock.pending == 0)
{
if (impure->lastGroup || !evaluateGroup(tdbb, AGG_TYPE_GROUP, MAX_UINT64))
{
rpb->rpb_number.setValid(false);
return false;
}
impure->partitionBlock.startPosition = position;
impure->partitionBlock.endPosition = m_bufferedStream->getPosition(request) - 1 -
(impure->state == STATE_FETCHED ? 1 : 0);
impure->partitionBlock.pending =
impure->partitionBlock.endPosition - impure->partitionBlock.startPosition + 1;
fb_assert(impure->partitionBlock.pending > 0);
m_bufferedStream->locate(tdbb, position);
impure->state = STATE_GROUPING;
impure->lastGroup = impure->state == STATE_EOF;
}
// Check if we need to re-aggregate by the ORDER BY clause.
if (!m_order && m_winPassSources.isEmpty())
impure->orderBlock = impure->partitionBlock;
else
{
if (!evaluateGroup(tdbb, AGG_TYPE_ORDER, impure->partitionBlock.pending))
fb_assert(false);
impure->orderBlock.startPosition = position;
impure->orderBlock.endPosition = m_bufferedStream->getPosition(request) - 1 -
(impure->state == STATE_FETCHED ? 1 : 0);
impure->orderBlock.pending =
impure->orderBlock.endPosition - impure->orderBlock.startPosition + 1;
fb_assert(impure->orderBlock.pending > 0);
}
m_bufferedStream->locate(tdbb, position);
impure->state = STATE_GROUPING;
}
fb_assert(impure->orderBlock.pending > 0 && impure->partitionBlock.pending > 0);
--impure->orderBlock.pending;
--impure->partitionBlock.pending;
if (m_winPassSources.hasData())
{
SlidingWindow window(tdbb, m_bufferedStream, m_group, request,
impure->partitionBlock.startPosition, impure->partitionBlock.endPosition,
impure->orderBlock.startPosition, impure->orderBlock.endPosition);
dsc* desc;
const NestConst<ValueExprNode>* const sourceEnd = m_winPassSources.end();
for (const NestConst<ValueExprNode>* source = m_winPassSources.begin(),
*target = m_winPassTargets.begin();
source != sourceEnd;
++source, ++target)
{
const AggNode* aggNode = (*source)->as<AggNode>();
const FieldNode* field = (*target)->as<FieldNode>();
const USHORT id = field->fieldId;
Record* record = request->req_rpb[field->fieldStream].rpb_record;
desc = aggNode->winPass(tdbb, request, &window);
if (!desc)
record->setNull(id);
else
{
MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
record->clearNull(id);
}
}
}
if (!m_bufferedStream->getRecord(tdbb))
fb_assert(false);
// If there is no group, we should reassign the map items.
if (!m_group)
{
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
*target = m_map->targetList.begin();
source != sourceEnd;
++source, ++target)
{
const AggNode* aggNode = (*source)->as<AggNode>();
if (!aggNode)
EXE_assignment(tdbb, *source, *target);
}
}
}
else
{
if (!evaluateGroup(tdbb, AGG_TYPE_GROUP, MAX_UINT64))
{
rpb->rpb_number.setValid(false);
return false;
}
}
rpb->rpb_number.setValid(true);
return true;
}
bool AggregatedStream::refetchRecord(thread_db* tdbb) const
template <typename ThisType, typename NextType>
bool BaseAggWinStream<ThisType, NextType>::refetchRecord(thread_db* tdbb) const
{
return m_next->refetchRecord(tdbb);
}
bool AggregatedStream::lockRecord(thread_db* /*tdbb*/) const
template <typename ThisType, typename NextType>
bool BaseAggWinStream<ThisType, NextType>::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
return false; // compiler silencer
}
void AggregatedStream::print(thread_db* tdbb, string& plan,
bool detailed, unsigned level) const
{
if (detailed)
plan += printIndent(++level) + (m_bufferedStream ? "Window" : "Aggregate");
m_next->print(tdbb, plan, detailed, level);
}
void AggregatedStream::markRecursive()
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::markRecursive()
{
m_next->markRecursive();
}
void AggregatedStream::invalidateRecords(jrd_req* request) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::invalidateRecords(jrd_req* request) const
{
m_next->invalidateRecords(request);
}
void AggregatedStream::findUsedStreams(StreamList& streams, bool expandAll) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::findUsedStreams(StreamList& streams,
bool expandAll) const
{
RecordStream::findUsedStreams(streams);
if (expandAll)
m_next->findUsedStreams(streams, true);
if (m_bufferedStream)
m_bufferedStream->findUsedStreams(streams, expandAll);
}
void AggregatedStream::nullRecords(thread_db* tdbb) const
{
RecordStream::nullRecords(tdbb);
if (m_bufferedStream)
m_bufferedStream->nullRecords(tdbb);
}
void AggregatedStream::init(thread_db* tdbb, CompilerScratch* csb)
{
fb_assert(m_map && m_next);
m_impure = CMP_impure(csb, sizeof(Impure));
// Separate nodes that requires the winPass call.
NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
for (NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
*target = m_map->targetList.begin();
source != sourceEnd;
++source, ++target)
{
AggNode* aggNode = (*source)->as<AggNode>();
if (aggNode)
{
aggNode->ordered = m_order != NULL;
bool wantWinPass = false;
aggNode->aggSetup(wantWinPass);
if (wantWinPass)
{
m_winPassSources.add(*source);
m_winPassTargets.add(*target);
}
}
}
}
// Compute the next aggregated record of a value group.
bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64 limit) const
template <typename ThisType, typename NextType>
bool BaseAggWinStream<ThisType, NextType>::evaluateGroup(thread_db* tdbb) const
{
const NestValueArray* const group = aggType == AGG_TYPE_GROUP ? m_group : m_order;
unsigned groupOffset = aggType == AGG_TYPE_GROUP || !m_group ? 0 : m_group->getCount();
jrd_req* const request = tdbb->getRequest();
if (--tdbb->tdbb_quantum < 0)
JRD_reschedule(tdbb, 0, true);
Impure* const impure = request->getImpure<Impure>(m_impure);
Impure* const impure = getImpure(request);
// if we found the last record last time, we're all done
if (impure->state == STATE_EOF)
@ -344,48 +143,50 @@ bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64
try
{
if (aggType == AGG_TYPE_GROUP || group != NULL)
aggInit(tdbb, request, aggType);
if (m_groupMap)
aggInit(tdbb, request, m_groupMap);
// If there isn't a record pending, open the stream and get one
if (!getNextRecord(tdbb, request, limit))
if (!getNextRecord(tdbb, request))
{
impure->state = STATE_EOF;
if (group || m_bufferedStream)
if (!m_oneRowWhenEmpty)
{
finiDistinct(tdbb, request);
if (m_groupMap)
aggFinish(tdbb, request, m_groupMap);
return false;
}
}
else
cacheValues(tdbb, request, group, groupOffset);
cacheValues(tdbb, request, m_group, impure->groupValues, DummyAdjustFunctor());
// Loop thru records until either a value change or EOF
while (impure->state == STATE_GROUPING)
{
if ((aggType == AGG_TYPE_GROUP || group != NULL) && !aggPass(tdbb, request))
if (m_groupMap && !aggPass(tdbb, request, m_groupMap->sourceList, m_groupMap->targetList))
impure->state = STATE_EOF;
else if (getNextRecord(tdbb, request, limit))
else if (getNextRecord(tdbb, request))
{
// In the case of a group by, look for a change in value of any of
// the columns; if we find one, stop aggregating and return what we have.
if (lookForChange(tdbb, request, group, groupOffset))
if (lookForChange(tdbb, request, m_group, NULL, impure->groupValues))
impure->state = STATE_FETCHED;
}
else
impure->state = STATE_EOF;
}
if (aggType == AGG_TYPE_GROUP || group != NULL)
aggExecute(tdbb, request);
if (m_groupMap)
aggExecute(tdbb, request, m_groupMap->sourceList, m_groupMap->targetList);
}
catch (const Exception&)
{
finiDistinct(tdbb, request);
if (m_groupMap)
aggFinish(tdbb, request, m_groupMap);
throw;
}
@ -393,32 +194,36 @@ bool AggregatedStream::evaluateGroup(thread_db* tdbb, AggType aggType, FB_UINT64
}
// Initialize the aggregate record
void AggregatedStream::aggInit(thread_db* tdbb, jrd_req* request, AggType aggType) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::aggInit(thread_db* tdbb, jrd_req* request,
const MapNode* map) const
{
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
const NestConst<ValueExprNode>* const sourceEnd = map->sourceList.end();
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
*target = m_map->targetList.begin();
for (const NestConst<ValueExprNode>* source = map->sourceList.begin(),
*target = map->targetList.begin();
source != sourceEnd;
++source, ++target)
{
const AggNode* aggNode = (*source)->as<AggNode>();
if (aggNode)
aggNode->aggInit(tdbb, request, aggType);
aggNode->aggInit(tdbb, request);
else if ((*source)->is<LiteralNode>())
EXE_assignment(tdbb, *source, *target);
}
}
// Go through and compute all the aggregates on this record
bool AggregatedStream::aggPass(thread_db* tdbb, jrd_req* request) const
template <typename ThisType, typename NextType>
bool BaseAggWinStream<ThisType, NextType>::aggPass(thread_db* tdbb, jrd_req* request,
const NestValueArray& sourceList, const NestValueArray& targetList) const
{
bool ret = true;
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
const NestConst<ValueExprNode>* const sourceEnd = sourceList.end();
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
*target = m_map->targetList.begin();
for (const NestConst<ValueExprNode>* source = sourceList.begin(),
*target = targetList.begin();
source != sourceEnd;
++source, ++target)
{
@ -440,12 +245,14 @@ bool AggregatedStream::aggPass(thread_db* tdbb, jrd_req* request) const
return ret;
}
void AggregatedStream::aggExecute(thread_db* tdbb, jrd_req* request) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::aggExecute(thread_db* tdbb, jrd_req* request,
const NestValueArray& sourceList, const NestValueArray& targetList) const
{
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
const NestConst<ValueExprNode>* const sourceEnd = sourceList.end();
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
*target = m_map->targetList.begin();
for (const NestConst<ValueExprNode>* source = sourceList.begin(),
*target = targetList.begin();
source != sourceEnd;
++source, ++target)
{
@ -469,87 +276,14 @@ void AggregatedStream::aggExecute(thread_db* tdbb, jrd_req* request) const
}
}
bool AggregatedStream::getNextRecord(thread_db* tdbb, jrd_req* request, FB_UINT64& limit) const
{
Impure* const impure = request->getImpure<Impure>(m_impure);
if (limit == 0)
return false;
else if (impure->state == STATE_FETCHED)
{
impure->state = STATE_GROUPING;
return true;
}
else if (m_next->getRecord(tdbb))
{
--limit;
return true;
}
else
return false;
}
// Cache the values of a group/order in the impure.
inline void AggregatedStream::cacheValues(thread_db* tdbb, jrd_req* request,
const NestValueArray* group, unsigned impureOffset) const
{
if (!group)
return;
Impure* const impure = request->getImpure<Impure>(m_impure);
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
ptrValue != endValue;
++ptrValue, ++impureOffset)
{
const ValueExprNode* from = *ptrValue;
impure_value* target = &impure->impureValues[impureOffset];
dsc* desc = EVL_expr(tdbb, request, from);
if (request->req_flags & req_null)
target->vlu_desc.dsc_address = NULL;
else
EVL_make_value(tdbb, desc, target);
}
}
// Look for change in the values of a group/order.
inline bool AggregatedStream::lookForChange(thread_db* tdbb, jrd_req* request,
const NestValueArray* group, unsigned impureOffset) const
{
if (!group)
return false;
Impure* const impure = request->getImpure<Impure>(m_impure);
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
ptrValue != endValue;
++ptrValue, ++impureOffset)
{
const ValueExprNode* from = *ptrValue;
impure_value* vtemp = &impure->impureValues[impureOffset];
dsc* desc = EVL_expr(tdbb, request, from);
if (request->req_flags & req_null)
{
if (vtemp->vlu_desc.dsc_address)
return true;
}
else if (!vtemp->vlu_desc.dsc_address || MOV_compare(&vtemp->vlu_desc, desc) != 0)
return true;
}
return false;
}
// Finalize a sort for distinct aggregate
void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
template <typename ThisType, typename NextType>
void BaseAggWinStream<ThisType, NextType>::aggFinish(thread_db* tdbb, jrd_req* request,
const MapNode* map) const
{
const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();
const NestConst<ValueExprNode>* const sourceEnd = map->sourceList.end();
for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin();
for (const NestConst<ValueExprNode>* source = map->sourceList.begin();
source != sourceEnd;
++source)
{
@ -560,131 +294,108 @@ void AggregatedStream::finiDistinct(thread_db* tdbb, jrd_req* request) const
}
}
SlidingWindow::SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
const NestValueArray* aGroup, jrd_req* aRequest,
FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd,
FB_UINT64 aOrderStart, FB_UINT64 aOrderEnd)
: tdbb(aTdbb), // Note: instanciate the class only as local variable
stream(aStream),
group(aGroup),
request(aRequest),
partitionStart(aPartitionStart),
partitionEnd(aPartitionEnd),
orderStart(aOrderStart),
orderEnd(aOrderEnd),
moved(false)
// Look for change in the values of a group/order.
template <typename ThisType, typename NextType>
int BaseAggWinStream<ThisType, NextType>::lookForChange(thread_db* tdbb, jrd_req* request,
const NestValueArray* group, const SortNode* sort, impure_value* values) const
{
savedPosition = stream->getPosition(request);
}
SlidingWindow::~SlidingWindow()
{
if (!moved)
return;
for (impure_value* impure = partitionKeys.begin(); impure != partitionKeys.end(); ++impure)
delete impure->vlu_string;
// Position the stream where we received it.
stream->locate(tdbb, savedPosition);
}
// Move in the window without pass partition boundaries.
bool SlidingWindow::move(SINT64 delta)
{
const SINT64 newPosition = SINT64(savedPosition) + delta;
// If we try to go out of bounds, no need to check the partition.
if (newPosition < 0 || newPosition >= (SINT64) stream->getCount(tdbb))
return false;
if (!group)
{
// No partition, we may go everywhere.
moved = true;
stream->locate(tdbb, newPosition);
if (!stream->getRecord(tdbb))
{
fb_assert(false);
return false;
}
return true;
}
if (!moved)
{
// This is our first move. We should cache the partition values, so subsequente moves didn't
// need to evaluate them again.
if (!stream->getRecord(tdbb))
{
fb_assert(false);
return false;
}
try
{
impure_value* impure = partitionKeys.getBuffer(group->getCount());
memset(impure, 0, sizeof(impure_value) * group->getCount());
const NestConst<ValueExprNode>* const end = group->end();
dsc* desc;
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr < end; ++ptr, ++impure)
{
const ValueExprNode* from = *ptr;
desc = EVL_expr(tdbb, request, from);
if (request->req_flags & req_null)
impure->vlu_desc.dsc_address = NULL;
else
EVL_make_value(tdbb, desc, impure);
}
}
catch (const Exception&)
{
stream->locate(tdbb, savedPosition); // Reposition for a new try.
throw;
}
moved = true;
}
stream->locate(tdbb, newPosition);
if (!stream->getRecord(tdbb))
{
fb_assert(false);
return false;
}
// Verify if we're still inside the same partition.
Impure* const impure = getImpure(request);
impure_value* impure = partitionKeys.begin();
dsc* desc;
const NestConst<ValueExprNode>* const end = group->end();
for (const NestConst<ValueExprNode>* ptr = group->begin(); ptr != end; ++ptr, ++impure)
for (const NestConst<ValueExprNode>* ptrValue = group->begin(), *endValue = group->end();
ptrValue != endValue;
++ptrValue)
{
const ValueExprNode* from = *ptr;
desc = EVL_expr(tdbb, request, from);
int direction = 1;
int nullDirection = 1;
if (sort)
{
unsigned index = ptrValue - group->begin();
if (sort->descending[index])
direction = -1;
nullDirection = (sort->getEffectiveNullOrder(index) == rse_nulls_first ? 1 : -1);
}
const ValueExprNode* from = *ptrValue;
impure_value* vtemp = &values[ptrValue - group->begin()];
dsc* desc = EVL_expr(tdbb, request, from);
int n;
if (request->req_flags & req_null)
{
if (impure->vlu_desc.dsc_address)
return false;
}
else
{
if (!impure->vlu_desc.dsc_address || MOV_compare(&impure->vlu_desc, desc) != 0)
return false;
if (vtemp->vlu_desc.dsc_address)
return -1 * nullDirection;
}
else if (!vtemp->vlu_desc.dsc_address)
return 1 * nullDirection;
else if ((n = MOV_compare(desc, &vtemp->vlu_desc)) != 0)
return n * direction;
}
return 0;
}
template <typename ThisType, typename NextType>
bool BaseAggWinStream<ThisType, NextType>::getNextRecord(thread_db* tdbb, jrd_req* request) const
{
Impure* const impure = getImpure(request);
if (impure->state == STATE_FETCHED)
{
impure->state = STATE_GROUPING;
return true;
}
else
return m_next->getRecord(tdbb);
}
// Export the template for WindowedStream::WindowStream.
template class 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;
}

View File

@ -30,6 +30,7 @@
#include "../jrd/req.h"
#include "../jrd/rse.h"
#include "../jrd/inf_pub.h"
#include "../jrd/evl_proto.h"
namespace Jrd
{
@ -601,10 +602,9 @@ namespace Jrd
class SlidingWindow
{
public:
SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream,
const NestValueArray* aGroup, jrd_req* aRequest,
SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, jrd_req* request,
FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd,
FB_UINT64 aOrderStart, FB_UINT64 aOrderEnd);
FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd);
~SlidingWindow();
FB_UINT64 getPartitionStart() const
@ -622,39 +622,44 @@ namespace Jrd
return partitionEnd - partitionStart + 1;
}
FB_UINT64 getOrderStart() const
FB_UINT64 getFrameStart() const
{
return orderStart;
return frameStart;
}
FB_UINT64 getOrderEnd() const
FB_UINT64 getFrameEnd() const
{
return orderEnd;
return frameEnd;
}
FB_UINT64 getOrderSize() const
FB_UINT64 getFrameSize() const
{
return orderEnd - orderStart + 1;
return frameEnd - frameStart + 1;
}
bool move(SINT64 delta);
FB_UINT64 getRecordPosition() const
{
return savedPosition;
}
bool moveWithinPartition(SINT64 delta);
bool moveWithinFrame(SINT64 delta);
private:
thread_db* tdbb;
const BaseBufferedStream* const stream;
const NestValueArray* group;
jrd_req* request;
Firebird::Array<impure_value> partitionKeys;
FB_UINT64 partitionStart;
FB_UINT64 partitionEnd;
FB_UINT64 orderStart;
FB_UINT64 orderEnd;
FB_UINT64 frameStart;
FB_UINT64 frameEnd;
FB_UINT64 savedPosition;
bool moved;
};
class AggregatedStream : public RecordStream
template <typename ThisType, typename NextType>
class BaseAggWinStream : public RecordStream
{
protected:
enum State
{
STATE_EOF, // We processed everything now process EOF
@ -662,74 +667,196 @@ namespace Jrd
STATE_GROUPING // Entering EVL group before fetching the first record
};
struct Block
{
FB_UINT64 startPosition;
FB_UINT64 endPosition;
FB_UINT64 pending;
};
struct Impure : public RecordSource::Impure
{
impure_value* impureValues;
Block partitionBlock;
Block orderBlock;
impure_value* groupValues;
State state;
bool lastGroup;
};
struct DummyAdjustFunctor
{
void operator ()(impure_value* target)
{
}
};
public:
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
const NestValueArray* group, MapNode* map, BaseBufferedStream* next,
const NestValueArray* order);
AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
const NestValueArray* group, MapNode* map, RecordSource* next);
BaseAggWinStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
const NestValueArray* group, MapNode* groupMap, bool oneRowWhenEmpty, NextType* next);
public:
void open(thread_db* tdbb) const 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

View File

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

View File

@ -127,17 +127,10 @@ inline void impure_value::make_double(const double val)
this->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&this->vlu_misc.vlu_double);
}
enum AggType
{
AGG_TYPE_GROUP,
AGG_TYPE_ORDER
};
struct impure_value_ex : public impure_value
{
SINT64 vlux_count;
blb* vlu_blob;
AggType aggType;
};
const int VLU_computed = 1; // An invariant sub-query has been computed

View File

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

View File

@ -9,7 +9,7 @@ BuildType=T
MajorVer=4
MinorVer=0
RevNo=0
BuildNum=354
BuildNum=366
NowAt=`pwd`
cd `dirname $0`

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
--
('2016-07-28 14:55:12', 'JRD', 0, 796)
('2016-09-02 12:40:00', 'JRD', 0, 801)
('2015-03-17 18:33:00', 'QLI', 1, 533)
('2015-01-07 18:01:51', 'GFIX', 3, 134)
('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -903,6 +903,11 @@ Data source : @4', NULL, NULL)
('crypt_checksum', 'CryptoManager::checkDigitalSignature', 'CryptoManager.cpp', NULL, 0, 793, NULL, 'Invalid or missing checksum of encrypted database', NULL, NULL);
('not_dba', 'Service::start', 'svc.cpp', NULL, 0, 794, NULL, 'You must have SYSDBA rights at this server', NULL, NULL);
('no_cursor', 'DSQL_open', 'dsql.cpp', NULL, 0, 795, NULL, 'Cannot open cursor for non-SELECT statement', NULL, NULL);
('dsql_window_incompat_frames', NULL, 'ExprNodes.cpp', NULL, 0, 796, NULL, 'If <window frame bound 1> specifies @1, then <window frame bound 2> shall not specify @2', NULL, NULL);
('dsql_window_range_multi_key', NULL, 'ExprNodes.cpp', NULL, 0, 797, NULL, 'RANGE based window with <expr> {PRECEDING | FOLLOWING} cannot have ORDER BY with more than one value', NULL, NULL);
('dsql_window_range_inv_key_type', NULL, 'ExprNodes.cpp', NULL, 0, 798, NULL, 'RANGE based window must have an ORDER BY key of numerical, date, time or timestamp types', NULL, NULL);
('dsql_window_frame_value_inv_type', NULL, 'ExprNodes.cpp', NULL, 0, 799, NULL, 'Window RANGE/ROWS PRECEDING/FOLLOWING value must be of a numerical type', NULL, NULL);
('window_frame_value_invalid', NULL, 'ExprNodes.cpp', NULL, 0, 800, NULL, 'Invalid PRECEDING or FOLLOWING offset in window function: cannot be negative', NULL, NULL);
-- QLI
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);

View File

@ -802,6 +802,11 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-902, 'XX', '000', 0, 793, 'crypt_checksum', NULL, NULL)
(-902, '28', '000', 0, 794, 'not_dba', NULL, NULL)
(-901, '07', '005', 0, 795, 'no_cursor', NULL, NULL)
(-104, '42', '000', 0, 796, 'dsql_window_incompat_frames', NULL, NULL)
(-104, '42', '000', 0, 797, 'dsql_window_range_multi_key', NULL, NULL)
(-104, '42', '000', 0, 798, 'dsql_window_range_inv_key_type', NULL, NULL)
(-104, '42', '000', 0, 799, 'dsql_window_frame_value_inv_type', NULL, NULL)
(-833, '42', '000', 0, 800, 'window_frame_value_invalid', NULL, NULL)
-- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)

View File

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

View File

@ -702,7 +702,10 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock,
}
if (compress)
{
port->initCompression();
port->port_flags |= PORT_compressed;
}
return port;
}

View File

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

View File

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

View File

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

View File

@ -259,6 +259,7 @@ const int op_derived_expr = 25;
const int op_partition_args = 26;
const int op_subproc_decl = 27;
const int op_subfunc_decl = 28;
const int op_window_win = 29;
static const UCHAR
// generic print formats
@ -339,7 +340,8 @@ static const UCHAR
decode[] = { op_line, op_verb, op_indent, op_byte, op_line, op_args, op_indent, op_byte,
op_line, op_args, 0},
subproc_decl[] = { op_subproc_decl, 0},
subfunc_decl[] = { op_subfunc_decl, 0};
subfunc_decl[] = { op_subfunc_decl, 0},
window_win[] = { op_byte, op_window_win, 0};
#include "../jrd/blp.h"
@ -3597,6 +3599,94 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
break;
}
case op_window_win:
{
offset = blr_print_line(control, offset);
static const char* sub_codes[] =
{
NULL,
"partition",
"order",
"map",
"extent_unit",
"extent_frame_bound",
"extent_frame_value",
"exclusion"
};
while ((blr_operator = control->ctl_blr_reader.getByte()) != blr_end)
{
blr_indent(control, level);
if (blr_operator > 0 && blr_operator < FB_NELEM(sub_codes))
blr_format(control, "blr_window_win_%s, ", sub_codes[blr_operator]);
switch (blr_operator)
{
case blr_window_win_partition:
case blr_window_win_order:
n = blr_print_byte(control);
offset = blr_print_line(control, offset);
++level;
while (--n >= 0)
{
blr_print_verb(control, level);
if (blr_operator == blr_window_win_partition)
blr_print_verb(control, level);
}
--level;
break;
case blr_window_win_map:
n = blr_print_word(control);
offset = blr_print_line(control, offset);
++level;
while (--n >= 0)
{
blr_indent(control, level);
blr_print_word(control);
offset = blr_print_line(control, (SSHORT) offset);
blr_print_verb(control, level);
}
--level;
break;
case blr_window_win_extent_unit:
case blr_window_win_exclusion:
blr_print_byte(control);
offset = blr_print_line(control, offset);
break;
case blr_window_win_extent_frame_bound:
blr_print_byte(control);
blr_print_byte(control);
offset = blr_print_line(control, offset);
break;
case blr_window_win_extent_frame_value:
blr_print_byte(control);
offset = blr_print_line(control, offset);
++level;
blr_print_verb(control, level);
--level;
break;
default:
fb_assert(false);
}
}
// print blr_end
control->ctl_blr_reader.seekBackward(1);
blr_print_verb(control, level);
break;
}
default:
fb_assert(false);
break;

View File

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