From 66568b12d5b027889d66c0c527f907c76b6339cc Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Wed, 22 Dec 2021 19:55:24 +0300 Subject: [PATCH 001/187] Fixed #7080: Executing batch crashes server (cherry picked from commit 7776f552c2988dec6d2d8b3e8bad131206f91eff) --- src/remote/client/interface.cpp | 5 +++++ src/remote/protocol.cpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index dca515ecf5..1343aeadc4 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -8612,6 +8612,11 @@ static void receive_packet_noqueue(rem_port* port, PACKET* packet) bCheckResponse = true; break; + case op_batch_create: + stmt_id = p->packet.p_batch_create.p_batch_statement; + bCheckResponse = true; + break; + case op_free_statement: stmt_id = p->packet.p_sqlfree.p_sqlfree_statement; bFreeStmt = (p->packet.p_sqlfree.p_sqlfree_option == DSQL_drop); diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index 890472c45e..926091c4d0 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -888,6 +888,8 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) ULONG count = b->p_batch_messages; ULONG size = statement->rsr_batch_size; + if (!size) + statement->rsr_batch_size = size = FB_ALIGN(statement->rsr_format->fmt_length, FB_ALIGNMENT); if (xdrs->x_op == XDR_DECODE) { b->p_batch_data.cstr_length = (count ? count : 1) * size; From 2920891287dc2af781981bf1a011ff010628c6bb Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Wed, 22 Dec 2021 15:18:46 -0300 Subject: [PATCH 002/187] Fixed #7084 - Creating unique constraints on MacOS fails on larger tables. configure.ac macros were not compiling and returning 1. That caused FB_ALIGNMENT and FB_DOUBLE_ALIGN to have the wrong value 1. --- configure.ac | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index beebfbb57d..0dcb0ca19a 100644 --- a/configure.ac +++ b/configure.ac @@ -1049,7 +1049,7 @@ AC_MSG_CHECKING(for working sem_init()) AC_RUN_IFELSE([AC_LANG_SOURCE([[#include main () { sem_t s; - exit(sem_init(&s,0,0)); + return sem_init(&s,0,0); } ]])],[AC_DEFINE(WORKING_SEM_INIT,1,[Define this if sem_init() works on the platform]) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) @@ -1089,7 +1089,7 @@ if test "$ac_cv_sys_file_offset_bits" = "no"; then AC_MSG_CHECKING(for native large file support) AC_RUN_IFELSE([AC_LANG_SOURCE([[#include main () { - exit(!(sizeof(off_t) == 8)); + return !(sizeof(off_t) == 8); }]])],[ac_cv_sys_file_offset_bits=64; AC_DEFINE(_FILE_OFFSET_BITS,64) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)],[]) fi @@ -1140,7 +1140,7 @@ main () { char a; union { long long x; sem_t y; } b; }; - exit((int)&((struct s*)0)->b); + return (int)&((struct s*)0)->b; }]])],[ac_cv_c_alignment=$ac_status],[ac_cv_c_alignment=$ac_status],[]) AC_MSG_RESULT($ac_cv_c_alignment) AC_DEFINE_UNQUOTED(FB_ALIGNMENT, $ac_cv_c_alignment, [Alignment of long]) @@ -1151,7 +1151,7 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[main () { char a; double b; }; - exit((int)&((struct s*)0)->b); + return (int)&((struct s*)0)->b; }]])],[ac_cv_c_double_align=$ac_status],[ac_cv_c_double_align=$ac_status],[]) AC_MSG_RESULT($ac_cv_c_double_align) AC_DEFINE_UNQUOTED(FB_DOUBLE_ALIGN, $ac_cv_c_double_align, [Alignment of double]) @@ -1168,7 +1168,7 @@ static int abs64Compare(SINT64 n1, SINT64 n2) { return n1 == n2 ? 0 : n1 < n2 ? 1 : -1; } int main() { - exit (abs64Compare(-9223372036854775808, 3652058) == 1 ? 0 : 1); + return abs64Compare(-9223372036854775808, 3652058) == 1 ? 0 : 1; }]])],[ac_cv_compare_failed=0 ac_cv_compare_result=success]) CFLAGS="$savedflags" From f9fbd80ec6d33efe72e8f1d2088652ae0c90d00f Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 23 Dec 2021 00:05:22 +0000 Subject: [PATCH 003/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index d112aef6b2..7faf924a13 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:351 + FORMAL BUILD NUMBER:353 */ -#define PRODUCT_VER_STRING "5.0.0.351" -#define FILE_VER_STRING "WI-T5.0.0.351" -#define LICENSE_VER_STRING "WI-T5.0.0.351" -#define FILE_VER_NUMBER 5, 0, 0, 351 +#define PRODUCT_VER_STRING "5.0.0.353" +#define FILE_VER_STRING "WI-T5.0.0.353" +#define LICENSE_VER_STRING "WI-T5.0.0.353" +#define FILE_VER_NUMBER 5, 0, 0, 353 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "351" +#define FB_BUILD_NO "353" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 0070254189..8411aedb07 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=351 +BuildNum=353 NowAt=`pwd` cd `dirname $0` From 02c30525ca03dbb30edffb55af5db61395440dda Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 24 Dec 2021 09:37:44 +0300 Subject: [PATCH 004/187] Cleanup the remainders of the BLR-backed savepoint operations. --- src/dsql/dsql.cpp | 3 +- src/dsql/gen.cpp | 79 ++++++++++++++++++++--------------------------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 9902782246..682e08d84b 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -697,8 +697,7 @@ void DsqlDmlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, boo bool DsqlDmlRequest::needRestarts() { - return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency) && - statement->getType() != DsqlCompiledStatement::TYPE_SAVEPOINT); + return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency)); }; // Execute a dynamic SQL statement diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index 627a2c0edd..e0dc3944a9 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -239,60 +239,49 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) else scratch->appendUChar(blr_version5); - if (statement->getType() == DsqlCompiledStatement::TYPE_SAVEPOINT) + const bool block = statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK || + statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK; + + // To parse sub-routines messages, they must not have that begin...end pair. + // And since it appears to be unnecessary for execute block too, do not generate them. + if (!block) + scratch->appendUChar(blr_begin); + + GEN_hidden_variables(scratch); + + switch (statement->getType()) { - // Do not generate BEGIN..END block around savepoint statement - // to avoid breaking of savepoint logic - statement->setSendMsg(NULL); - statement->setReceiveMsg(NULL); + case DsqlCompiledStatement::TYPE_SELECT: + case DsqlCompiledStatement::TYPE_SELECT_UPD: + case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlCompiledStatement::TYPE_SELECT_BLOCK: node->genBlr(scratch); - } - else - { - const bool block = statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK || - statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK; + break; - // To parse sub-routines messages, they must not have that begin...end pair. - // And since it appears to be unnecessary for execute block too, do not generate them. - if (!block) - scratch->appendUChar(blr_begin); - - GEN_hidden_variables(scratch); - - switch (statement->getType()) + ///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + default: { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - node->genBlr(scratch); - break; - - ///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - default: + dsql_msg* message = statement->getSendMsg(); + if (!message->msg_parameter) + statement->setSendMsg(NULL); + else { - dsql_msg* message = statement->getSendMsg(); - if (!message->msg_parameter) - statement->setSendMsg(NULL); - else - { - GEN_port(scratch, message); - scratch->appendUChar(blr_receive_batch); - scratch->appendUChar(message->msg_number); - } - message = statement->getReceiveMsg(); - if (!message->msg_parameter) - statement->setReceiveMsg(NULL); - else - GEN_port(scratch, message); - node->genBlr(scratch); + GEN_port(scratch, message); + scratch->appendUChar(blr_receive_batch); + scratch->appendUChar(message->msg_number); } + message = statement->getReceiveMsg(); + if (!message->msg_parameter) + statement->setReceiveMsg(NULL); + else + GEN_port(scratch, message); + node->genBlr(scratch); } - - if (!block) - scratch->appendUChar(blr_end); } + if (!block) + scratch->appendUChar(blr_end); + scratch->appendUChar(blr_eoc); } From 850e21a34ce34b1bc20c9c23bceff0f8070adc39 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 24 Dec 2021 11:29:37 -0300 Subject: [PATCH 005/187] Fix warnings. --- examples/udr/Triggers.cpp | 2 -- src/dsql/metd.epp | 7 ------- src/jrd/vio.cpp | 9 +++------ 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/examples/udr/Triggers.cpp b/examples/udr/Triggers.cpp index 4e9bcca834..f397b9a373 100644 --- a/examples/udr/Triggers.cpp +++ b/examples/udr/Triggers.cpp @@ -234,8 +234,6 @@ FB_UDR_BEGIN_TRIGGER(replicate_persons) "select data_source from replicate_config where name = ?", SQL_DIALECT_CURRENT, NULL), status, statusVector); - const char* table = metadata->getTriggerTable(status); - // Skip the first exclamation point, separating the module name and entry point. const char* info = strchr(metadata->getEntryPoint(status), '!'); diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 9eb1ea8528..bfb47f644e 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -63,13 +63,6 @@ using namespace Firebird; DATABASE DB = STATIC "yachts.lnk"; -static const UCHAR blr_bpb[] = -{ - isc_bpb_version1, - isc_bpb_source_type, 1, isc_blob_blr, - isc_bpb_target_type, 1, isc_blob_blr -}; - static void convert_dtype(TypeClause*, SSHORT); static void free_relation(dsql_rel*); diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 8af88effdc..07aaa3fb7a 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -238,7 +238,6 @@ inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation, inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation) { const Attachment* const attachment = tdbb->getAttachment(); - const jrd_tra* const transaction = tdbb->getTransaction(); if (relation->isSystem() && attachment->isGbak()) { @@ -2527,8 +2526,6 @@ bool VIO_get_current(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); - Attachment* const attachment = tdbb->getAttachment(); - #ifdef VIO_DEBUG jrd_rel* relation = rpb->rpb_relation; VIO_trace(DEBUG_TRACE, @@ -5195,12 +5192,12 @@ static void list_staying_fast(thread_db* tdbb, record_param* rpb, RecordStack& s } } - const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; + ///const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; while (temp.rpb_b_page) { - ULONG page = temp.rpb_page = temp.rpb_b_page; - USHORT line = temp.rpb_line = temp.rpb_b_line; + ///ULONG page = temp.rpb_page = temp.rpb_b_page; + ///USHORT line = temp.rpb_line = temp.rpb_b_line; temp.rpb_record = NULL; if (temp.rpb_flags & rpb_delta) From 68a7e885722f5684154db978ab8e9a54888e08fb Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 24 Dec 2021 17:42:04 +0300 Subject: [PATCH 006/187] Raise protocol version --- src/remote/inet.cpp | 3 ++- src/remote/os/win32/xnet.cpp | 3 ++- src/remote/protocol.h | 9 +++++++-- src/remote/server/server.cpp | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index b706178066..92471b149c 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -716,7 +716,8 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_lazy_send, 5), REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_lazy_send, 6), REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_lazy_send, 7), - REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_lazy_send, 8) + REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_lazy_send, 8), + REMOTE_PROTOCOL(PROTOCOL_VERSION18, ptype_lazy_send, 9) }; fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); cnct->p_cnct_count = FB_NELEM(protocols_to_try); diff --git a/src/remote/os/win32/xnet.cpp b/src/remote/os/win32/xnet.cpp index b1645657c0..9dc8e34557 100644 --- a/src/remote/os/win32/xnet.cpp +++ b/src/remote/os/win32/xnet.cpp @@ -306,7 +306,8 @@ rem_port* XNET_analyze(ClntAuthBlock* cBlock, REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_batch_send, 5), REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_batch_send, 6), REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_batch_send, 7), - REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_batch_send, 8) + REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_batch_send, 8), + REMOTE_PROTOCOL(PROTOCOL_VERSION18, ptype_batch_send, 9) }; fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); cnct->p_cnct_count = FB_NELEM(protocols_to_try); diff --git a/src/remote/protocol.h b/src/remote/protocol.h index 862f896db0..8acfde2ce1 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -94,10 +94,15 @@ const USHORT PROTOCOL_VERSION16 = (FB_PROTOCOL_FLAG | 16); const USHORT PROTOCOL_STMT_TOUT = PROTOCOL_VERSION16; // Protocol 17: -// - supports op_batch_sync, op_info_batch, op_fetch_scroll +// - supports op_batch_sync, op_info_batch const USHORT PROTOCOL_VERSION17 = (FB_PROTOCOL_FLAG | 17); -const USHORT PROTOCOL_FETCH_SCROLL = PROTOCOL_VERSION17; + +// Protocol 18: +// - supports op_fetch_scroll + +const USHORT PROTOCOL_VERSION18 = (FB_PROTOCOL_FLAG | 18); +const USHORT PROTOCOL_FETCH_SCROLL = PROTOCOL_VERSION18; // Architecture types diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 4873772dab..10c6ea2dbe 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -1920,7 +1920,7 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) { if ((protocol->p_cnct_version == PROTOCOL_VERSION10 || (protocol->p_cnct_version >= PROTOCOL_VERSION11 && - protocol->p_cnct_version <= PROTOCOL_VERSION17)) && + protocol->p_cnct_version <= PROTOCOL_VERSION18)) && (protocol->p_cnct_architecture == arch_generic || protocol->p_cnct_architecture == ARCHITECTURE) && protocol->p_cnct_weight >= weight) From 00218503d69b7383c2d6cc40f6867982df87a23e Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 24 Dec 2021 11:54:52 -0300 Subject: [PATCH 007/187] Revert "Fix warnings." This reverts commit 850e21a34ce34b1bc20c9c23bceff0f8070adc39. --- examples/udr/Triggers.cpp | 2 ++ src/dsql/metd.epp | 7 +++++++ src/jrd/vio.cpp | 9 ++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/udr/Triggers.cpp b/examples/udr/Triggers.cpp index f397b9a373..4e9bcca834 100644 --- a/examples/udr/Triggers.cpp +++ b/examples/udr/Triggers.cpp @@ -234,6 +234,8 @@ FB_UDR_BEGIN_TRIGGER(replicate_persons) "select data_source from replicate_config where name = ?", SQL_DIALECT_CURRENT, NULL), status, statusVector); + const char* table = metadata->getTriggerTable(status); + // Skip the first exclamation point, separating the module name and entry point. const char* info = strchr(metadata->getEntryPoint(status), '!'); diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index bfb47f644e..9eb1ea8528 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -63,6 +63,13 @@ using namespace Firebird; DATABASE DB = STATIC "yachts.lnk"; +static const UCHAR blr_bpb[] = +{ + isc_bpb_version1, + isc_bpb_source_type, 1, isc_blob_blr, + isc_bpb_target_type, 1, isc_blob_blr +}; + static void convert_dtype(TypeClause*, SSHORT); static void free_relation(dsql_rel*); diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 07aaa3fb7a..8af88effdc 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -238,6 +238,7 @@ inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation, inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation) { const Attachment* const attachment = tdbb->getAttachment(); + const jrd_tra* const transaction = tdbb->getTransaction(); if (relation->isSystem() && attachment->isGbak()) { @@ -2526,6 +2527,8 @@ bool VIO_get_current(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); + Attachment* const attachment = tdbb->getAttachment(); + #ifdef VIO_DEBUG jrd_rel* relation = rpb->rpb_relation; VIO_trace(DEBUG_TRACE, @@ -5192,12 +5195,12 @@ static void list_staying_fast(thread_db* tdbb, record_param* rpb, RecordStack& s } } - ///const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; + const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; while (temp.rpb_b_page) { - ///ULONG page = temp.rpb_page = temp.rpb_b_page; - ///USHORT line = temp.rpb_line = temp.rpb_b_line; + ULONG page = temp.rpb_page = temp.rpb_b_page; + USHORT line = temp.rpb_line = temp.rpb_b_line; temp.rpb_record = NULL; if (temp.rpb_flags & rpb_delta) From 80e7651261edf8d1f5e63cfb6127def9804f8eae Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 24 Dec 2021 11:57:57 -0300 Subject: [PATCH 008/187] Fix warnings. --- examples/udr/Triggers.cpp | 2 -- src/dsql/metd.epp | 7 ------- src/jrd/vio.cpp | 12 ++++++------ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/examples/udr/Triggers.cpp b/examples/udr/Triggers.cpp index 4e9bcca834..f397b9a373 100644 --- a/examples/udr/Triggers.cpp +++ b/examples/udr/Triggers.cpp @@ -234,8 +234,6 @@ FB_UDR_BEGIN_TRIGGER(replicate_persons) "select data_source from replicate_config where name = ?", SQL_DIALECT_CURRENT, NULL), status, statusVector); - const char* table = metadata->getTriggerTable(status); - // Skip the first exclamation point, separating the module name and entry point. const char* info = strchr(metadata->getEntryPoint(status), '!'); diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 9eb1ea8528..bfb47f644e 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -63,13 +63,6 @@ using namespace Firebird; DATABASE DB = STATIC "yachts.lnk"; -static const UCHAR blr_bpb[] = -{ - isc_bpb_version1, - isc_bpb_source_type, 1, isc_blob_blr, - isc_bpb_target_type, 1, isc_blob_blr -}; - static void convert_dtype(TypeClause*, SSHORT); static void free_relation(dsql_rel*); diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 8af88effdc..5443e920b6 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -238,7 +238,6 @@ inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation, inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation) { const Attachment* const attachment = tdbb->getAttachment(); - const jrd_tra* const transaction = tdbb->getTransaction(); if (relation->isSystem() && attachment->isGbak()) { @@ -2527,8 +2526,6 @@ bool VIO_get_current(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); - Attachment* const attachment = tdbb->getAttachment(); - #ifdef VIO_DEBUG jrd_rel* relation = rpb->rpb_relation; VIO_trace(DEBUG_TRACE, @@ -5195,12 +5192,15 @@ static void list_staying_fast(thread_db* tdbb, record_param* rpb, RecordStack& s } } - const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; + ///const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; while (temp.rpb_b_page) { - ULONG page = temp.rpb_page = temp.rpb_b_page; - USHORT line = temp.rpb_line = temp.rpb_b_line; + ///ULONG page = temp.rpb_page = temp.rpb_b_page; + ///USHORT line = temp.rpb_line = temp.rpb_b_line; + temp.rpb_page = temp.rpb_b_page; + temp.rpb_line = temp.rpb_b_line; + temp.rpb_record = NULL; if (temp.rpb_flags & rpb_delta) From a0c88b83033b2bc7cf77e0c21977ea5e2200fc24 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 25 Dec 2021 00:05:18 +0000 Subject: [PATCH 009/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 7faf924a13..ad63467e1b 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:353 + FORMAL BUILD NUMBER:358 */ -#define PRODUCT_VER_STRING "5.0.0.353" -#define FILE_VER_STRING "WI-T5.0.0.353" -#define LICENSE_VER_STRING "WI-T5.0.0.353" -#define FILE_VER_NUMBER 5, 0, 0, 353 +#define PRODUCT_VER_STRING "5.0.0.358" +#define FILE_VER_STRING "WI-T5.0.0.358" +#define LICENSE_VER_STRING "WI-T5.0.0.358" +#define FILE_VER_NUMBER 5, 0, 0, 358 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "353" +#define FB_BUILD_NO "358" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 8411aedb07..a8defc4cd9 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=353 +BuildNum=358 NowAt=`pwd` cd `dirname $0` From 9772a398af7daef47279de53d75603c219d56b02 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 26 Dec 2021 11:35:16 -0300 Subject: [PATCH 010/187] Fix MacOS UDR and Legacy_UserManager plugins not working due to not exported entry point. (#7088) Add FB_DLL_EXPORT to public headers. Use default visibility also for Linux/Unix (in addition to MacOS) as user application can also be compiled with -fvisibility=hidden. --- doc/Using_OO_API.html | 4 ++-- examples/dbcrypt/CryptKeyHolder.cpp | 2 +- examples/dbcrypt/DbCrypt.cpp | 2 +- examples/extauth/ExtAuth.cpp | 8 +------- examples/interfaces/ifaceExamples.h | 8 -------- examples/replication/fbSampleReplicator.cpp | 9 +-------- src/auth/AuthDbg.cpp | 2 +- .../SecureRemotePassword/manage/SrpManagement.cpp | 10 +--------- src/auth/SecurityDatabase/LegacyManagement.epp | 2 +- src/auth/SecurityDatabase/LegacyServer.cpp | 2 +- src/common/common.h | 6 ------ src/extlib/ib_util.cpp | 8 +++----- src/include/firebird.h | 7 ------- src/include/firebird/UdrCppEngine.h | 2 +- src/include/ibase.h | 12 ++++++++++++ src/intl/ld.cpp | 8 ++++---- src/intl/ld.h | 8 -------- src/intl/ld_proto.h | 8 ++++---- src/jrd/jrd.cpp | 2 +- src/plugins/crypt/chacha/ChaCha.cpp | 2 +- src/plugins/udr_engine/UdrEngine.cpp | 2 +- src/remote/client/interface.cpp | 2 +- src/utilities/ntrace/traceplugin.cpp | 2 +- src/yvalve/gds.cpp | 4 ++-- src/yvalve/gds_proto.h | 6 ++++-- 25 files changed, 45 insertions(+), 83 deletions(-) diff --git a/doc/Using_OO_API.html b/doc/Using_OO_API.html index b15ab1adf4..841a4b49d4 100644 --- a/doc/Using_OO_API.html +++ b/doc/Using_OO_API.html @@ -1673,7 +1673,7 @@ only for some specific OS you can make this place a bit simpler. In minimum case the function should register module and all factories in plugin manager:

extern -"C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* +"C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master)

{

IPluginManager* @@ -3793,4 +3793,4 @@ release of it.

- \ No newline at end of file + diff --git a/examples/dbcrypt/CryptKeyHolder.cpp b/examples/dbcrypt/CryptKeyHolder.cpp index bc79942238..efa57df60f 100644 --- a/examples/dbcrypt/CryptKeyHolder.cpp +++ b/examples/dbcrypt/CryptKeyHolder.cpp @@ -298,7 +298,7 @@ Factory factory; } // anonymous namespace -extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* m) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* m) { master = m; IPluginManager* pluginManager = master->getPluginManager(); diff --git a/examples/dbcrypt/DbCrypt.cpp b/examples/dbcrypt/DbCrypt.cpp index fcfc1ba3f4..6b0260eb27 100644 --- a/examples/dbcrypt/DbCrypt.cpp +++ b/examples/dbcrypt/DbCrypt.cpp @@ -266,7 +266,7 @@ Factory factory; } // anonymous namespace -extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { IPluginManager* pluginManager = master->getPluginManager(); diff --git a/examples/extauth/ExtAuth.cpp b/examples/extauth/ExtAuth.cpp index 5c3c5efd4d..56926575f6 100644 --- a/examples/extauth/ExtAuth.cpp +++ b/examples/extauth/ExtAuth.cpp @@ -435,13 +435,7 @@ Factory serverFactory; } // anonymous namespace -#if defined(_WIN32) -#define FB_DLL_EXPORT __declspec(dllexport) -#else -#define FB_DLL_EXPORT -#endif - -extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* m) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* m) { master = m; IPluginManager* pluginManager = master->getPluginManager(); diff --git a/examples/interfaces/ifaceExamples.h b/examples/interfaces/ifaceExamples.h index da349264dc..cfcb1f9dca 100644 --- a/examples/interfaces/ifaceExamples.h +++ b/examples/interfaces/ifaceExamples.h @@ -37,14 +37,6 @@ typedef int FbSampleAtomic; #include -#if defined(_WIN32) -#define FB_DLL_EXPORT __declspec(dllexport) -#elif defined(__APPLE__) -#define FB_DLL_EXPORT __attribute__((visibility("default"))) -#else -#define FB_DLL_EXPORT -#endif - using namespace Firebird; #define SAMPLES_DIALECT SQL_DIALECT_V6 diff --git a/examples/replication/fbSampleReplicator.cpp b/examples/replication/fbSampleReplicator.cpp index fa8f63232b..fcf59537b2 100644 --- a/examples/replication/fbSampleReplicator.cpp +++ b/examples/replication/fbSampleReplicator.cpp @@ -100,14 +100,7 @@ public: extern "C" { -#if defined(__WIN32__) - void __declspec(dllexport) FB_PLUGIN_ENTRY_POINT(IMaster* m); -#else - void FB_PLUGIN_ENTRY_POINT(IMaster* m) - __attribute__((visibility("default"))); -#endif // __WIN32__ - - void FB_PLUGIN_ENTRY_POINT(IMaster* m) + FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* m) { master = m; IPluginManager* pm = m->getPluginManager(); diff --git a/src/auth/AuthDbg.cpp b/src/auth/AuthDbg.cpp index 6f2110e629..386f9f5e23 100644 --- a/src/auth/AuthDbg.cpp +++ b/src/auth/AuthDbg.cpp @@ -38,7 +38,7 @@ static Firebird::SimpleFactory clientFactory; static Firebird::SimpleFactory serverFactory; -extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index b2150ac155..ce17331521 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -38,14 +38,6 @@ #include "../common/classes/auto.h" #include "../common/classes/ParsedList.h" -#ifndef FB_EXPORTED -#if defined(DARWIN) -#define FB_EXPORTED __attribute__((visibility("default"))) -#else -#define FB_EXPORTED -#endif // OS choice (DARWIN) -#endif // FB_EXPORTED - namespace { const unsigned int SZ_LOGIN = 31; @@ -986,7 +978,7 @@ static Firebird::SimpleFactory factory; } // namespace Auth -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); Firebird::PluginManagerInterfacePtr()->registerPluginFactory(Firebird::IPluginManager::TYPE_AUTH_USER_MANAGEMENT, Auth::RemotePassword::plugName, &Auth::factory); diff --git a/src/auth/SecurityDatabase/LegacyManagement.epp b/src/auth/SecurityDatabase/LegacyManagement.epp index b60aeed564..80cbc070dc 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.epp +++ b/src/auth/SecurityDatabase/LegacyManagement.epp @@ -762,7 +762,7 @@ int SecurityDatabaseManagement::execute(Firebird::CheckStatusWrapper* st, Firebi // register plugin static Firebird::SimpleFactory factory; -extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); Firebird::PluginManagerInterfacePtr()->registerPluginFactory( diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 23f9b471c2..0c7e59a855 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -411,7 +411,7 @@ void registerLegacyServer(IPluginManager* iPlugin) #ifdef PLUG_MODULE -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { CachedMasterInterface::set(master); diff --git a/src/common/common.h b/src/common/common.h index c5d3079fc1..c73511cfa1 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -252,8 +252,6 @@ #define API_ROUTINE __attribute__((visibility("default"))) #define API_ROUTINE_VARARG API_ROUTINE -#define INTERNAL_API_ROUTINE API_ROUTINE -#define FB_EXPORTED __attribute__((visibility("default"))) #define O_DIRECT F_NOCACHE #endif /* Darwin Platforms */ @@ -603,10 +601,6 @@ extern "C" int remove(const char* path); #define CLIB_ROUTINE #endif -#ifndef FB_EXPORTED -#define FB_EXPORTED -#endif - #ifdef HAS_NOEXCEPT #define NOEXCEPT noexcept #define NOEXCEPT_ARG(X) noexcept((X)) diff --git a/src/extlib/ib_util.cpp b/src/extlib/ib_util.cpp index 114825ae85..614fc96d2d 100644 --- a/src/extlib/ib_util.cpp +++ b/src/extlib/ib_util.cpp @@ -19,21 +19,19 @@ */ #include -#include "ib_util.h" #include "firebird.h" - -typedef void* VoidPtr; +#include "ibase.h" // initialized by the engine static void* (*allocFunc)(long) = NULL; -extern "C" void FB_EXPORTED ib_util_init(void* (*aAllocFunc)(long)) +extern "C" FB_DLL_EXPORT void ib_util_init(void* (*aAllocFunc)(long)) { allocFunc = aAllocFunc; } -extern "C" VoidPtr FB_EXPORTED ib_util_malloc(long size) +extern "C" FB_DLL_EXPORT void* ib_util_malloc(long size) { return allocFunc ? allocFunc(size) : malloc(size); } diff --git a/src/include/firebird.h b/src/include/firebird.h index 1bd7f2d4b3..391d7707ce 100644 --- a/src/include/firebird.h +++ b/src/include/firebird.h @@ -43,13 +43,6 @@ #define DEBUG_GDS_ALLOC #endif -#if defined(WIN_NT) -#define FB_DLL_EXPORT __declspec(dllexport) -#elif defined(DARWIN) -#define FB_DLL_EXPORT API_ROUTINE -#else -#define FB_DLL_EXPORT -#endif //#if defined(SOLX86) // this pragmas is used only with gcc 2.95! //#define __PRAGMA_REDEFINE_EXTNAME diff --git a/src/include/firebird/UdrCppEngine.h b/src/include/firebird/UdrCppEngine.h index 26ef5a0e90..d9aac7e5d3 100644 --- a/src/include/firebird/UdrCppEngine.h +++ b/src/include/firebird/UdrCppEngine.h @@ -43,7 +43,7 @@ } \ } \ \ - extern "C" FB_BOOLEAN* FB_UDR_PLUGIN_ENTRY_POINT(::Firebird::IStatus* status, \ + extern "C" FB_DLL_EXPORT FB_BOOLEAN* FB_UDR_PLUGIN_ENTRY_POINT(::Firebird::IStatus* status, \ FB_BOOLEAN* theirUnloadFlag, ::Firebird::IUdrPlugin* udrPlugin) \ { \ ::Firebird::Udr::FactoryRegistration::finish(status, udrPlugin); \ diff --git a/src/include/ibase.h b/src/include/ibase.h index 6f7e593b2c..957afbb2d3 100644 --- a/src/include/ibase.h +++ b/src/include/ibase.h @@ -64,6 +64,18 @@ #define FB_API_DEPRECATED #endif +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +#define FB_DLL_EXPORT __declspec(dllexport) +#elif defined __has_attribute +#if __has_attribute (visibility) +#define FB_DLL_EXPORT __attribute__ ((visibility("default"))) +#else +#define FB_DLL_EXPORT +#endif +#else +#define FB_DLL_EXPORT +#endif + #include "./firebird/impl/types_pub.h" /***********************/ diff --git a/src/intl/ld.cpp b/src/intl/ld.cpp index 1056921ee3..151cd3e812 100644 --- a/src/intl/ld.cpp +++ b/src/intl/ld.cpp @@ -468,7 +468,7 @@ struct }; -INTL_BOOL FB_DLL_EXPORT LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* /*config_info*/) +FB_DLL_EXPORT INTL_BOOL LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* /*config_info*/) { // ASF: We can't read config_info if version < INTL_VERSION_2, // since it wasn't pushed in the stack by the engine. @@ -491,7 +491,7 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_charset(charset* cs, const ASCII* name, const } -INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, USHORT attributes, const UCHAR* specific_attributes, ULONG specific_attributes_length, INTL_BOOL ignore_attributes, const ASCII* config_info) @@ -557,7 +557,7 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_n } -ULONG FB_DLL_EXPORT LD_setup_attributes( +FB_DLL_EXPORT ULONG LD_setup_attributes( const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst) { @@ -583,7 +583,7 @@ ULONG FB_DLL_EXPORT LD_setup_attributes( } -void FB_DLL_EXPORT LD_version(USHORT* version) +FB_DLL_EXPORT void LD_version(USHORT* version) { // We support version 1 and 2. if (*version != INTL_VERSION_1) diff --git a/src/intl/ld.h b/src/intl/ld.h index 0ee001f733..0fc0237bad 100644 --- a/src/intl/ld.h +++ b/src/intl/ld.h @@ -55,14 +55,6 @@ #define UINT16 USHORT -#if defined(WIN_NT) -#define FB_DLL_EXPORT __declspec(dllexport) -#elif defined(DARWIN) -#define FB_DLL_EXPORT API_ROUTINE -#else -#define FB_DLL_EXPORT -#endif - /* Following this line is LD.H from Borland Language Driver Kit */ diff --git a/src/intl/ld_proto.h b/src/intl/ld_proto.h index 6a6636dfa1..f20fdf076a 100644 --- a/src/intl/ld_proto.h +++ b/src/intl/ld_proto.h @@ -38,13 +38,13 @@ struct CsConvertImpl extern USHORT version; -INTL_BOOL FB_DLL_EXPORT LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* config_info); -INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, +FB_DLL_EXPORT INTL_BOOL LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* config_info); +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, USHORT attributes, const UCHAR* specific_attributes, ULONG specific_attributes_length, INTL_BOOL ignore_attributes, const ASCII* config_info); -void FB_DLL_EXPORT LD_version(USHORT* version); -ULONG FB_DLL_EXPORT LD_setup_attributes( +FB_DLL_EXPORT void LD_version(USHORT* version); +FB_DLL_EXPORT ULONG LD_setup_attributes( const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index dab848ce40..c6539a42bc 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -465,7 +465,7 @@ void registerEngine(IPluginManager* iPlugin) } // namespace Jrd -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { CachedMasterInterface::set(master); registerEngine(PluginManagerInterfacePtr()); diff --git a/src/plugins/crypt/chacha/ChaCha.cpp b/src/plugins/crypt/chacha/ChaCha.cpp index 33d9c0d4ec..2748a82191 100644 --- a/src/plugins/crypt/chacha/ChaCha.cpp +++ b/src/plugins/crypt/chacha/ChaCha.cpp @@ -189,7 +189,7 @@ SimpleFactory > factory64; } // anonymous namespace -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { CachedMasterInterface::set(master); PluginManagerInterfacePtr()->registerPluginFactory(IPluginManager::TYPE_WIRE_CRYPT, "ChaCha", &factory); diff --git a/src/plugins/udr_engine/UdrEngine.cpp b/src/plugins/udr_engine/UdrEngine.cpp index 25a35cb5e0..e6549c35f2 100644 --- a/src/plugins/udr_engine/UdrEngine.cpp +++ b/src/plugins/udr_engine/UdrEngine.cpp @@ -718,7 +718,7 @@ class IExternalEngineFactoryImpl : public SimpleFactory { } factory; -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { CachedMasterInterface::set(master); diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 1343aeadc4..6567b98f9c 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -1046,7 +1046,7 @@ void registerRedirector(Firebird::IPluginManager* iPlugin) } // namespace Remote /* -extern "C" void FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { IPluginManager* pi = master->getPluginManager(); registerRedirector(pi); diff --git a/src/utilities/ntrace/traceplugin.cpp b/src/utilities/ntrace/traceplugin.cpp index 39cde97145..6fe0c013b4 100644 --- a/src/utilities/ntrace/traceplugin.cpp +++ b/src/utilities/ntrace/traceplugin.cpp @@ -115,7 +115,7 @@ void registerTrace(Firebird::IPluginManager* iPlugin) } -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); registerTrace(Firebird::PluginManagerInterfacePtr()); diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 387b46c2d7..618ddeccb7 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -3977,13 +3977,13 @@ static void sanitize(Firebird::string& locale) } -void FB_EXPORTED gds__default_printer(void* /*arg*/, SSHORT offset, const TEXT* line) +void API_ROUTINE_VARARG gds__default_printer(void* /*arg*/, SSHORT offset, const TEXT* line) { printf("%4d %s\n", offset, line); } -void FB_EXPORTED gds__trace_printer(void* /*arg*/, SSHORT offset, const TEXT* line) +void API_ROUTINE_VARARG gds__trace_printer(void* /*arg*/, SSHORT offset, const TEXT* line) { // Assume that line is not too long char buffer[PRETTY_BUFFER_SIZE + 10]; diff --git a/src/yvalve/gds_proto.h b/src/yvalve/gds_proto.h index 384cce4be1..8f1e91fe04 100644 --- a/src/yvalve/gds_proto.h +++ b/src/yvalve/gds_proto.h @@ -134,8 +134,10 @@ SINT64 API_ROUTINE isc_portable_integer(const UCHAR*, SSHORT); void gds__cleanup(); void gds__ulstr(char* buffer, FB_UINT64 value, const int minlen, const char filler); -void FB_EXPORTED gds__default_printer(void*, SSHORT, const TEXT*); -void FB_EXPORTED gds__trace_printer(void*, SSHORT, const TEXT*); +// These functions uses cdecl convention in Windows, so use API_ROUTINE_VARARG instead of API_ROUTINE (stdcall). +void API_ROUTINE_VARARG gds__default_printer(void*, SSHORT, const TEXT*); +void API_ROUTINE_VARARG gds__trace_printer(void*, SSHORT, const TEXT*); + #ifdef NOT_USED_OR_REPLACED void gds__print_pool(Firebird::MemoryPool*, const TEXT*, ...); #endif From 4d37e9301ab7f5ff3aca6a5f7543e614795c03df Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Mon, 27 Dec 2021 00:05:32 +0000 Subject: [PATCH 011/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index ad63467e1b..781b58f3b0 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:358 + FORMAL BUILD NUMBER:359 */ -#define PRODUCT_VER_STRING "5.0.0.358" -#define FILE_VER_STRING "WI-T5.0.0.358" -#define LICENSE_VER_STRING "WI-T5.0.0.358" -#define FILE_VER_NUMBER 5, 0, 0, 358 +#define PRODUCT_VER_STRING "5.0.0.359" +#define FILE_VER_STRING "WI-T5.0.0.359" +#define LICENSE_VER_STRING "WI-T5.0.0.359" +#define FILE_VER_NUMBER 5, 0, 0, 359 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "358" +#define FB_BUILD_NO "359" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index a8defc4cd9..1df431f9eb 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=358 +BuildNum=359 NowAt=`pwd` cd `dirname $0` From 35b21d32a20b0c3f1993cb476a82a82845c0bdcf Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 31 Dec 2021 14:27:14 +0200 Subject: [PATCH 012/187] Fixed build issues with ext auth samples --- examples/extauth/msvc/ExtAuth_MSVC15.vcxproj | 10 +++++----- examples/extauth/msvc/KeyGen_MSVC15.vcxproj | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj b/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj index f7c55009a5..dc416534f1 100644 --- a/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj +++ b/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj @@ -107,7 +107,7 @@ true true ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -123,7 +123,7 @@ true true ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -141,7 +141,7 @@ true true ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -161,7 +161,7 @@ true true ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -182,4 +182,4 @@ - + \ No newline at end of file diff --git a/examples/extauth/msvc/KeyGen_MSVC15.vcxproj b/examples/extauth/msvc/KeyGen_MSVC15.vcxproj index 09268aed3a..69a896f294 100644 --- a/examples/extauth/msvc/KeyGen_MSVC15.vcxproj +++ b/examples/extauth/msvc/KeyGen_MSVC15.vcxproj @@ -83,22 +83,22 @@ ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen From 128ce239f2f32e9075e1ba7fa05978959063ee33 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 31 Dec 2021 16:10:02 +0200 Subject: [PATCH 013/187] Fixed build issues on win32 with ext auth samples --- examples/extauth/msvc/ExtAuth_MSVC15.vcxproj | 8 ++++---- examples/extauth/msvc/KeyGen_MSVC15.vcxproj | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj b/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj index dc416534f1..d63b18cfa2 100644 --- a/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj +++ b/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj @@ -105,7 +105,7 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 @@ -121,7 +121,7 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 @@ -139,7 +139,7 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 @@ -159,7 +159,7 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 diff --git a/examples/extauth/msvc/KeyGen_MSVC15.vcxproj b/examples/extauth/msvc/KeyGen_MSVC15.vcxproj index 69a896f294..6439e904a5 100644 --- a/examples/extauth/msvc/KeyGen_MSVC15.vcxproj +++ b/examples/extauth/msvc/KeyGen_MSVC15.vcxproj @@ -105,9 +105,9 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -120,9 +120,9 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -137,9 +137,9 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -156,9 +156,9 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console From 3a0078f2c31ef946ec18191a9d8b179b4aa00523 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 31 Dec 2021 22:52:55 +0300 Subject: [PATCH 014/187] Avoid replicating internal DDL statements (just in case they would exist) --- src/dsql/dsql.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 682e08d84b..d73d619c58 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -1016,7 +1016,10 @@ void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, node->executeDdl(tdbb, internalScratch, req_transaction); - if (node->mustBeReplicated()) + const bool isInternalRequest = + (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); + + if (!isInternalRequest && node->mustBeReplicated()) REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); } catch (status_exception& ex) From 545f91cc813a05b9428fa51c51dc8570a58e54fc Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 1 Jan 2022 00:05:24 +0000 Subject: [PATCH 015/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 781b58f3b0..cad3801015 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:359 + FORMAL BUILD NUMBER:362 */ -#define PRODUCT_VER_STRING "5.0.0.359" -#define FILE_VER_STRING "WI-T5.0.0.359" -#define LICENSE_VER_STRING "WI-T5.0.0.359" -#define FILE_VER_NUMBER 5, 0, 0, 359 +#define PRODUCT_VER_STRING "5.0.0.362" +#define FILE_VER_STRING "WI-T5.0.0.362" +#define LICENSE_VER_STRING "WI-T5.0.0.362" +#define FILE_VER_NUMBER 5, 0, 0, 362 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "359" +#define FB_BUILD_NO "362" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 1df431f9eb..6a84024d8c 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=359 +BuildNum=362 NowAt=`pwd` cd `dirname $0` From 3d77ecc5db29b670d67545c7090e93af4680e132 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 3 Jan 2022 15:35:12 -0300 Subject: [PATCH 016/187] Fix #7090 - Performance degradation with CURRENT_DATE, LOCALTIME and LOCALTIMESTAMP. --- src/dsql/ExprNodes.cpp | 71 +++++++------------------------- src/dsql/StmtNodes.cpp | 2 +- src/jrd/btr.cpp | 8 ++-- src/jrd/cvt.cpp | 10 +---- src/jrd/dfw.epp | 2 +- src/jrd/exe.cpp | 10 ++--- src/jrd/recsrc/ProcedureScan.cpp | 2 +- src/jrd/replication/Applier.cpp | 2 +- src/jrd/req.h | 53 +++++++++++++++++++++--- 9 files changed, 77 insertions(+), 83 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 21b64eec0a..8066bf30be 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -4169,19 +4169,9 @@ dsc* CurrentDateNode::execute(thread_db* tdbb, jrd_req* request) const request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); + impure->vlu_misc.vlu_sql_date = request->getLocalTimeStamp().timestamp_date; - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = request->req_gmt_timestamp.value(); - timeStampTz.time_zone = TimeZoneUtil::GMT_ZONE; - - impure->vlu_misc.vlu_sql_date = TimeZoneUtil::timeStampTzToTimeStamp( - timeStampTz, request->req_attachment->att_current_timezone).timestamp_date; - - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_dtype = dtype_sql_date; - impure->vlu_desc.dsc_length = type_lengths[dtype_sql_date]; - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_date; + impure->vlu_desc.makeDate(&impure->vlu_misc.vlu_sql_date); return &impure->vlu_desc; } @@ -4282,21 +4272,15 @@ dsc* CurrentTimeNode::execute(thread_db* tdbb, jrd_req* request) const request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - - ISC_TIMESTAMP_TZ currentTimeStamp; - currentTimeStamp.utc_timestamp = request->req_gmt_timestamp.value(); - currentTimeStamp.time_zone = tdbb->getAttachment()->att_current_timezone; - - impure->vlu_desc.dsc_dtype = dtype_sql_time_tz; - impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time_tz]; - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_time_tz; + ISC_TIMESTAMP_TZ currentTimeStamp = request->getTimeStampTz(); impure->vlu_misc.vlu_sql_time_tz.time_zone = tdbb->getAttachment()->att_current_timezone; impure->vlu_misc.vlu_sql_time_tz.utc_time = TimeZoneUtil::timeStampTzToTimeTz(currentTimeStamp).utc_time; TimeStamp::round_time(impure->vlu_misc.vlu_sql_time_tz.utc_time, precision); + impure->vlu_desc.makeTimeTz(&impure->vlu_misc.vlu_sql_time_tz); + return &impure->vlu_desc; } @@ -4397,19 +4381,10 @@ dsc* CurrentTimeStampNode::execute(thread_db* tdbb, jrd_req* request) const request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - ISC_TIMESTAMP encTimes = request->req_gmt_timestamp.value(); + impure->vlu_misc.vlu_timestamp_tz = request->getTimeStampTz(); + TimeStamp::round_time(impure->vlu_misc.vlu_timestamp_tz.utc_timestamp.timestamp_time, precision); - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_timestamp_tz; - - TimeStamp::round_time(encTimes.timestamp_time, precision); - - impure->vlu_desc.dsc_dtype = dtype_timestamp_tz; - impure->vlu_desc.dsc_length = type_lengths[dtype_timestamp_tz]; - - impure->vlu_misc.vlu_timestamp_tz.utc_timestamp = encTimes; - impure->vlu_misc.vlu_timestamp_tz.time_zone = tdbb->getAttachment()->att_current_timezone; + impure->vlu_desc.makeTimestampTz(&impure->vlu_misc.vlu_timestamp_tz); return &impure->vlu_desc; } @@ -8164,21 +8139,10 @@ dsc* LocalTimeNode::execute(thread_db* tdbb, jrd_req* request) const request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = request->req_gmt_timestamp.value(); - timeStampTz.time_zone = TimeZoneUtil::GMT_ZONE; - - impure->vlu_misc.vlu_sql_time = TimeZoneUtil::timeStampTzToTimeStamp( - timeStampTz, request->req_attachment->att_current_timezone).timestamp_time; - + impure->vlu_misc.vlu_sql_time = request->getLocalTimeStamp().timestamp_time; TimeStamp::round_time(impure->vlu_misc.vlu_sql_time, precision); - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_dtype = dtype_sql_time; - impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time]; - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_time; + impure->vlu_desc.makeTime(&impure->vlu_misc.vlu_sql_time); return &impure->vlu_desc; } @@ -8267,15 +8231,10 @@ dsc* LocalTimeStampNode::execute(thread_db* tdbb, jrd_req* request) const request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - - impure->vlu_misc.vlu_timestamp = request->getLocalTimeStamp().value(); + impure->vlu_misc.vlu_timestamp = request->getLocalTimeStamp(); TimeStamp::round_time(impure->vlu_misc.vlu_timestamp.timestamp_time, precision); - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_timestamp; - impure->vlu_desc.dsc_dtype = dtype_timestamp; - impure->vlu_desc.dsc_length = type_lengths[dtype_timestamp]; + impure->vlu_desc.makeTimestamp(&impure->vlu_misc.vlu_timestamp); return &impure->vlu_desc; } @@ -13115,7 +13074,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const { Jrd::ContextPoolHolder context(tdbb, funcRequest->req_pool); // Save the old pool. - funcRequest->req_gmt_timestamp = request->req_gmt_timestamp; + funcRequest->setGmtTimeStamp(request->getGmtTimeStamp()); EXE_start(tdbb, funcRequest, transaction); @@ -13145,7 +13104,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const EXE_unwind(tdbb, funcRequest); funcRequest->req_attachment = NULL; funcRequest->req_flags &= ~(req_in_use | req_proc_fetch); - funcRequest->req_gmt_timestamp.invalidate(); + funcRequest->invalidateTimeStamp(); throw; } @@ -13173,7 +13132,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const funcRequest->req_attachment = NULL; funcRequest->req_flags &= ~(req_in_use | req_proc_fetch); - funcRequest->req_gmt_timestamp.invalidate(); + funcRequest->invalidateTimeStamp(); } if (!(request->req_flags & req_null)) diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index e755666a5d..df013bae04 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -3288,7 +3288,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons &tdbb->getAttachment()->att_original_timezone, tdbb->getAttachment()->att_current_timezone); - procRequest->req_gmt_timestamp = request->req_gmt_timestamp; + procRequest->setGmtTimeStamp(request->getGmtTimeStamp()); EXE_start(tdbb, procRequest, transaction); diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 931432af2c..100e3c5b25 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -577,9 +577,9 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& Jrd::ContextPoolHolder context(tdbb, expr_request->req_pool); if (org_request) - expr_request->req_gmt_timestamp = org_request->req_gmt_timestamp; + expr_request->setGmtTimeStamp(org_request->getGmtTimeStamp()); else - TimeZoneUtil::validateGmtTimeStamp(expr_request->req_gmt_timestamp); + expr_request->validateTimeStamp(); if (!(result = EVL_expr(tdbb, expr_request, idx->idx_expression))) result = &idx->idx_expression_desc; @@ -594,7 +594,7 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& expr_request->req_caller = NULL; expr_request->req_flags &= ~req_in_use; expr_request->req_attachment = NULL; - expr_request->req_gmt_timestamp.invalidate(); + expr_request->invalidateTimeStamp(); throw; } @@ -605,7 +605,7 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& expr_request->req_caller = NULL; expr_request->req_flags &= ~req_in_use; expr_request->req_attachment = NULL; - expr_request->req_gmt_timestamp.invalidate(); + expr_request->invalidateTimeStamp(); return result; } diff --git a/src/jrd/cvt.cpp b/src/jrd/cvt.cpp index 3578188ed3..efd887ecd9 100644 --- a/src/jrd/cvt.cpp +++ b/src/jrd/cvt.cpp @@ -580,10 +580,7 @@ SLONG EngineCallbacks::getLocalDate() thread_db* tdbb = JRD_get_thread_data(); if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getRequest()) - { - fb_assert(!tdbb->getRequest()->req_gmt_timestamp.isEmpty()); - return tdbb->getRequest()->getLocalTimeStamp().value().timestamp_date; - } + return tdbb->getRequest()->getLocalTimeStamp().timestamp_date; return TimeZoneUtil::timeStampTzToTimeStamp( TimeZoneUtil::getCurrentSystemTimeStamp(), getSessionTimeZone()).timestamp_date; @@ -595,10 +592,7 @@ ISC_TIMESTAMP EngineCallbacks::getCurrentGmtTimeStamp() thread_db* tdbb = JRD_get_thread_data(); if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getRequest()) - { - fb_assert(!tdbb->getRequest()->req_gmt_timestamp.isEmpty()); - return tdbb->getRequest()->req_gmt_timestamp.value(); - } + return tdbb->getRequest()->getGmtTimeStamp(); return TimeZoneUtil::timeStampTzToTimeStamp(TimeZoneUtil::getCurrentSystemTimeStamp(), TimeZoneUtil::GMT_ZONE); } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 5d104fc606..6e232a698d 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -5917,7 +5917,7 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ AutoSetRestore2 autoRequest(tdbb, &thread_db::getRequest, &thread_db::setRequest, defaultRequest); - TimeZoneUtil::validateGmtTimeStamp(defaultRequest->req_gmt_timestamp); + defaultRequest->validateTimeStamp(); TRA_attach_request(transaction, defaultRequest); dsc* result = EVL_expr(tdbb, defaultRequest, defaultNode); diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index ab0f4335d3..d82bf63607 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -907,7 +907,7 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) request->req_records_affected.clear(); // Store request start time for timestamp work - TimeZoneUtil::validateGmtTimeStamp(request->req_gmt_timestamp); + request->validateTimeStamp(); // Set all invariants to not computed. const ULONG* const* ptr, * const* end; @@ -1011,7 +1011,7 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) request->req_flags &= ~(req_active | req_proc_fetch | req_reserved); request->req_flags |= req_abort | req_stall; - request->req_gmt_timestamp.invalidate(); + request->invalidateTimeStamp(); request->req_caller = NULL; request->req_proc_inputs = NULL; request->req_proc_caller = NULL; @@ -1155,7 +1155,7 @@ void EXE_execute_triggers(thread_db* tdbb, TimeStamp timestamp; if (request) - timestamp = request->req_gmt_timestamp; + timestamp = request->getGmtTimeStamp(); else TimeZoneUtil::validateGmtTimeStamp(timestamp); @@ -1203,7 +1203,7 @@ void EXE_execute_triggers(thread_db* tdbb, } } - trigger->req_gmt_timestamp = timestamp; + trigger->setGmtTimeStamp(timestamp.value()); trigger->req_trigger_action = trigger_action; TraceTrigExecute trace(tdbb, trigger, which_trig); @@ -1433,7 +1433,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no TRA_release_request_snapshot(tdbb, request); request->req_flags &= ~(req_active | req_reserved); - request->req_gmt_timestamp.invalidate(); + request->invalidateTimeStamp(); release_blobs(tdbb, request); } diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index e80cd642f4..3398576332 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -108,7 +108,7 @@ void ProcedureScan::open(thread_db* tdbb) const try { - proc_request->req_gmt_timestamp = request->req_gmt_timestamp; + proc_request->setGmtTimeStamp(request->getGmtTimeStamp()); TraceProcExecute trace(tdbb, proc_request, request, m_targetList); diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index 93b3a687fa..8565a4638b 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -241,7 +241,7 @@ Applier* Applier::create(thread_db* tdbb) AutoPtr csb(FB_NEW_POOL(*req_pool) CompilerScratch(*req_pool)); const auto request = JrdStatement::makeRequest(tdbb, csb, true); - TimeZoneUtil::validateGmtTimeStamp(request->req_gmt_timestamp); + request->validateTimeStamp(); request->req_attachment = attachment; auto& att_pool = *attachment->att_pool; diff --git a/src/jrd/req.h b/src/jrd/req.h index e48137010d..d5b478d91c 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -234,6 +234,11 @@ public: private: JrdStatement* const statement; mutable StmtNumber req_id; // request identifier + Firebird::TimeStamp req_gmt_timestamp; // Start time of request in GMT time zone + + // These are valid only when !req_gmt_timestamp.isEmpty(), so no initialization is necessary. + mutable ISC_TIMESTAMP req_local_timestamp; // Timestamp in req_timezone's timezone + mutable ISC_USHORT req_timezone; // Timezone borrowed from the attachment public: MemoryPool* req_pool; @@ -268,7 +273,6 @@ public: ULONG req_flags; // misc request flags Savepoint* req_savepoints; // Looper savepoint list Savepoint* req_proc_sav_point; // procedure savepoint list - Firebird::TimeStamp req_gmt_timestamp; // Start time of request in GMT time zone unsigned int req_timeout; // query timeout in milliseconds, set by the dsql_req::setupTimer Firebird::RefPtr req_timer; // timeout timer, shared with dsql_req @@ -369,22 +373,59 @@ public: return tmp.m_transaction; } - Firebird::TimeStamp getLocalTimeStamp() const + void invalidateTimeStamp() { - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = req_gmt_timestamp.value(); - timeStampTz.time_zone = Firebird::TimeZoneUtil::GMT_ZONE; + req_gmt_timestamp.invalidate(); + } - return Firebird::TimeZoneUtil::timeStampTzToTimeStamp(timeStampTz, req_attachment->att_current_timezone); + ISC_TIMESTAMP getLocalTimeStamp() const + { + fb_assert(!req_gmt_timestamp.isEmpty()); + + if (req_timezone != req_attachment->att_current_timezone) + updateLocalTimeStamp(); + + return req_local_timestamp; + } + + ISC_TIMESTAMP getGmtTimeStamp() const + { + fb_assert(!req_gmt_timestamp.isEmpty()); + return req_gmt_timestamp.value(); + } + + void setGmtTimeStamp(ISC_TIMESTAMP ts) + { + req_gmt_timestamp = ts; + updateLocalTimeStamp(); } ISC_TIMESTAMP_TZ getTimeStampTz() const { + fb_assert(!req_gmt_timestamp.isEmpty()); + ISC_TIMESTAMP_TZ timeStampTz; timeStampTz.utc_timestamp = req_gmt_timestamp.value(); timeStampTz.time_zone = req_attachment->att_current_timezone; return timeStampTz; } + + void validateTimeStamp() + { + if (req_gmt_timestamp.isEmpty()) + { + Firebird::TimeZoneUtil::validateGmtTimeStamp(req_gmt_timestamp); + updateLocalTimeStamp(); + } + } + +private: + void updateLocalTimeStamp() const + { + req_local_timestamp = Firebird::TimeZoneUtil::timeStampTzToTimeStamp( + getTimeStampTz(), req_attachment->att_current_timezone); + req_timezone = req_attachment->att_current_timezone; + } }; // Flags for req_flags From 54924bc8ce4ba7f9f9a3dac67243d62d67d69b8d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 3 Jan 2022 22:00:19 -0300 Subject: [PATCH 017/187] Improvement #7092 - Improve performance of CURRENT_TIME. --- src/dsql/ExprNodes.cpp | 6 +----- src/jrd/req.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 8066bf30be..23bc600f0d 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -4272,11 +4272,7 @@ dsc* CurrentTimeNode::execute(thread_db* tdbb, jrd_req* request) const request->req_flags &= ~req_null; // Use the request timestamp. - ISC_TIMESTAMP_TZ currentTimeStamp = request->getTimeStampTz(); - - impure->vlu_misc.vlu_sql_time_tz.time_zone = tdbb->getAttachment()->att_current_timezone; - impure->vlu_misc.vlu_sql_time_tz.utc_time = TimeZoneUtil::timeStampTzToTimeTz(currentTimeStamp).utc_time; - + impure->vlu_misc.vlu_sql_time_tz = request->getTimeTz(); TimeStamp::round_time(impure->vlu_misc.vlu_sql_time_tz.utc_time, precision); impure->vlu_desc.makeTimeTz(&impure->vlu_misc.vlu_sql_time_tz); diff --git a/src/jrd/req.h b/src/jrd/req.h index d5b478d91c..86c7511369 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -239,6 +239,8 @@ private: // These are valid only when !req_gmt_timestamp.isEmpty(), so no initialization is necessary. mutable ISC_TIMESTAMP req_local_timestamp; // Timestamp in req_timezone's timezone mutable ISC_USHORT req_timezone; // Timezone borrowed from the attachment + mutable ISC_TIME req_local_time; // req_gmt_timestamp converted to local time (WITH TZ) + mutable bool req_local_time_valid; // req_local_time calculation is expensive. So is it valid (calculated)? public: MemoryPool* req_pool; @@ -410,6 +412,35 @@ public: return timeStampTz; } + ISC_TIME_TZ getTimeTz() const + { + fb_assert(!req_gmt_timestamp.isEmpty()); + + ISC_TIME_TZ timeTz; + + if (req_timezone != req_attachment->att_current_timezone) + updateLocalTimeStamp(); + + if (req_local_time_valid) + { + timeTz.utc_time = req_local_time; + timeTz.time_zone = req_timezone; + } + else + { + ISC_TIMESTAMP_TZ timeStamp; + timeStamp.utc_timestamp = req_gmt_timestamp.value(); + timeStamp.time_zone = req_timezone; + + timeTz = Firebird::TimeZoneUtil::timeStampTzToTimeTz(timeStamp); + + req_local_time = timeTz.utc_time; + req_local_time_valid = true; + } + + return timeTz; + } + void validateTimeStamp() { if (req_gmt_timestamp.isEmpty()) @@ -425,6 +456,7 @@ private: req_local_timestamp = Firebird::TimeZoneUtil::timeStampTzToTimeStamp( getTimeStampTz(), req_attachment->att_current_timezone); req_timezone = req_attachment->att_current_timezone; + req_local_time_valid = false; } }; From c07ab9b559b31c8f70d0500828200fb83fd7a64e Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 3 Jan 2022 22:46:01 -0300 Subject: [PATCH 018/187] Refactor request's timestamp handling. --- src/jrd/req.h | 169 +++++++++++++++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 63 deletions(-) diff --git a/src/jrd/req.h b/src/jrd/req.h index 86c7511369..55b8057fb5 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -167,6 +167,104 @@ private: class jrd_req : public pool_alloc { +private: + class TimeStampCache + { + public: + void invalidate() + { + gmtTimeStamp.invalidate(); + } + + ISC_TIMESTAMP getLocalTimeStamp(USHORT currentTimeZone) const + { + fb_assert(!gmtTimeStamp.isEmpty()); + + if (timeZone != currentTimeZone) + update(currentTimeZone); + + return localTimeStamp; + } + + ISC_TIMESTAMP getGmtTimeStamp() const + { + fb_assert(!gmtTimeStamp.isEmpty()); + return gmtTimeStamp.value(); + } + + void setGmtTimeStamp(USHORT currentTimeZone, ISC_TIMESTAMP ts) + { + gmtTimeStamp = ts; + update(currentTimeZone); + } + + ISC_TIMESTAMP_TZ getTimeStampTz(USHORT currentTimeZone) const + { + fb_assert(!gmtTimeStamp.isEmpty()); + + ISC_TIMESTAMP_TZ timeStampTz; + timeStampTz.utc_timestamp = gmtTimeStamp.value(); + timeStampTz.time_zone = currentTimeZone; + return timeStampTz; + } + + ISC_TIME_TZ getTimeTz(USHORT currentTimeZone) const + { + fb_assert(!gmtTimeStamp.isEmpty()); + + ISC_TIME_TZ timeTz; + + if (timeZone != currentTimeZone) + update(currentTimeZone); + + if (localTimeValid) + { + timeTz.utc_time = localTime; + timeTz.time_zone = timeZone; + } + else + { + ISC_TIMESTAMP_TZ timeStamp; + timeStamp.utc_timestamp = gmtTimeStamp.value(); + timeStamp.time_zone = timeZone; + + timeTz = Firebird::TimeZoneUtil::timeStampTzToTimeTz(timeStamp); + + localTime = timeTz.utc_time; + localTimeValid = true; + } + + return timeTz; + } + + void validate(USHORT currentTimeZone) + { + if (gmtTimeStamp.isEmpty()) + { + Firebird::TimeZoneUtil::validateGmtTimeStamp(gmtTimeStamp); + update(currentTimeZone); + } + } + + private: + void update(USHORT currentTimeZone) const + { + localTimeStamp = Firebird::TimeZoneUtil::timeStampTzToTimeStamp( + getTimeStampTz(currentTimeZone), currentTimeZone); + timeZone = currentTimeZone; + localTimeValid = false; + } + + private: + Firebird::TimeStamp gmtTimeStamp; // Start time of request in GMT time zone + + // These are valid only when !gmtTimeStamp.isEmpty(), so no initialization is necessary. + mutable ISC_TIMESTAMP localTimeStamp; // Timestamp in timeZone's zone + mutable ISC_USHORT timeZone; // Timezone borrowed from the attachment when updated + mutable ISC_TIME localTime; // gmtTimeStamp converted to local time (WITH TZ) + mutable bool localTimeValid; // localTime calculation is expensive. So is it valid (calculated)? + }; + public: jrd_req(Attachment* attachment, /*const*/ JrdStatement* aStatement, Firebird::MemoryStats* parent_stats) @@ -234,13 +332,7 @@ public: private: JrdStatement* const statement; mutable StmtNumber req_id; // request identifier - Firebird::TimeStamp req_gmt_timestamp; // Start time of request in GMT time zone - - // These are valid only when !req_gmt_timestamp.isEmpty(), so no initialization is necessary. - mutable ISC_TIMESTAMP req_local_timestamp; // Timestamp in req_timezone's timezone - mutable ISC_USHORT req_timezone; // Timezone borrowed from the attachment - mutable ISC_TIME req_local_time; // req_gmt_timestamp converted to local time (WITH TZ) - mutable bool req_local_time_valid; // req_local_time calculation is expensive. So is it valid (calculated)? + TimeStampCache req_timeStampCache; // time stamp cache public: MemoryPool* req_pool; @@ -377,86 +469,37 @@ public: void invalidateTimeStamp() { - req_gmt_timestamp.invalidate(); + req_timeStampCache.invalidate(); } ISC_TIMESTAMP getLocalTimeStamp() const { - fb_assert(!req_gmt_timestamp.isEmpty()); - - if (req_timezone != req_attachment->att_current_timezone) - updateLocalTimeStamp(); - - return req_local_timestamp; + return req_timeStampCache.getLocalTimeStamp(req_attachment->att_current_timezone); } ISC_TIMESTAMP getGmtTimeStamp() const { - fb_assert(!req_gmt_timestamp.isEmpty()); - return req_gmt_timestamp.value(); + return req_timeStampCache.getGmtTimeStamp(); } void setGmtTimeStamp(ISC_TIMESTAMP ts) { - req_gmt_timestamp = ts; - updateLocalTimeStamp(); + req_timeStampCache.setGmtTimeStamp(req_attachment->att_current_timezone, ts); } ISC_TIMESTAMP_TZ getTimeStampTz() const { - fb_assert(!req_gmt_timestamp.isEmpty()); - - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = req_gmt_timestamp.value(); - timeStampTz.time_zone = req_attachment->att_current_timezone; - return timeStampTz; + return req_timeStampCache.getTimeStampTz(req_attachment->att_current_timezone); } ISC_TIME_TZ getTimeTz() const { - fb_assert(!req_gmt_timestamp.isEmpty()); - - ISC_TIME_TZ timeTz; - - if (req_timezone != req_attachment->att_current_timezone) - updateLocalTimeStamp(); - - if (req_local_time_valid) - { - timeTz.utc_time = req_local_time; - timeTz.time_zone = req_timezone; - } - else - { - ISC_TIMESTAMP_TZ timeStamp; - timeStamp.utc_timestamp = req_gmt_timestamp.value(); - timeStamp.time_zone = req_timezone; - - timeTz = Firebird::TimeZoneUtil::timeStampTzToTimeTz(timeStamp); - - req_local_time = timeTz.utc_time; - req_local_time_valid = true; - } - - return timeTz; + return req_timeStampCache.getTimeTz(req_attachment->att_current_timezone); } void validateTimeStamp() { - if (req_gmt_timestamp.isEmpty()) - { - Firebird::TimeZoneUtil::validateGmtTimeStamp(req_gmt_timestamp); - updateLocalTimeStamp(); - } - } - -private: - void updateLocalTimeStamp() const - { - req_local_timestamp = Firebird::TimeZoneUtil::timeStampTzToTimeStamp( - getTimeStampTz(), req_attachment->att_current_timezone); - req_timezone = req_attachment->att_current_timezone; - req_local_time_valid = false; + req_timeStampCache.validate(req_attachment->att_current_timezone); } }; From e596e03ee08ec01227787b1a93892a55cf136a8e Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 5 Jan 2022 00:06:17 +0000 Subject: [PATCH 019/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index cad3801015..1385e0fa58 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:362 + FORMAL BUILD NUMBER:365 */ -#define PRODUCT_VER_STRING "5.0.0.362" -#define FILE_VER_STRING "WI-T5.0.0.362" -#define LICENSE_VER_STRING "WI-T5.0.0.362" -#define FILE_VER_NUMBER 5, 0, 0, 362 +#define PRODUCT_VER_STRING "5.0.0.365" +#define FILE_VER_STRING "WI-T5.0.0.365" +#define LICENSE_VER_STRING "WI-T5.0.0.365" +#define FILE_VER_NUMBER 5, 0, 0, 365 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "362" +#define FB_BUILD_NO "365" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 6a84024d8c..3dd44d693b 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=362 +BuildNum=365 NowAt=`pwd` cd `dirname $0` From 82da31ccfd9a3f3daa692aa9ed70588854d15bcd Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 5 Jan 2022 10:34:07 +0300 Subject: [PATCH 020/187] Remove the WNET protocol (#7082) * Wiped out the WNET support * Remove the WNET files --- CMakeLists.txt | 1 - builds/install/misc/firebird.conf | 15 +- builds/mac_os_x/CS/CS.pbproj/project.pbxproj | 10 - builds/win32/msvc15/remote.vcxproj | 2 - builds/win32/msvc15/remote.vcxproj.filters | 6 - configure.ac | 13 - doc/Firebird_conf.txt | 6 +- doc/README.connection_strings | 26 +- doc/sql.extensions/README.context_variables2 | 2 +- src/alice/alice.h | 29 +- src/alice/alice_meta.epp | 91 +- src/alice/tdr.cpp | 97 +- src/common/config/config.h | 5 - src/common/isc_f_proto.h | 6 +- src/common/isc_file.cpp | 130 +- src/include/cross/android.arm64 | 3 - src/include/cross/android.arme | 3 - src/include/cross/android.x86 | 3 - src/include/cross/android.x86_64 | 3 - src/include/firebird/impl/consts_pub.h | 3 +- src/include/gen/autoconfig.h.in | 3 - src/include/gen/autoconfig_msvc.h | 3 - src/remote/CMakeLists.txt | 2 - src/remote/client/interface.cpp | 60 +- src/remote/inet.cpp | 2 +- src/remote/os/win32/wnet.cpp | 1476 ------------------ src/remote/os/win32/wnet_proto.h | 35 - src/remote/remote.h | 1 - src/remote/remote_def.h | 11 +- src/remote/server/os/win32/property.cpp | 10 +- src/remote/server/os/win32/property.rc | 1 - src/remote/server/os/win32/property.rh | 1 - src/remote/server/os/win32/srvr_w32.cpp | 101 +- src/remote/server/server.cpp | 6 +- src/utilities/gsec/gsec.cpp | 14 +- src/utilities/gstat/dba.epp | 13 +- 36 files changed, 146 insertions(+), 2047 deletions(-) delete mode 100644 src/remote/os/win32/wnet.cpp delete mode 100644 src/remote/os/win32/wnet_proto.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f336efd61..6bb9033ea0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,6 @@ endif() set(FB_PREFIX ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}) set(FB_IPC_NAME "FirebirdIPI") set(FB_LOGFILENAME "firebird.log") -set(FB_PIPE_NAME "interbas") set(FB_SERVICE_NAME "gds_db") set(FB_SERVICE_PORT 3050) diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index 61a503f578..54711f45cf 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -41,9 +41,8 @@ # # String # ------ -# Strings are also what they sound like, strings. Examples: +# Strings are also what they sound like, strings. Example: # RemoteServiceName = gds_db -# RemotePipeName = pipe47 # # Scopes # ------ @@ -1083,18 +1082,6 @@ # #IpcName = FIREBIRD -# -# The name of the pipe used as a transport channel in NetBEUI protocol. -# Has the same meaning as a port number for TCP/IP. The default value is -# compatible with IB/FB1. -# -# Per-connection configurable. -# -# Type: string -# -#RemotePipeName = interbas - - # ============================ # Settings for Unix/Linux platforms # ============================ diff --git a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj index c75d4835fa..628a47746d 100644 --- a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj +++ b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj @@ -6767,16 +6767,6 @@ path = winvx.cpp; refType = 4; }; - F616C8C00200B0D001EF0ADE = { - isa = PBXFileReference; - path = wnet.cpp; - refType = 4; - }; - F616C8C10200B0D001EF0ADE = { - isa = PBXFileReference; - path = wnet_proto.h; - refType = 4; - }; F616C8C20200B0D001EF0ADE = { isa = PBXFileReference; path = xdr.cpp; diff --git a/builds/win32/msvc15/remote.vcxproj b/builds/win32/msvc15/remote.vcxproj index 41d0aa0e9e..691b114bdd 100644 --- a/builds/win32/msvc15/remote.vcxproj +++ b/builds/win32/msvc15/remote.vcxproj @@ -157,7 +157,6 @@ - @@ -165,7 +164,6 @@ - diff --git a/builds/win32/msvc15/remote.vcxproj.filters b/builds/win32/msvc15/remote.vcxproj.filters index 391b076fc8..5ebb99e644 100644 --- a/builds/win32/msvc15/remote.vcxproj.filters +++ b/builds/win32/msvc15/remote.vcxproj.filters @@ -29,9 +29,6 @@ REMOTE files - - REMOTE files - AUTH files @@ -64,9 +61,6 @@ Header files - - Header files - Header files diff --git a/configure.ac b/configure.ac index 0dcb0ca19a..e845e3cf89 100644 --- a/configure.ac +++ b/configure.ac @@ -1222,15 +1222,6 @@ case "$PLATFORM" in ;; win32) - FB_PIPE_NAME=interbas - AC_ARG_WITH(pipe-name, - [ --with-pipe-name specify wnet pipe name (default=interbas)], - [FB_PIPE_NAME=${withval}]) - AH_VERBATIM(FB_PIPE_NAME, -[/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas"]) - AC_DEFINE_UNQUOTED(FB_PIPE_NAME,"$FB_PIPE_NAME") - AC_SUBST(FB_PIPE_NAME) XE_PREPEND( -mthreads -lmpr -lversion -lws2_32 -lole32,LIBS) ;; @@ -1503,10 +1494,6 @@ esac echo " Service name : $FB_SERVICE_NAME" echo " Service port : $FB_SERVICE_PORT" -case "$PLATFORM" in - win32) echo " Pipe name : $FB_PIPE_NAME";; -esac - echo " GPRE modules : c_cxx.cpp$GPRE_LANGUAGE_MODULES" echo diff --git a/doc/Firebird_conf.txt b/doc/Firebird_conf.txt index 99a485e21f..06871c017c 100644 --- a/doc/Firebird_conf.txt +++ b/doc/Firebird_conf.txt @@ -94,9 +94,8 @@ you only use 0/1 String ------ -Strings are also what they sound like, strings. Examples: +Strings are also what they sound like, strings. Example: RootDirectory = /opt/firebird -RemotePipeName = "pipe47" Configuration options @@ -144,8 +143,7 @@ DeadThreadsCollection integer default 50 PriorityBoost integer default 5 RemoteServiceName string default gds_db RemoteServicePort integer default 3050 (TCP port number) -RemotePipeName string default "interbas" (Windows only?) -IpcName string default "FirebirdIPI" (Windows only) +IpcName string default "FIREBIRD" (Windows only) MaxUnflushedWrites integer # of writes before file writes are forcibly synched. diff --git a/doc/README.connection_strings b/doc/README.connection_strings index a43908808d..39fe8b538a 100644 --- a/doc/README.connection_strings +++ b/doc/README.connection_strings @@ -10,10 +10,6 @@ For TCP (aka INET) protocol: [ / ] : -For named pipes (aka NetBEUI, aka WNET) protocol: - - \\ [ @ ] \ - For local connections as simple as: @@ -55,11 +51,6 @@ Examples: myserver/fb_db:mydb localhost/fb_db:mydb - Connect via named pipes: - - \\myserver\C:\db\mydb.fdb - \\myserver@fb_db\C:\db\mydb.fdb - Local connection: /db/mydb.fdb @@ -71,8 +62,7 @@ connection strings: [ : // [ [ : ] ] ] / -Where protocol is one of: INET (means TCP), WNET (means named pipes) or XNET -(means shared memory). +Where protocol is one of: INET (means TCP) or XNET (means shared memory). Examples: @@ -114,22 +104,12 @@ Examples: inet4://myserver/mydb inet6://myserver/mydb - Connect via named pipes: - - wnet://myserver/C:\db\mydb.fdb - wnet://myserver:fb_db/C:\db\mydb.fdb - Loopback connection via TCP: inet:///db/mydb.fdb inet://C:\db\mydb.fdb inet://mydb - Loopback connection via named pipes: - - wnet://C:\db\mydb.fdb - wnet://mydb - Local connection via shared memory: xnet://C:\db\mydb.fdb @@ -155,9 +135,7 @@ to connect locally using a specific transport protocol, please specify: inet:// or - wnet:// - or xnet:// -Note: WNET (named pipes) and XNET (shared memory) protocols are available on Windows only. +Note: XNET (shared memory) protocol is available on Windows only. diff --git a/doc/sql.extensions/README.context_variables2 b/doc/sql.extensions/README.context_variables2 index 5ef0fff719..88dff5daf1 100644 --- a/doc/sql.extensions/README.context_variables2 +++ b/doc/sql.extensions/README.context_variables2 @@ -48,7 +48,7 @@ Usage: Variable name Value ------------------------------------------------------------------------------ NETWORK_PROTOCOL | The network protocol used by client to connect. Currently - | used values: "TCPv4", "TCPv6", "WNET", "XNET" and NULL. + | used values: "TCPv4", "TCPv6", "XNET" and NULL. | WIRE_COMPRESSED | Compression status of current connection. | If connection is compressed - returns "TRUE", if it is diff --git a/src/alice/alice.h b/src/alice/alice.h index 0648f297ac..b6b87033a2 100644 --- a/src/alice/alice.h +++ b/src/alice/alice.h @@ -98,30 +98,21 @@ struct user_action -// String block: used to store a string of constant length. - -class alice_str : public pool_alloc_rpt -{ -public: - USHORT str_length; - UCHAR str_data[2]; -}; - // Transaction block: used to store info about a multi-database transaction. // Transaction Description Record struct tdr : public pool_alloc { - tdr* tdr_next; // next sub-transaction - TraNumber tdr_id; // database-specific transaction id - alice_str* tdr_fullpath; // full (possibly) remote pathname - const TEXT* tdr_filename; // filename within full pathname - alice_str* tdr_host_site; // host for transaction - alice_str* tdr_remote_site; // site for remote transaction - FB_API_HANDLE tdr_handle; // reconnected transaction handle - FB_API_HANDLE tdr_db_handle; // re-attached database handle - USHORT tdr_db_caps; // capabilities of database - USHORT tdr_state; // see flags below + tdr* tdr_next; // next sub-transaction + TraNumber tdr_id; // database-specific transaction id + Firebird::string tdr_fullpath; // full (possibly) remote pathname + Firebird::string tdr_filename; // filename + Firebird::string tdr_host_site; // host for transaction + Firebird::string tdr_remote_site; // site for remote transaction + FB_API_HANDLE tdr_handle; // reconnected transaction handle + FB_API_HANDLE tdr_db_handle; // re-attached database handle + USHORT tdr_db_caps; // capabilities of database + USHORT tdr_state; // see flags below }; // CVC: This information should match Transaction Description Record constants in acl.h diff --git a/src/alice/alice_meta.epp b/src/alice/alice_meta.epp index fc99727e19..651602fcc6 100644 --- a/src/alice/alice_meta.epp +++ b/src/alice/alice_meta.epp @@ -39,6 +39,7 @@ #include "../common/classes/UserBlob.h" #include "../alice/alice_proto.h" #include "../common/utils_proto.h" +#include "../common/isc_f_proto.h" #include @@ -54,7 +55,7 @@ DATABASE DB = STATIC FILENAME "yachts.lnk"; typedef Firebird::HalfStaticArray TextBuffer; -static alice_str* alloc_string(const TEXT**); +static void get_string(const TEXT**, Firebird::string&); static USHORT get_capabilities(ISC_STATUS*); static tdr* get_description(ISC_QUAD*); static void parse_fullpath(tdr*); @@ -242,19 +243,15 @@ void MET_set_capabilities(ISC_STATUS* user_status, tdr* trans) * Eat a string with a byte-encoded length. */ -static alice_str* alloc_string(const TEXT** ptr) +static void get_string(const TEXT** ptr, Firebird::string& str) { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); const TEXT* p = *ptr; - const USHORT length = (USHORT) *p++; - alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str; - memcpy(string->str_data, p, length); - string->str_data[length] = 0; *ptr = p + length; - return string; + str.assign(p, length); } @@ -319,8 +316,8 @@ static tdr* get_description(ISC_QUAD* blob_id) return NULL; tdr* trans = NULL; - alice_str* host_site = NULL; - alice_str* database_path = NULL; + Firebird::string host_site; + Firebird::string database_path; const TEXT* p = buffer.begin(); @@ -336,11 +333,11 @@ static tdr* get_description(ISC_QUAD* blob_id) switch (*p++) { case TDR_HOST_SITE: - host_site = alloc_string(&p); + get_string(&p, host_site); break; case TDR_DATABASE_PATH: - database_path = alloc_string(&p); + get_string(&p, database_path); break; case TDR_TRANSACTION_ID: @@ -359,7 +356,7 @@ static tdr* get_description(ISC_QUAD* blob_id) ptr->tdr_fullpath = database_path; parse_fullpath(ptr); ptr->tdr_id = id; - database_path = NULL; + database_path.clear(); break; default: @@ -382,73 +379,17 @@ static tdr* get_description(ISC_QUAD* blob_id) static void parse_fullpath(tdr* trans) { - AliceGlobals* tdgbl = AliceGlobals::getSpecific(); + Firebird::PathName filename = trans->tdr_fullpath.c_str(); + Firebird::PathName hostname; - // start at the end of the full pathname + // Find the last remote node in the path - const TEXT* p = (TEXT*) trans->tdr_fullpath->str_data; - const TEXT* const start = p; - while (*p) - p++; - const TEXT* const end = p; + while (ISC_analyze_tcp(filename, hostname)) + trans->tdr_remote_site = hostname.c_str(); - // Check for a named pipes name - \\node\path\db or //node/path/db - while (p > start && !(*p == '/' && p[-1] == '/') && !(*p == '\\' && p[-1] == '\\')) - { - --p; - } + // At this point the filename is clear from any remote nodes - if (p > start) - { - // Increment p past slash, & search forward for end of node name - p = p + 1; - const TEXT* q = p; - - while (*q && *q != '/' && *q != '\\') - q++; - if (*q) - { - trans->tdr_filename = q + 1; - - trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->getDefaultPool(), q - p + 1) alice_str; - fb_utils::copy_terminate((char*) trans->tdr_remote_site->str_data, (char*) p, q - p + 1); - } - } - else - { - p = end; - - // If not named pipes, check the other protocols - // work backwards until we find a remote protocol specifier - - - while (p >= start && (*p != '^' && *p != ':' && *p != '@')) - p--; - // dimitr: make sure that the remote path is parsed correctly - // for win32 servers, i.e. the drive separator is taken into account - if ((p - 2 >= start) && p[-2] == ':' && (p[0] == ':')) - p -= 2; - trans->tdr_filename = p + 1; - - // now find the last remote node in the chain - - while (p > start && (*p == ':' || *p == '^' || *p == '@')) - p--; - - USHORT length = 0; - for (; p >= start && (*p != '^' && *p != ':' && *p != '@'); ++length) - --p; - ++p; - - if (length) - { - trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str; - TEXT* q = (TEXT *) trans->tdr_remote_site->str_data; - while (length--) - *q++ = *p++; - *q = 0; - } - } + trans->tdr_filename = filename.c_str(); } diff --git a/src/alice/tdr.cpp b/src/alice/tdr.cpp index 29762496d6..f7b1e89b0b 100644 --- a/src/alice/tdr.cpp +++ b/src/alice/tdr.cpp @@ -472,7 +472,7 @@ bool TDR_reconnect_multiple(FB_API_HANDLE handle, TraNumber id, const TEXT* name { if (ptr->tdr_state == TRA_limbo) { - reconnect(ptr->tdr_db_handle, ptr->tdr_id, ptr->tdr_filename, switches); + reconnect(ptr->tdr_db_handle, ptr->tdr_id, ptr->tdr_filename.c_str(), switches); } } } @@ -504,28 +504,23 @@ static void print_description(const tdr* trans) AliceGlobals* tdgbl = AliceGlobals::getSpecific(); if (!trans) - { return; - } if (!tdgbl->uSvc->isService()) - { ALICE_print(92); // msg 92: Multidatabase transaction: - } bool prepared_seen = false; for (const tdr* ptr = trans; ptr; ptr = ptr->tdr_next) { - if (ptr->tdr_host_site) + const auto host_site = ptr->tdr_host_site.nullStr(); + if (host_site) { - const char* pszHostSize = reinterpret_cast(ptr->tdr_host_site->str_data); - if (!tdgbl->uSvc->isService()) { // msg 93: Host Site: %s - ALICE_print(93, SafeArg() << pszHostSize); + ALICE_print(93, SafeArg() << host_site); } - tdgbl->uSvc->putLine(isc_spb_tra_host_site, pszHostSize); + tdgbl->uSvc->putLine(isc_spb_tra_host_site, host_site); } if (ptr->tdr_id) @@ -586,28 +581,26 @@ static void print_description(const tdr* trans) break; } - if (ptr->tdr_remote_site) + const auto remote_site = ptr->tdr_remote_site.nullStr(); + if (remote_site) { - const char* pszRemoteSite = reinterpret_cast(ptr->tdr_remote_site->str_data); - if (!tdgbl->uSvc->isService()) { // msg 101: Remote Site: %s - ALICE_print(101, SafeArg() << pszRemoteSite); + ALICE_print(101, SafeArg() << remote_site); } - tdgbl->uSvc->putLine(isc_spb_tra_remote_site, pszRemoteSite); + tdgbl->uSvc->putLine(isc_spb_tra_remote_site, remote_site); } - if (ptr->tdr_fullpath) + const auto fullpath = ptr->tdr_fullpath.nullStr(); + if (fullpath) { - const char* pszFullpath = reinterpret_cast(ptr->tdr_fullpath->str_data); - if (!tdgbl->uSvc->isService()) { // msg 102: Database Path: %s - ALICE_print(102, SafeArg() << pszFullpath); + ALICE_print(102, SafeArg() << fullpath); } - tdgbl->uSvc->putLine(isc_spb_tra_db_path, pszFullpath); + tdgbl->uSvc->putLine(isc_spb_tra_db_path, fullpath); } } @@ -649,10 +642,9 @@ static void print_description(const tdr* trans) static SINT64 ask() { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); + if (tdgbl->uSvc->isService()) - { return ~SINT64(0); - } char response[32]; SINT64 switches = 0; @@ -694,79 +686,56 @@ static SINT64 ask() static void reattach_database(tdr* trans) { ISC_STATUS_ARRAY status_vector; - char buffer[1024]; + char buffer[BUFFER_LARGE]; // sizeof(buffer) - 1 => leave space for the terminator. const char* const end = buffer + sizeof(buffer) - 1; AliceGlobals* tdgbl = AliceGlobals::getSpecific(); - ISC_get_host(buffer, sizeof(buffer)); - - if (trans->tdr_fullpath) + if (trans->tdr_fullpath.hasData()) { + Firebird::string hostname; + ISC_get_host(hostname); + // if this is being run from the same host, // try to reconnect using the same pathname - if (!strcmp(buffer, reinterpret_cast(trans->tdr_host_site->str_data))) + if (trans->tdr_host_site == hostname) { - if (TDR_attach_database(status_vector, trans, - reinterpret_cast(trans->tdr_fullpath->str_data))) - { + if (TDR_attach_database(status_vector, trans, trans->tdr_fullpath.c_str())) return; - } } - else if (trans->tdr_host_site) + else if (trans->tdr_host_site.hasData()) { // try going through the previous host with all available // protocols, using chaining to try the same method of // attachment originally used from that host - char* p = buffer; - const UCHAR* q = trans->tdr_host_site->str_data; - while (*q && p < end) - *p++ = *q++; - *p++ = ':'; - q = trans->tdr_fullpath->str_data; - while (*q && p < end) - *p++ = *q++; - *p = 0; - if (TDR_attach_database(status_vector, trans, buffer)) - { + const Firebird::string pathname = trans->tdr_host_site + ':' + trans->tdr_fullpath; + if (TDR_attach_database(status_vector, trans, pathname.c_str())) return; - } } // attaching using the old method didn't work; // try attaching to the remote node directly - if (trans->tdr_remote_site) + if (trans->tdr_remote_site.hasData()) { - char* p = buffer; - const UCHAR* q = trans->tdr_remote_site->str_data; - while (*q && p < end) - *p++ = *q++; - *p++ = ':'; - q = reinterpret_cast(trans->tdr_filename); - while (*q && p < end) - *p++ = *q++; - *p = 0; - if (TDR_attach_database (status_vector, trans, buffer)) - { + const Firebird::string pathname = trans->tdr_remote_site + ':' + trans->tdr_filename; + if (TDR_attach_database(status_vector, trans, pathname.c_str())) return; - } } } + // we have failed to reattach; notify the user // and let them try to succeed where we have failed ALICE_print(86, SafeArg() << trans->tdr_id); // msg 86: Could not reattach to database for transaction %ld. - ALICE_print(87, SafeArg() << (trans->tdr_fullpath ? (char*)(trans->tdr_fullpath->str_data) : "is unknown")); + ALICE_print(87, SafeArg() << (trans->tdr_fullpath.hasData() ? trans->tdr_fullpath.c_str() : "unknown")); // msg 87: Original path: %s if (tdgbl->uSvc->isService()) - { ALICE_exit(FINI_ERROR, tdgbl); - } for (;;) { @@ -782,12 +751,8 @@ static void reattach_database(tdr* trans) ++p; if (TDR_attach_database(status_vector, trans, p)) { - const size_t p_len = strlen(p); - alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), p_len + 1) alice_str; - strcpy(reinterpret_cast(string->str_data), p); - string->str_length = static_cast(p_len); - trans->tdr_fullpath = string; - trans->tdr_filename = (TEXT *) string->str_data; + trans->tdr_fullpath.assign(p); + trans->tdr_filename = trans->tdr_fullpath; return; } ALICE_print(89); // msg 89: Attach unsuccessful. diff --git a/src/common/config/config.h b/src/common/config/config.h index 2550cb9b55..d8bcdd7577 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -137,7 +137,6 @@ enum ConfigKey KEY_DEADLOCK_TIMEOUT, KEY_REMOTE_SERVICE_NAME, KEY_REMOTE_SERVICE_PORT, - KEY_REMOTE_PIPE_NAME, KEY_IPC_NAME, KEY_MAX_UNFLUSHED_WRITES, KEY_MAX_UNFLUSHED_WRITE_TIME, @@ -232,7 +231,6 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_INTEGER, "DeadlockTimeout", false, 10}, // seconds {TYPE_STRING, "RemoteServiceName", false, FB_SERVICE_NAME}, {TYPE_INTEGER, "RemoteServicePort", false, 0}, - {TYPE_STRING, "RemotePipeName", false, FB_PIPE_NAME}, {TYPE_STRING, "IpcName", false, FB_IPC_NAME}, #ifdef WIN_NT {TYPE_INTEGER, "MaxUnflushedWrites", false, 100}, @@ -522,9 +520,6 @@ public: // Service port for INET CONFIG_GET_PER_DB_KEY(unsigned short, getRemoteServicePort, KEY_REMOTE_SERVICE_PORT, getInt); - // Pipe name for WNET - CONFIG_GET_PER_DB_STR(getRemotePipeName, KEY_REMOTE_PIPE_NAME); - // Name for IPC-related objects CONFIG_GET_PER_DB_STR(getIpcName, KEY_IPC_NAME); diff --git a/src/common/isc_f_proto.h b/src/common/isc_f_proto.h index 021092be4d..718f8f8208 100644 --- a/src/common/isc_f_proto.h +++ b/src/common/isc_f_proto.h @@ -30,13 +30,15 @@ #include "../common/classes/fb_string.h" #include "../common/common.h" -enum iscProtocol {ISC_PROTOCOL_LOCAL, ISC_PROTOCOL_TCPIP, ISC_PROTOCOL_WLAN}; +enum iscProtocol {ISC_PROTOCOL_LOCAL, ISC_PROTOCOL_TCPIP}; #ifndef NO_NFS bool ISC_analyze_nfs(Firebird::PathName&, Firebird::PathName&); #endif -bool ISC_analyze_protocol(const char*, Firebird::PathName&, Firebird::PathName&, const char*, bool needFile); +#ifdef WIN_NT bool ISC_analyze_pclan(Firebird::PathName&, Firebird::PathName&); +#endif +bool ISC_analyze_protocol(const char*, Firebird::PathName&, Firebird::PathName&, const char*, bool needFile); bool ISC_analyze_tcp(Firebird::PathName&, Firebird::PathName&, bool = true); bool ISC_check_if_remote(const Firebird::PathName&, bool); iscProtocol ISC_extract_host(Firebird::PathName&, Firebird::PathName&, bool); diff --git a/src/common/isc_file.cpp b/src/common/isc_file.cpp index fae006bcc6..10222ac6b6 100644 --- a/src/common/isc_file.cpp +++ b/src/common/isc_file.cpp @@ -346,6 +346,47 @@ bool ISC_analyze_nfs(tstring& expanded_filename, tstring& node_name) #endif +#if defined(WIN_NT) +bool ISC_analyze_pclan(tstring& expanded_name, tstring& node_name) +{ +/************************************** + * + * I S C _ a n a l y z e _ p c l a n + * + ************************************** + * + * Functional description + * Check a file name for a SMB mount point. If so, + * decompose into node name and remote file name. + * + **************************************/ + ISC_expand_share(expanded_name); + + if (expanded_name.length() < 2 || + (expanded_name[0] != '\\' && expanded_name[0] != '/') || + (expanded_name[1] != '\\' && expanded_name[1] != '/')) + { + return false; + } + + const size p = expanded_name.find_first_of("\\/", 2); + if (p == npos) + return false; + + if (Config::getRemoteFileOpenAbility()) + { + if (expanded_name.find(':', p + 1) == npos) + return false; + } + + node_name = expanded_name.substr(2, p - 2); + expanded_name.erase(0, p + 1); + + return true; +} +#endif + + bool ISC_analyze_protocol(const char* protocol, tstring& expanded_name, tstring& node_name, const char* separator, bool need_file) { @@ -403,57 +444,6 @@ bool ISC_analyze_protocol(const char* protocol, tstring& expanded_name, tstring& } -#if defined(WIN_NT) -bool ISC_analyze_pclan(tstring& expanded_name, tstring& node_name) -{ -/************************************** - * - * I S C _ a n a l y z e _ p c l a n - * - ************************************** - * - * Functional description - * Analyze a filename for a named pipe node name on the front. - * If one is found, extract the node name, compute the residual - * file name, and return true. Otherwise return false. - * - **************************************/ - node_name.erase(); - if (expanded_name.length() < 2 || - (expanded_name[0] != '\\' && expanded_name[0] != '/') || - (expanded_name[1] != '\\' && expanded_name[1] != '/')) - { - return false; - } - - const size p = expanded_name.find_first_of("\\/", 2); - if (p == npos) - return false; - - if (Config::getRemoteFileOpenAbility()) - { - if (expanded_name.find(':', p + 1) == npos) - return false; - } - - node_name = "\\\\"; - node_name += expanded_name.substr(2, p - 2); - - // If this is a loopback, substitute "." for the host name. Otherwise, - // the CreateFile on the pipe will fail. - TEXT localhost[MAXHOSTLEN]; - ISC_get_host(localhost, sizeof(localhost)); - if (node_name.substr(2, npos) == localhost) - { - node_name.replace(2, npos, "."); - } - - expanded_name.erase(0, p + 1); - return true; -} -#endif // WIN_NT - - bool ISC_analyze_tcp(tstring& file_name, tstring& node_name, bool need_file) { /************************************** @@ -564,46 +554,22 @@ iscProtocol ISC_extract_host(Firebird::PathName& file_name, // Always check for an explicit TCP node name if (ISC_analyze_tcp(file_name, host_name)) - { return ISC_PROTOCOL_TCPIP; - } -#ifndef NO_NFS + if (implicit_flag) { - // Check for a file on an NFS mounted device + // Check for a file on a network mount - if (ISC_analyze_nfs(file_name, host_name)) - { +#ifdef WIN_NT + if (ISC_analyze_pclan(file_name, host_name)) return ISC_PROTOCOL_TCPIP; - } - } #endif -#if defined(WIN_NT) - // Check for an explicit named pipe node name - - if (ISC_analyze_pclan(file_name, host_name)) - { - return ISC_PROTOCOL_WLAN; - } - - if (implicit_flag) - { - // Check for a file on a shared drive. First try to expand - // the path. Then check the expanded path for a TCP or named pipe. - - ISC_expand_share(file_name); - if (ISC_analyze_tcp(file_name, host_name)) - { +#ifndef NO_NFS + if (ISC_analyze_nfs(file_name, host_name)) return ISC_PROTOCOL_TCPIP; - } - if (ISC_analyze_pclan(file_name, host_name)) - { - return ISC_PROTOCOL_WLAN; - } - +#endif } -#endif // WIN_NT return ISC_PROTOCOL_LOCAL; } diff --git a/src/include/cross/android.arm64 b/src/include/cross/android.arm64 index 4a4e30160f..825570e26b 100644 --- a/src/include/cross/android.arm64 +++ b/src/include/cross/android.arm64 @@ -61,9 +61,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/cross/android.arme b/src/include/cross/android.arme index 70973d5cdf..5dcdba3a8b 100644 --- a/src/include/cross/android.arme +++ b/src/include/cross/android.arme @@ -67,9 +67,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/cross/android.x86 b/src/include/cross/android.x86 index 73bb83c51e..47503e2661 100644 --- a/src/include/cross/android.x86 +++ b/src/include/cross/android.x86 @@ -61,9 +61,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/cross/android.x86_64 b/src/include/cross/android.x86_64 index 2910dd421a..cc5d8844ed 100644 --- a/src/include/cross/android.x86_64 +++ b/src/include/cross/android.x86_64 @@ -61,9 +61,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/firebird/impl/consts_pub.h b/src/include/firebird/impl/consts_pub.h index 2802d23c08..723ef16603 100644 --- a/src/include/firebird/impl/consts_pub.h +++ b/src/include/firebird/impl/consts_pub.h @@ -160,8 +160,7 @@ ::= "TCPv4" | "TCPv6" | - "XNET" | - "WNET" | + "XNET" .... ::= diff --git a/src/include/gen/autoconfig.h.in b/src/include/gen/autoconfig.h.in index d3d6ac8161..de0489a3bd 100644 --- a/src/include/gen/autoconfig.h.in +++ b/src/include/gen/autoconfig.h.in @@ -94,9 +94,6 @@ /* log file name within log dir */ #define FB_LOGFILENAME "@FB_LOGFILENAME@" -/* Wnet pipe name */ -#define FB_PIPE_NAME "@FB_PIPE_NAME@" - /* Installation path prefix */ #define FB_PREFIX "@FB_PREFIX@" diff --git a/src/include/gen/autoconfig_msvc.h b/src/include/gen/autoconfig_msvc.h index d7ba71254b..1e25478c64 100644 --- a/src/include/gen/autoconfig_msvc.h +++ b/src/include/gen/autoconfig_msvc.h @@ -285,9 +285,6 @@ #define FB_SERVICE_NAME "gds_db" #define FB_SERVICE_PORT 3050 -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* Xnet objects name */ #define FB_IPC_NAME "FIREBIRD" diff --git a/src/remote/CMakeLists.txt b/src/remote/CMakeLists.txt index 49d6dac09b..8c2fdc6e1b 100644 --- a/src/remote/CMakeLists.txt +++ b/src/remote/CMakeLists.txt @@ -17,8 +17,6 @@ set(remote_src ../auth/trusted/AuthSspi.cpp ) add_src_win32(remote_src - os/win32/wnet.cpp - os/win32/wnet_proto.h os/win32/xnet.cpp os/win32/xnet.h os/win32/xnet_proto.h diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 6567b98f9c..38091ad2c0 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -83,7 +83,6 @@ #if defined(WIN_NT) #include "../common/isc_proto.h" -#include "../remote/os/win32/wnet_proto.h" #include "../remote/os/win32/xnet_proto.h" #endif @@ -97,15 +96,10 @@ const char* const PROTOCOL_INET4 = "inet4"; const char* const PROTOCOL_INET6 = "inet6"; #ifdef WIN_NT -const char* const PROTOCOL_WNET = "wnet"; const char* const PROTOCOL_XNET = "xnet"; - -const char* const WNET_SEPARATOR = "@"; -const char* const WNET_LOCALHOST = "\\\\."; #endif const char* const INET_SEPARATOR = "/"; - const char* const INET_LOCALHOST = "localhost"; @@ -4726,13 +4720,11 @@ bool ResultSet::fetch(CheckStatusWrapper* status, void* buffer, P_FETCH operatio ( // Low in inventory (statement->rsr_rows_pending <= statement->rsr_reorder_level) && (statement->rsr_msgs_waiting <= statement->rsr_reorder_level) && - // not using named pipe on NT // Pipelining causes both server & client to - // write at the same time. In named pipes, writes + // write at the same time. In XNET, writes // block for the other end to read - and so when both // attempt to write simultaneously, they end up - // waiting indefinitely for the other end to read - (port->port_type != rem_port::PIPE) && + // waiting indefinitely for the other end to read. (port->port_type != rem_port::XNET) && // We're fetching either forward or backward (operation == fetch_next || operation == fetch_prior) && @@ -5871,12 +5863,11 @@ void Request::receive(CheckStatusWrapper* status, int level, unsigned int msg_ty (tail->rrq_rows_pending <= tail->rrq_reorder_level && // Low in inventory tail->rrq_msgs_waiting <= tail->rrq_reorder_level && // Pipelining causes both server & client to - // write at the same time. In named pipes, writes + // write at the same time. In XNET, writes // block for the other end to read - and so when both // attempt to write simultaenously, they end up - // waiting indefinetly for the other end to read - (port->port_type != rem_port::PIPE) && // not named pipe on NT - (port->port_type != rem_port::XNET) && // not shared memory on NT + // waiting indefinetly for the other end to read. + (port->port_type != rem_port::XNET) && request->rrq_max_msg <= 1))) { // there's only one message type @@ -7295,20 +7286,6 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned #ifdef WIN_NT if (ISC_analyze_protocol(PROTOCOL_XNET, attach_name, node_name, NULL, needFile)) port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_USER_VFY, cBlock.getConfig(), ref_db_name); - else if (ISC_analyze_protocol(PROTOCOL_WNET, attach_name, node_name, WNET_SEPARATOR, needFile) || - ISC_analyze_pclan(attach_name, node_name)) - { - if (node_name.isEmpty()) - node_name = WNET_LOCALHOST; - else - { - ISC_unescape(node_name); - ISC_utf8ToSystem(node_name); - } - - port = WNET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_USER_VFY, - cBlock.getConfig(), ref_db_name); - } else #endif @@ -7341,15 +7318,13 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned if (!port) { PathName expanded_name = attach_name; - ISC_expand_share(expanded_name); - if (ISC_analyze_pclan(expanded_name, node_name)) { ISC_unescape(node_name); ISC_utf8ToSystem(node_name); - port = WNET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_USER_VFY, - cBlock.getConfig(), ref_db_name); + port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_USER_VFY, pb, + cBlock.getConfig(), ref_db_name, cryptCb); } } #endif @@ -7383,12 +7358,6 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_USER_VFY, cBlock.getConfig(), ref_db_name); } - - if (!port) - { - port = WNET_analyze(&cBlock, attach_name, WNET_LOCALHOST, flags & ANALYZE_USER_VFY, - cBlock.getConfig(), ref_db_name); - } #endif if (!port) { @@ -7809,20 +7778,9 @@ static void disconnect( rem_port* port) } } - // BAND-AID: - // It seems as if we are disconnecting the port - // on both the server and client side. For now - // let the server handle this for named pipes + packet->p_operation = op_disconnect; + port->send(packet); - // 8-Aug-1997 M. Duquette - // R. Kumar - // M. Romanini - - if (port->port_type != rem_port::PIPE) - { - packet->p_operation = op_disconnect; - port->send(packet); - } REMOTE_free_packet(port, packet); } diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index 92471b149c..103169eb1e 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -671,7 +671,7 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, ISC_get_user(&buffer, &eff_uid, &eff_gid); #ifdef WIN_NT - // WNET and XNET lowercase user names (as it's always case-insensitive in Windows) + // XNET lowercases user names (as it's always case-insensitive in Windows), // so let's be consistent and use the same trick for INET as well buffer.lower(); #endif diff --git a/src/remote/os/win32/wnet.cpp b/src/remote/os/win32/wnet.cpp deleted file mode 100644 index 192f34322e..0000000000 --- a/src/remote/os/win32/wnet.cpp +++ /dev/null @@ -1,1476 +0,0 @@ -/* - * PROGRAM: JRD Remote Interface/Server - * MODULE: wnet.cpp - * DESCRIPTION: Windows Net Communications module. - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifdef DEBUG -// define WNET_trace to 0 (zero) for no packet debugging -#define WNET_trace -#endif - -#include "firebird.h" -#include -#include -#include "../remote/remote.h" -#include "ibase.h" - -#include "../utilities/install/install_nt.h" - -#include "../remote/proto_proto.h" -#include "../remote/remot_proto.h" -#include "../remote/os/win32/wnet_proto.h" -#include "../yvalve/gds_proto.h" -#include "../common/isc_proto.h" -#include "../common/isc_f_proto.h" -#include "../common/config/config.h" -#include "../common/utils_proto.h" -#include "../common/classes/ClumpletWriter.h" -#include "../common/classes/init.h" - -#include - -using namespace Firebird; - -const int MAX_DATA = 2048; -const int BUFFER_SIZE = MAX_DATA; - -const char* PIPE_PREFIX = "pipe"; // win32-specific -const char* SERVER_PIPE_SUFFIX = "server"; -const char* EVENT_PIPE_SUFFIX = "event"; -AtomicCounter event_counter; - -static GlobalPtr wnet_ports; -static GlobalPtr init_mutex; -static volatile bool wnet_initialized = false; -static volatile bool wnet_shutdown = false; - -static bool accept_connection(rem_port*, const P_CNCT*); -static rem_port* alloc_port(rem_port*); -static rem_port* aux_connect(rem_port*, PACKET*); -static rem_port* aux_request(rem_port*, PACKET*); -static bool connect_client(rem_port*); -static void disconnect(rem_port*); -#ifdef NOT_USED_OR_REPLACED -static void exit_handler(void*); -#endif -static void force_close(rem_port*); -static rem_str* make_pipe_name(const RefPtr&, const TEXT*, const TEXT*, const TEXT*); -static rem_port* receive(rem_port*, PACKET*); -static int send_full(rem_port*, PACKET*); -static int send_partial(rem_port*, PACKET*); -static RemoteXdr* xdrwnet_create(rem_port*, UCHAR *, USHORT, xdr_op); -static bool_t xdrwnet_endofrecord(RemoteXdr*);//, int); -static bool wnet_error(rem_port*, const TEXT*, ISC_STATUS, int); -static void wnet_gen_error(rem_port*, const Arg::StatusVector& v); -static bool_t wnet_read(RemoteXdr*); -static bool_t wnet_write(RemoteXdr*); //, int); -#ifdef DEBUG -static void packet_print(const TEXT*, const UCHAR*, const int); -#endif -static bool packet_receive(rem_port*, UCHAR*, SSHORT, SSHORT*); -static bool packet_send(rem_port*, const SCHAR*, SSHORT); -static void wnet_make_file_name(TEXT*, DWORD); - -static int cleanup_ports(const int, const int, void*); - -struct WnetXdr : public RemoteXdr -{ - virtual bool_t x_getbytes(SCHAR *, unsigned); // get some bytes from " - virtual bool_t x_putbytes(const SCHAR*, unsigned); // put some bytes to " -}; - - -rem_port* WNET_analyze(ClntAuthBlock* cBlock, - const PathName& file_name, - const TEXT* node_name, - bool uv_flag, - RefPtr* config, - const Firebird::PathName* ref_db_name) -{ -/************************************** - * - * W N E T _ a n a l y z e - * - ************************************** - * - * Functional description - * Determine whether the file name has a "\\nodename". - * If so, establish an external connection to the node. - * - * If a connection is established, return a port block, otherwise - * return NULL. - * - **************************************/ - - // We need to establish a connection to a remote server. Allocate the necessary - // blocks and get ready to go. - - Rdb* rdb = FB_NEW Rdb; - PACKET* packet = &rdb->rdb_packet; - - // Pick up some user identification information - string buffer; - ClumpletWriter user_id(ClumpletReader::UnTagged, 64000); - if (cBlock) - { - cBlock->extractDataFromPluginTo(user_id); - } - - ISC_get_user(&buffer, 0, 0); - buffer.lower(); - ISC_systemToUtf8(buffer); - user_id.insertString(CNCT_user, buffer); - - ISC_get_host(buffer); - buffer.lower(); - ISC_systemToUtf8(buffer); - user_id.insertString(CNCT_host, buffer); - - if (uv_flag) { - user_id.insertTag(CNCT_user_verification); - } - - // Establish connection to server - - P_CNCT* const cnct = &packet->p_cnct; - packet->p_operation = op_connect; - cnct->p_cnct_operation = 0; - cnct->p_cnct_cversion = CONNECT_VERSION3; - cnct->p_cnct_client = ARCHITECTURE; - - const PathName& cnct_file(ref_db_name ? (*ref_db_name) : file_name); - cnct->p_cnct_file.cstr_length = (ULONG) cnct_file.length(); - cnct->p_cnct_file.cstr_address = reinterpret_cast(cnct_file.c_str()); - - // If we want user verification, we can't speak anything less than version 7 - - cnct->p_cnct_user_id.cstr_length = (ULONG) user_id.getBufferLength(); - cnct->p_cnct_user_id.cstr_address = user_id.getBuffer(); - - static const p_cnct::p_cnct_repeat protocols_to_try[] = - { - REMOTE_PROTOCOL(PROTOCOL_VERSION10, ptype_batch_send, 1), - REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_batch_send, 2), - REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_batch_send, 3), - REMOTE_PROTOCOL(PROTOCOL_VERSION13, ptype_batch_send, 4), - REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_batch_send, 5), - REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_batch_send, 6), - REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_batch_send, 7), - REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_batch_send, 8) - }; - fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); - cnct->p_cnct_count = FB_NELEM(protocols_to_try); - - for (size_t i = 0; i < cnct->p_cnct_count; i++) { - cnct->p_cnct_versions[i] = protocols_to_try[i]; - } - - // If we can't talk to a server, punt. Let somebody else generate an error. - - rem_port* port = NULL; - try - { - port = WNET_connect(node_name, packet, 0, config); - } - catch (const Exception&) - { - delete rdb; - throw; - } - - // Get response packet from server. - - rdb->rdb_port = port; - port->port_context = rdb; - port->receive(packet); - - P_ACPT* accept = NULL; - switch (packet->p_operation) - { - case op_accept_data: - case op_cond_accept: - accept = &packet->p_acpd; - if (cBlock) - { - cBlock->storeDataForPlugin(packet->p_acpd.p_acpt_data.cstr_length, - packet->p_acpd.p_acpt_data.cstr_address); - cBlock->authComplete = packet->p_acpd.p_acpt_authenticated; - port->addServerKeys(&packet->p_acpd.p_acpt_keys); - cBlock->resetClnt(&packet->p_acpd.p_acpt_keys); - } - break; - - case op_accept: - if (cBlock) - { - cBlock->resetClnt(); - } - accept = &packet->p_acpt; - break; - - case op_response: - try - { - Firebird::LocalStatus warning; // Ignore connect warnings for a while - REMOTE_check_response(&warning, rdb, packet); - } - catch (const Firebird::Exception&) - { - disconnect(port); - delete rdb; - throw; - } - // fall through - response is not a required accept - - default: - disconnect(port); - delete rdb; - Arg::Gds(isc_connect_reject).raise(); - break; - } - - fb_assert(accept); - fb_assert(port); - port->port_protocol = accept->p_acpt_version; - - // once we've decided on a protocol, concatenate the version - // string to reflect it... - - string temp; - temp.printf("%s/P%d", port->port_version->str_data, - port->port_protocol & FB_PROTOCOL_MASK); - delete port->port_version; - port->port_version = REMOTE_make_string(temp.c_str()); - - if (accept->p_acpt_architecture == ARCHITECTURE) - port->port_flags |= PORT_symmetric; - - if (accept->p_acpt_type != ptype_out_of_band) - port->port_flags |= PORT_no_oob; - - return port; -} - - -rem_port* WNET_connect(const TEXT* name, PACKET* packet, USHORT flag, Firebird::RefPtr* config) -{ -/************************************** - * - * W N E T _ c o n n e c t - * - ************************************** - * - * Functional description - * Establish half of a communication link. If a connect packet is given, - * the connection is on behalf of a remote interface. Otherwise the - * connect is for a server process. - * - **************************************/ - rem_port* const port = alloc_port(0); - if (config) - { - port->port_config = *config; - } - - delete port->port_connection; - port->port_connection = make_pipe_name(port->getPortConfig(), name, SERVER_PIPE_SUFFIX, 0); - - // If we're a host, just make the connection - - if (packet) - { - while (true) - { - port->port_pipe = CreateFile(port->port_connection->str_data, - GENERIC_WRITE | GENERIC_READ, - 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (port->port_pipe != INVALID_HANDLE_VALUE) { - break; - } - const ISC_STATUS status = GetLastError(); - if (status != ERROR_PIPE_BUSY) - { - wnet_error(port, "CreateFile", isc_net_connect_err, status); - disconnect(port); - return NULL; - } - WaitNamedPipe(port->port_connection->str_data, 3000L); - } - send_full(port, packet); - return port; - } - - // We're a server, so wait for a host to show up - - wnet_ports->registerPort(port); - while (!wnet_shutdown) - { - port->port_pipe = - CreateNamedPipe(port->port_connection->str_data, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_WAIT | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, - PIPE_UNLIMITED_INSTANCES, - MAX_DATA, - MAX_DATA, - 0, - ISC_get_security_desc()); - if (port->port_pipe == INVALID_HANDLE_VALUE) - { - const DWORD dwError = GetLastError(); - if (dwError == ERROR_CALL_NOT_IMPLEMENTED) - { - disconnect(port); - wnet_shutdown = true; - break; - } - - wnet_error(port, "CreateNamedPipe", isc_net_connect_listen_err, dwError); - disconnect(port); - return NULL; - } - - if (!connect_client(port)) - break; - - if (flag & (SRVR_debug | SRVR_multi_client)) - { - port->port_server_flags |= SRVR_server; - port->port_flags |= PORT_server; - if (flag & SRVR_multi_client) - { - port->port_server_flags |= SRVR_multi_client; - } - - return port; - } - - TEXT name[MAXPATHLEN]; - GetModuleFileName(NULL, name, sizeof(name)); - - string cmdLine; - cmdLine.printf("%s -w -h %" HANDLEFORMAT"@%" ULONGFORMAT, name, port->port_pipe, GetCurrentProcessId()); - - STARTUPINFO start_crud; - PROCESS_INFORMATION pi; - start_crud.cb = sizeof(STARTUPINFO); - start_crud.lpReserved = NULL; - start_crud.lpReserved2 = NULL; - start_crud.cbReserved2 = 0; - start_crud.lpDesktop = NULL; - start_crud.lpTitle = NULL; - start_crud.dwFlags = STARTF_FORCEOFFFEEDBACK; - - if (CreateProcess(NULL, cmdLine.begin(), NULL, NULL, FALSE, - (flag & SRVR_high_priority ? - HIGH_PRIORITY_CLASS | DETACHED_PROCESS : - NORMAL_PRIORITY_CLASS | DETACHED_PROCESS), - NULL, NULL, &start_crud, &pi)) - { - // hvlad: child process will close our handle of client pipe - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - } - else - { - gds__log("WNET/wnet_error: fork/CreateProcess errno = %d", GetLastError()); - CloseHandle(port->port_pipe); - } - - if (wnet_shutdown) - disconnect(port); - } - - if (wnet_shutdown) - { - Arg::Gds temp(isc_net_server_shutdown); - temp << Arg::Str("WNET"); - temp.raise(); - } - - return NULL; -} - - -rem_port* WNET_reconnect(HANDLE handle) -{ -/************************************** - * - * W N E T _ r e c o n n e c t - * - ************************************** - * - * Functional description - * A communications link has been established by another - * process. We have inherited the handle. Set up - * a port block. - * - **************************************/ - rem_port* const port = alloc_port(0); - - delete port->port_connection; - port->port_connection = make_pipe_name(port->getPortConfig(), NULL, SERVER_PIPE_SUFFIX, 0); - - port->port_pipe = handle; - port->port_server_flags |= SRVR_server; - port->port_flags |= PORT_server; - - return port; -} - - -static bool accept_connection( rem_port* port, const P_CNCT* cnct) -{ -/************************************** - * - * a c c e p t _ c o n n e c t i o n - * - ************************************** - * - * Functional description - * Accept an incoming request for connection. This is purely a lower - * level handshaking function, and does not constitute the server - * response for protocol selection. - * - **************************************/ - // Default account to "guest" (in theory all packets contain a name) - - string user_name("guest"), host_name; - - // Pick up account and host name, if given - - ClumpletReader id(ClumpletReader::UnTagged, - cnct->p_cnct_user_id.cstr_address, - cnct->p_cnct_user_id.cstr_length); - - for (id.rewind(); !id.isEof(); id.moveNext()) - { - switch (id.getClumpTag()) - { - case CNCT_user: - id.getString(user_name); - break; - - case CNCT_host: - id.getString(host_name); - break; - - default: - break; - } - } - - port->port_login = port->port_user_name = user_name; - port->port_peer_name = host_name; - port->port_protocol_id = "WNET"; - - return true; -} - - -static rem_port* alloc_port( rem_port* parent) -{ -/************************************** - * - * a l l o c _ p o r t - * - ************************************** - * - * Functional description - * Allocate a port block, link it in to parent (if there is a parent), - * and initialize input and output XDR streams. - * - **************************************/ - - if (!wnet_initialized) - { - MutexLockGuard guard(init_mutex, FB_FUNCTION); - if (!wnet_initialized) - { - wnet_initialized = true; - fb_shutdown_callback(0, cleanup_ports, fb_shut_postproviders, 0); - } - } - - rem_port* port = FB_NEW rem_port(rem_port::PIPE, BUFFER_SIZE * 2); - - TEXT buffer[BUFFER_TINY]; - ISC_get_host(buffer, sizeof(buffer)); - port->port_host = REMOTE_make_string(buffer); - port->port_connection = REMOTE_make_string(buffer); - sprintf(buffer, "WNet (%s)", port->port_host->str_data); - port->port_version = REMOTE_make_string(buffer); - - port->port_accept = accept_connection; - port->port_disconnect = disconnect; - port->port_force_close = force_close; - port->port_receive_packet = receive; - port->port_send_packet = send_full; - port->port_send_partial = send_partial; - port->port_connect = aux_connect; - port->port_request = aux_request; - port->port_buff_size = BUFFER_SIZE; - - port->port_event = CreateEvent(NULL, TRUE, TRUE, NULL); - - port->port_send = xdrwnet_create(port, &port->port_buffer[BUFFER_SIZE], BUFFER_SIZE, XDR_ENCODE); - - port->port_receive = xdrwnet_create(port, port->port_buffer, 0, XDR_DECODE); - - if (parent) - { - delete port->port_connection; - port->port_connection = nullptr; - port->port_connection = REMOTE_make_string(parent->port_connection->str_data); - - port->linkParent(parent); - } - - return port; -} - - -static rem_port* aux_connect( rem_port* port, PACKET* packet) -{ -/************************************** - * - * a u x _ c o n n e c t - * - ************************************** - * - * Functional description - * Try to establish an alternative connection. Somebody has already - * done a successfull connect request ("packet" contains the response). - * - **************************************/ - // If this is a server, we're got an auxiliary connection. Accept it - - if (port->port_server_flags) - { - if (!connect_client(port)) - return NULL; - - port->port_flags |= PORT_async; - return port; - } - - // The server will be sending its process id in the packet to - // create a unique pipe name. - - P_RESP* response = &packet->p_resp; - - TEXT str_pid[32]; - const TEXT* p = 0; - if (response->p_resp_data.cstr_length) - { - // Avoid B.O. - const size_t len = MIN(response->p_resp_data.cstr_length, sizeof(str_pid) - 1); - memcpy(str_pid, response->p_resp_data.cstr_address, len); - str_pid[len] = 0; - p = str_pid; - } - - rem_port* const new_port = alloc_port(port->port_parent); - port->port_async = new_port; - new_port->port_flags = port->port_flags & PORT_no_oob; - new_port->port_flags |= PORT_async; - new_port->port_connection = make_pipe_name(port->getPortConfig(), - port->port_connection->str_data, EVENT_PIPE_SUFFIX, p); - - while (true) - { - new_port->port_pipe = - CreateFile(new_port->port_connection->str_data, GENERIC_READ, 0, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (new_port->port_pipe != INVALID_HANDLE_VALUE) - break; - const ISC_STATUS status = GetLastError(); - if (status != ERROR_PIPE_BUSY) - { - wnet_error(new_port, "CreateFile", isc_net_event_connect_err, status); - return NULL; - } - WaitNamedPipe(new_port->port_connection->str_data, 3000L); - } - - return new_port; -} - - -static rem_port* aux_request( rem_port* vport, PACKET* packet) -{ -/************************************** - * - * a u x _ r e q u e s t - * - ************************************** - * - * Functional description - * A remote interface has requested the server prepare an auxiliary - * connection; the server calls aux_request to set up the connection. - * Send the servers process id on the packet. If at a later time - * a multi client server is used, there may be a need to - * generate a unique id based on connection. - * - **************************************/ - - const DWORD server_pid = (vport->port_server_flags & SRVR_multi_client) ? - ++event_counter : GetCurrentProcessId(); - rem_port* const new_port = alloc_port(vport->port_parent); - new_port->port_server_flags = vport->port_server_flags; - new_port->port_flags = (vport->port_flags & PORT_no_oob) | PORT_connecting; - vport->port_async = new_port; - - TEXT str_pid[32]; - wnet_make_file_name(str_pid, server_pid); - new_port->port_connection = make_pipe_name(vport->getPortConfig(), - vport->port_connection->str_data, EVENT_PIPE_SUFFIX, str_pid); - - new_port->port_pipe = - CreateNamedPipe(new_port->port_connection->str_data, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_WAIT | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, - PIPE_UNLIMITED_INSTANCES, - MAX_DATA, - MAX_DATA, - 0, - ISC_get_security_desc()); - - if (new_port->port_pipe == INVALID_HANDLE_VALUE) - { - wnet_error(new_port, "CreateNamedPipe", isc_net_event_listen_err, ERRNO); - disconnect(new_port); - return NULL; - } - - P_RESP* response = &packet->p_resp; - response->p_resp_data.cstr_length = (ULONG) strlen(str_pid); - memcpy(response->p_resp_data.cstr_address, str_pid, response->p_resp_data.cstr_length); - - return new_port; -} - - -static bool connect_client(rem_port *port) -{ -/************************************** - * - * c o n n e c t _ c l i e n t - * - ************************************** - * - * Functional description - * Wait for new client connected. - * - **************************************/ - - OVERLAPPED ovrl = {0}; - ovrl.hEvent = port->port_event; - if (!ConnectNamedPipe(port->port_pipe, &ovrl)) - { - DWORD err = GetLastError(); - switch (err) - { - case ERROR_PIPE_CONNECTED: - break; - - case ERROR_IO_PENDING: - if (WaitForSingleObject(port->port_event, INFINITE) == WAIT_OBJECT_0) - { - if (!wnet_shutdown) - break; - } - else - err = GetLastError(); // fall thru - - default: - if (!wnet_shutdown) { - wnet_error(port, "ConnectNamedPipe", isc_net_connect_err, err); - } - disconnect(port); - return false; - } - } - return true; -} - - -static void disconnect(rem_port* port) -{ -/************************************** - * - * d i s c o n n e c t - * - ************************************** - * - * Functional description - * Break a remote connection. - * - **************************************/ - - if (port->port_state == rem_port::DISCONNECTED) - return; - - port->port_state = rem_port::DISCONNECTED; - - if (port->port_async) - { - disconnect(port->port_async); - port->port_async = NULL; - } - port->port_context = NULL; - - // If this is a sub-port, unlink it from its parent - port->unlinkParent(); - port->port_flags &= ~PORT_connecting; - - if (port->port_server_flags & SRVR_server) - { - FlushFileBuffers(port->port_pipe); - DisconnectNamedPipe(port->port_pipe); - } - if (port->port_event != INVALID_HANDLE_VALUE) - { - CloseHandle(port->port_event); - port->port_event = INVALID_HANDLE_VALUE; - } - if (port->port_pipe != INVALID_HANDLE_VALUE) - { - CloseHandle(port->port_pipe); - port->port_pipe = INVALID_HANDLE_VALUE; - } - - wnet_ports->unRegisterPort(port); - - if (port->port_thread_guard && port->port_events_thread && !port->port_events_threadId.isCurrent()) - port->port_thread_guard->setWait(port->port_events_thread); - else - port->releasePort(); -} - - -static void force_close(rem_port* port) -{ -/************************************** - * - * f o r c e _ c l o s e - * - ************************************** - * - * Functional description - * Forcibly close remote connection. - * - **************************************/ - - if (port->port_event != INVALID_HANDLE_VALUE) - { - port->port_state = rem_port::BROKEN; - - const HANDLE handle = port->port_pipe; - port->port_pipe = INVALID_HANDLE_VALUE; - SetEvent(port->port_event); - CloseHandle(handle); - } -} - - -#ifdef NOT_USED_OR_REPLACED -static void exit_handler(void* main_port) -{ -/************************************** - * - * e x i t _ h a n d l e r - * - ************************************** - * - * Functional description - * Shutdown all active connections - * to allow restart. - * - **************************************/ - for (rem_port* vport = static_cast(main_port); vport; vport = vport->port_next) - CloseHandle(vport->port_pipe); -} -#endif - - -static rem_str* make_pipe_name(const RefPtr& config, const TEXT* connect_name, - const TEXT* suffix_name, const TEXT* str_pid) -{ -/************************************** - * - * m a k e _ p i p e _ n a m e - * - ************************************** - * - * Functional description - * Construct a name for the pipe connection. - * Figure out whether we need a remote node name, - * and construct the pipe name accordingly. - * If a server pid != 0, append it to pipe name as <>/ - * - **************************************/ - string buffer("\\\\"); - - const TEXT* p = connect_name; - - if (!p || *p++ != '\\' || *p++ != '\\') - p = "."; - - while (*p && *p != '\\' && *p != '@') - buffer += *p++; - - const TEXT* protocol = NULL; - switch (*p) - { - case 0: - protocol = config->getRemoteServiceName(); - break; - case '@': - protocol = p + 1; - break; - default: - while (*p) - { - if (*p++ == '\\') - protocol = p; - } - } - - buffer += '\\'; - buffer += PIPE_PREFIX; - buffer += '\\'; - const char *pipe_name = config->getRemotePipeName(); - buffer += pipe_name; - buffer += '\\'; - buffer += suffix_name; - buffer += '\\'; - buffer += protocol; - - if (str_pid) - { - buffer += '\\'; - buffer += str_pid; - } - - return REMOTE_make_string(buffer.c_str()); -} - - -static rem_port* receive( rem_port* main_port, PACKET* packet) -{ -/************************************** - * - * r e c e i v e - * - ************************************** - * - * Functional description - * Receive a message from a port or clients of a port. If the process - * is a server and a connection request comes in, generate a new port - * block for the client. - * - **************************************/ - - if (!xdr_protocol(main_port->port_receive, packet)) - packet->p_operation = op_exit; - - return main_port; -} - - -static int send_full( rem_port* port, PACKET* packet) -{ -/************************************** - * - * s e n d _ f u l l - * - ************************************** - * - * Functional description - * Send a packet across a port to another process. - * - **************************************/ - - if (!xdr_protocol(port->port_send, packet)) - return FALSE; - - return xdrwnet_endofrecord(port->port_send); //, TRUE); -} - - -static int send_partial( rem_port* port, PACKET* packet) -{ -/************************************** - * - * s e n d _ p a r t i a l - * - ************************************** - * - * Functional description - * Send a packet across a port to another process. - * - **************************************/ - - return xdr_protocol(port->port_send, packet); -} - - -static RemoteXdr* xdrwnet_create(rem_port* port, UCHAR* buffer, USHORT length, xdr_op x_op) -{ -/************************************** - * - * x d r w n e t _ c r e a t e - * - ************************************** - * - * Functional description - * Initialize an XDR stream for Apollo mailboxes. - * - **************************************/ - - RemoteXdr* xdrs = FB_NEW WnetXdr; - - xdrs->x_public = port; - xdrs->create(reinterpret_cast(buffer), length, x_op); - - return xdrs; -} - - -static bool_t xdrwnet_endofrecord( RemoteXdr* xdrs) //, bool_t flushnow) -{ -/************************************** - * - * x d r w n e t _ e n d o f r e c o r d - * - ************************************** - * - * Functional description - * Write out the rest of a record. - * - **************************************/ - - return wnet_write(xdrs); //, flushnow); -} - - -static bool wnet_error(rem_port* port, - const TEXT* function, ISC_STATUS operation, int status) -{ -/************************************** - * - * w n e t _ e r r o r - * - ************************************** - * - * Functional description - * An I/O error has occurred. If a status vector is present, - * generate an error return. In any case, return NULL, which - * is used to indicate and error. - * - **************************************/ - if (status) - { - if (port->port_state == rem_port::PENDING) - { - gds__log("WNET/wnet_error: %s errno = %d", function, status); - } - - wnet_gen_error(port, Arg::Gds(operation) << SYS_ERR(status)); - } - else - { - wnet_gen_error(port, Arg::Gds(operation)); - } - - return false; -} - - -static void wnet_gen_error (rem_port* port, const Arg::StatusVector& v) -{ -/************************************** - * - * w n e t _ g e n _ e r r o r - * - ************************************** - * - * Functional description - * An error has occurred. Mark the port as broken. - * Format the status vector if there is one and - * save the status vector strings in a permanent place. - * - **************************************/ - port->port_state = rem_port::BROKEN; - - TEXT node_name[MAXPATHLEN]; - if (port->port_connection) - { - fb_utils::copy_terminate(node_name, port->port_connection->str_data + 2, sizeof(node_name)); - TEXT* const p = strchr(node_name, '\\'); - if (p != NULL) - *p = '\0'; - } - else - { - strcpy(node_name, "(unknown)"); - } - - Arg::Gds error(isc_network_error); - error << Arg::Str(node_name) << v; - error.raise(); -} - - -bool_t WnetXdr::x_getbytes(SCHAR* buff, unsigned bytecount) -{ -/************************************** - * - * w n e t _ g e t b y t e s - * - ************************************** - * - * Functional description - * Get a bunch of bytes from a memory stream if it fits. - * - **************************************/ - // Use memcpy to optimize bulk transfers. - - while (bytecount > (SLONG) sizeof(ISC_QUAD)) - { - if (x_handy >= bytecount) - { - memcpy(buff, x_private, bytecount); - x_private += bytecount; - x_handy -= bytecount; - return TRUE; - } - if (x_handy > 0) - { - memcpy(buff, x_private, x_handy); - x_private += x_handy; - buff += x_handy; - bytecount -= x_handy; - x_handy = 0; - } - if (!wnet_read(this)) - return FALSE; - } - - // Scalar values and bulk transfer remainder fall thru - // to be moved byte-by-byte to avoid memcpy setup costs. - - if (!bytecount) - return TRUE; - - if (x_handy >= bytecount) - { - x_handy -= bytecount; - do { - *buff++ = *x_private++; - } while (--bytecount); - return TRUE; - } - - while (bytecount--) - { - if (x_handy == 0 && !wnet_read(this)) - return FALSE; - *buff++ = *x_private++; - --x_handy; - } - - return TRUE; -} - - -bool_t WnetXdr::x_putbytes(const SCHAR* buff, unsigned count) -{ -/************************************** - * - * w n e t _ p u t b y t e s - * - ************************************** - * - * Functional description - * Put a bunch of bytes to a memory stream if it fits. - * - **************************************/ - SLONG bytecount = count; - - // Use memcpy to optimize bulk transfers. - - while (bytecount > (SLONG) sizeof(ISC_QUAD)) - { - if (x_handy >= bytecount) - { - memcpy(x_private, buff, bytecount); - x_private += bytecount; - x_handy -= bytecount; - return TRUE; - } - if (x_handy > 0) - { - memcpy(x_private, buff, x_handy); - x_private += x_handy; - buff += x_handy; - bytecount -= x_handy; - x_handy = 0; - } - if (!wnet_write(this /*, 0*/)) - return FALSE; - } - - // Scalar values and bulk transfer remainder fall thru - // to be moved byte-by-byte to avoid memcpy setup costs. - - if (!bytecount) - return TRUE; - - if (x_handy >= bytecount) - { - x_handy -= bytecount; - do { - *x_private++ = *buff++; - } while (--bytecount); - return TRUE; - } - - while (bytecount--) - { - if (x_handy == 0 && !wnet_write(this /*, 0*/)) - return FALSE; - --x_handy; - *x_private++ = *buff++; - } - - return TRUE; -} - - -static bool_t wnet_read( RemoteXdr* xdrs) -{ -/************************************** - * - * w n e t _ r e a d - * - ************************************** - * - * Functional description - * Read a buffer full of data. If we receive a bad packet, - * send the moral equivalent of a NAK and retry. ACK all - * partial packets. Don't ACK the last packet -- the next - * message sent will handle this. - * - **************************************/ - rem_port* port = xdrs->x_public; - SCHAR* p = xdrs->x_base; - const SCHAR* const end = p + BUFFER_SIZE; - - // If buffer is not completely empty, slide down what what's left - - if (xdrs->x_handy > 0) - { - memmove(p, xdrs->x_private, xdrs->x_handy); - p += xdrs->x_handy; - } - - while (true) - { - SSHORT length = end - p; - if (!packet_receive(port, reinterpret_cast(p), length, &length)) - { - return FALSE; - } - if (length >= 0) - { - p += length; - break; - } - p -= length; - if (!packet_send(port, 0, 0)) - return FALSE; - } - - xdrs->x_handy = p - xdrs->x_base; - xdrs->x_private = xdrs->x_base; - - return TRUE; -} - - -static bool_t wnet_write( RemoteXdr* xdrs /*, bool_t end_flag*/) -{ -/************************************** - * - * w n e t _ w r i t e - * - ************************************** - * - * Functional description - * Write a buffer fulll of data. - * Obsolete: If the end_flag isn't set, indicate - * that the buffer is a fragment, and reset the XDR for another buffer - * load. - * - **************************************/ - // Encode the data portion of the packet - - rem_port* vport = xdrs->x_public; - const SCHAR* p = xdrs->x_base; - SSHORT length = xdrs->x_private - p; - - // Send data in manageable hunks. If a packet is partial, indicate - // that with a negative length. A positive length marks the end. - - while (length) - { - const SSHORT l = MIN(length, MAX_DATA); - length -= l; - if (!packet_send(vport, p, (SSHORT) (length ? -l : l))) - return FALSE; - p += l; - } - - xdrs->x_private = xdrs->x_base; - xdrs->x_handy = BUFFER_SIZE; - - return TRUE; -} - - -#ifdef DEBUG -static void packet_print(const TEXT* string, const UCHAR* packet, const int length) -{ -/************************************** - * - * p a c k e t _ p r i n t - * - ************************************** - * - * Functional description - * Print a summary of packet. - * - **************************************/ - int sum = 0; - int l = length; - - if (l) - { - do { - sum += *packet++; - } while (--l); - } - - printf("%s\t: length = %d, checksum = %d\n", string, length, sum); -} -#endif - - -static bool packet_receive(rem_port* port, UCHAR* buffer, SSHORT buffer_length, SSHORT* length) -{ -/************************************** - * - * p a c k e t _ r e c e i v e - * - ************************************** - * - * Functional description - * Receive a packet and pass on it's goodness. If it's good, - * return true and the reported length of the packet, and update - * the receive sequence number. If it's bad, return false. If it's - * a duplicate message, just ignore it. - * - **************************************/ - DWORD n = 0; - OVERLAPPED ovrl = {0}; - ovrl.hEvent = port->port_event; - - BOOL status = ReadFile(port->port_pipe, buffer, buffer_length, &n, &ovrl); - DWORD dwError = GetLastError(); - - if (!status && dwError == ERROR_IO_PENDING) - { - status = GetOverlappedResult(port->port_pipe, &ovrl, &n, TRUE); - dwError = GetLastError(); - } - if (!status && dwError != ERROR_BROKEN_PIPE) { - return wnet_error(port, "ReadFile", isc_net_read_err, dwError); - } - - if (!n) - { - if (port->port_flags & (PORT_detached | PORT_disconnect)) - return false; - - return wnet_error(port, "ReadFile end-of-file", isc_net_read_err, dwError); - } - - // decrypt - if (port->port_crypt_plugin) - { - LocalStatus ls; - CheckStatusWrapper st(&ls); - - port->port_crypt_plugin->decrypt(&st, n, buffer, buffer); - if (st.getState() & IStatus::STATE_ERRORS) - { - status_exception::raise(&st); - } - } - -#if defined(DEBUG) && defined(WNET_trace) - packet_print("receive", buffer, n); -#endif - - port->port_rcv_packets++; - port->port_rcv_bytes += n; - - *length = (SSHORT) n; - - return true; -} - - -static bool packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_length) -{ -/************************************** - * - * p a c k e t _ s e n d - * - ************************************** - * - * Functional description - * Send some data on it's way. - * - **************************************/ - const SCHAR* data = buffer; - const DWORD length = buffer_length; - - // encrypt - HalfStaticArray b; - if (port->port_crypt_plugin && port->port_crypt_complete) - { - LocalStatus ls; - CheckStatusWrapper st(&ls); - - char* d = b.getBuffer(buffer_length); - port->port_crypt_plugin->encrypt(&st, buffer_length, data, d); - if (st.getState() & IStatus::STATE_ERRORS) - { - status_exception::raise(&st); - } - - data = d; - } - - OVERLAPPED ovrl = {0}; - ovrl.hEvent = port->port_event; - - DWORD n; - BOOL status = WriteFile(port->port_pipe, data, length, &n, &ovrl); - DWORD dwError = GetLastError(); - - if (!status && dwError == ERROR_IO_PENDING) - { - status = GetOverlappedResult(port->port_pipe, &ovrl, &n, TRUE); - dwError = GetLastError(); - } - if (!status && dwError != ERROR_NO_DATA) - return wnet_error(port, "WriteFile", isc_net_write_err, dwError); - if (n != length) - { - if (port->port_flags & (PORT_detached | PORT_disconnect)) - return false; - - return wnet_error(port, "WriteFile truncated", isc_net_write_err, dwError); - } - -#if defined(DEBUG) && defined(WNET_trace) - packet_print("send", reinterpret_cast(buffer), buffer_length); -#endif - - port->port_snd_packets++; - port->port_snd_bytes += buffer_length; - - return true; -} - - -static void wnet_make_file_name( TEXT* name, DWORD number) -{ -/************************************** - * - * w n e t _ m a k e _ f i l e _ n a m e - * - ************************************** - * - * Functional description - * Create a file name out of a number making sure - * the Windows <8>.<3> limitations are handled. - * - **************************************/ - TEXT temp[32]; - - sprintf(temp, "%lu", number); - - size_t length = strlen(temp); - if (length < 8) - { - strcpy(name, temp); - return; - } - - TEXT* p = name; - const TEXT* q = temp; - - while (length) - { - size_t len = (length > 8) ? 8 : length; - length -= len; - do { - *p++ = *q++; - } while (--len != 0); - - if (length) - *p++ = '\\'; - } - *p++ = 0; -} - -static int cleanup_ports(const int, const int, void*) -{ -/************************************** - * - * c l e a n u p _ p o r t s - * - ************************************** - * - * Functional description - * Shutdown all active connections - * to allow correct shutdown. - * - **************************************/ - wnet_shutdown = true; - - wnet_ports->closePorts(); - return 0; -} diff --git a/src/remote/os/win32/wnet_proto.h b/src/remote/os/win32/wnet_proto.h deleted file mode 100644 index e3251ecebd..0000000000 --- a/src/remote/os/win32/wnet_proto.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * PROGRAM: JRD Remote Interface/Server - * MODULE: wnet_proto.h - * DESCRIPTION: Prototpe header file for wnet.cpp - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifndef REMOTE_WNET_PROTO_H -#define REMOTE_WNET_PROTO_H - -#include "../common/classes/fb_string.h" - -rem_port* WNET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, bool, - Firebird::RefPtr*, const Firebird::PathName*); -rem_port* WNET_connect(const TEXT*, struct packet*, USHORT, Firebird::RefPtr*); -rem_port* WNET_reconnect(HANDLE); - - -#endif // REMOTE_WNET_PROTO_H diff --git a/src/remote/remote.h b/src/remote/remote.h index 0fa6ae8e72..befc838c65 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -1042,7 +1042,6 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted enum rem_port_t { INET, // Internet (TCP/IP) - PIPE, // Windows NT named pipe connection XNET // Windows NT shared memory connection } port_type; enum state_t { diff --git a/src/remote/remote_def.h b/src/remote/remote_def.h index 58daa1aa7a..39e34e0e59 100644 --- a/src/remote/remote_def.h +++ b/src/remote/remote_def.h @@ -81,11 +81,10 @@ const USHORT SRVR_server = 1; // server const USHORT SRVR_multi_client = 2; // multi-client server const USHORT SRVR_debug = 4; // debug run const USHORT SRVR_inet = 8; // Inet protocol -const USHORT SRVR_wnet = 16; // Wnet (named pipe) protocol (WinNT) -const USHORT SRVR_xnet = 32; // Xnet protocol (Win32) -const USHORT SRVR_non_service = 64; // not running as an NT service -const USHORT SRVR_high_priority = 128; // fork off server at high priority -const USHORT SRVR_thread_per_port = 256; // bind thread to a port -const USHORT SRVR_no_icon = 512; // tell the server not to show the icon +const USHORT SRVR_xnet = 16; // Xnet protocol (Win32) +const USHORT SRVR_non_service = 32; // not running as an NT service +const USHORT SRVR_high_priority = 64; // fork off server at high priority +const USHORT SRVR_thread_per_port = 128; // bind thread to a port +const USHORT SRVR_no_icon = 256; // tell the server not to show the icon #endif /* REMOTE_REMOTE_DEF_H */ diff --git a/src/remote/server/os/win32/property.cpp b/src/remote/server/os/win32/property.cpp index 94a3eab1d0..00ff4cf690 100644 --- a/src/remote/server/os/win32/property.cpp +++ b/src/remote/server/os/win32/property.cpp @@ -154,7 +154,7 @@ LRESULT CALLBACK GeneralPage(HWND hDlg, UINT unMsg, WPARAM wParam, LPARAM lParam lstrcpy(szText, FB_VERSION); SetDlgItemText(hDlg, IDC_STAT1, szText); - if (usServerFlags & (SRVR_inet | SRVR_wnet)) + if (usServerFlags & SRVR_inet) LoadString(hInstance, IDS_SERVERPROD_NAME, szText, sizeof(szText)); else LoadString(hInstance, IDS_LOCALPROD_NAME, szText, sizeof(szText)); @@ -241,14 +241,6 @@ static char* MakeVersionString(char* pchBuf, size_t nLen, USHORT usServerFlagMas if (p < end) *p++ = '\n'; } - if ((usServerFlagMask & SRVR_wnet) && end > p) - { - p += LoadString(hInstance, IDS_NP, p, end - p); - if (p < end) - *p++ = '\r'; - if (p < end) - *p++ = '\n'; - } if ((usServerFlagMask & SRVR_xnet) && end > p) { p += LoadString(hInstance, IDS_IPC, p, end - p); diff --git a/src/remote/server/os/win32/property.rc b/src/remote/server/os/win32/property.rc index 4223a9e1e3..ba69270590 100644 --- a/src/remote/server/os/win32/property.rc +++ b/src/remote/server/os/win32/property.rc @@ -45,7 +45,6 @@ STRINGTABLE BEGIN IDS_TCP, "TCP/IP Client Support" IDS_IPC, "Local Client Support" - IDS_NP, "NetBEUI Client Support" IDS_SUPER, "Multithreaded Server" IDS_SERVERPROD_NAME, "Firebird SQL Server for Windows" IDS_LOCALPROD_NAME, "Local Firebird Server" diff --git a/src/remote/server/os/win32/property.rh b/src/remote/server/os/win32/property.rh index 6d72d4ec36..f4cefc774c 100644 --- a/src/remote/server/os/win32/property.rh +++ b/src/remote/server/os/win32/property.rh @@ -48,7 +48,6 @@ produced by Borland Resource Workshop #define IDI_IBSVR 1007 #define IDS_TCP 11 -#define IDS_NP 12 #define IDS_IPC 13 #define IDS_SUPER 14 diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index bd753380c5..9eaf7c9572 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -103,7 +103,6 @@ #include "../remote/server/serve_proto.h" #include "../remote/server/ReplServer.h" #include "../remote/server/os/win32/window_proto.h" -#include "../remote/os/win32/wnet_proto.h" #include "../remote/server/os/win32/window.rh" #include "../remote/os/win32/xnet_proto.h" #include "../yvalve/gds_proto.h" @@ -118,7 +117,6 @@ static THREAD_ENTRY_DECLARE inet_connect_wait_thread(THREAD_ENTRY_PARAM); -static THREAD_ENTRY_DECLARE wnet_connect_wait_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE xnet_connect_wait_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE process_connection_thread(THREAD_ENTRY_PARAM); @@ -129,7 +127,6 @@ static int wait_threads(const int reason, const int mask, void* arg); static HINSTANCE hInst; static TEXT protocol_inet[128]; -static TEXT protocol_wnet[128]; static TEXT instance[MAXPATHLEN]; static USHORT server_flag = 0; static bool server_shutdown = false; @@ -224,7 +221,6 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs, SetProcessAffinityMask(GetCurrentProcess(), affinity); protocol_inet[0] = 0; - protocol_wnet[0] = 0; strcpy(instance, FB_DEFAULT_INSTANCE); @@ -295,8 +291,6 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs, port = NULL; } } - else if (server_flag & SRVR_wnet) - port = WNET_reconnect(connection_handle); else if (server_flag & SRVR_xnet) port = XNET_reconnect((ULONG_PTR) connection_handle); @@ -428,56 +422,6 @@ static THREAD_ENTRY_DECLARE inet_connect_wait_thread(THREAD_ENTRY_PARAM) } -static THREAD_ENTRY_DECLARE wnet_connect_wait_thread(THREAD_ENTRY_PARAM) -{ -/************************************** - * - * w n e t _ c o n n e c t _ w a i t _ t h r e a d - * - ************************************** - * - * Functional description - * - **************************************/ - ThreadCounter counter; - - while (!server_shutdown) - { - rem_port* port = NULL; - - try - { - port = WNET_connect(protocol_wnet, NULL, server_flag, NULL); - } - catch (const Exception& ex) - { - SimpleStatusVector<> status_vector; - ex.stuffException(status_vector); - - if (status_vector[1] == isc_net_server_shutdown) - break; - - iscLogException("WNET_connect", ex); - } - - if (port) - { - try - { - Thread::start(process_connection_thread, port, THREAD_medium); - } - catch (const Exception&) - { - gds__log("WNET: can't start worker thread, connection terminated"); - port->disconnect(NULL, NULL); - } - } - } - - return 0; -} - - static THREAD_ENTRY_DECLARE xnet_connect_wait_thread(THREAD_ENTRY_PARAM) { /************************************** @@ -571,18 +515,6 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) } } - if (server_flag & SRVR_wnet) - { - try - { - Thread::start(wnet_connect_wait_thread, 0, THREAD_medium); - } - catch (const Exception& ex) - { - iscLogException("WNET: can't start listener thread", ex); - } - } - if (server_flag & SRVR_xnet) { try @@ -650,19 +582,10 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) if (*p) { TEXT buffer[32]; + const char* end = buffer + sizeof(buffer) - 1; char* pp = buffer; - while (*p && *p != ' ' && (pp - buffer < sizeof(buffer) - 1)) - { - if (*p == '@') - { - p++; - *pp++ = '\0'; - connection_handle = (HANDLE) _atoi64(buffer); - pp = buffer; - } - else - *pp++ = *p++; - } + while (*p && *p != ' ' && pp < end) + *pp++ = *p++; *pp++ = '\0'; if (connection_handle == INVALID_HANDLE_VALUE) @@ -699,7 +622,7 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) *pserver_flag |= SRVR_no_icon; break; - case 'P': // Specify a port or named pipe other than the default + case 'P': // Specify a port other than the default while (*p && *p == ' ') p++; if (*p) @@ -708,23 +631,14 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) // in the future, hence I did generic code. char* pi = protocol_inet; const char* piend = protocol_inet + sizeof(protocol_inet) - 1; - char* pw = protocol_wnet; - const char* pwend = protocol_wnet + sizeof(protocol_wnet) - 1; *pi++ = '/'; - *pw++ = '\\'; - *pw++ = '\\'; - *pw++ = '.'; - *pw++ = '@'; while (*p && *p != ' ') { if (pi < piend) *pi++ = *p; - if (pw < pwend) - *pw++ = *p++; } *pi++ = '\0'; - *pw++ = '\0'; } break; @@ -766,10 +680,6 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) } break; - case 'W': - *pserver_flag |= SRVR_wnet; - break; - case 'X': *pserver_flag |= SRVR_xnet; break; @@ -791,9 +701,8 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) } } - if ((*pserver_flag & (SRVR_inet | SRVR_wnet | SRVR_xnet)) == 0) + if ((*pserver_flag & (SRVR_inet | SRVR_xnet)) == 0) { - *pserver_flag |= SRVR_wnet; *pserver_flag |= SRVR_inet; *pserver_flag |= SRVR_xnet; } diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 10c6ea2dbe..d1e3965144 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -2536,7 +2536,7 @@ static void aux_request( rem_port* port, /*P_REQ* request,*/ PACKET* send) return; } - // This buffer is used by INET and WNET transports + // This buffer is used by INET transport // to return the server identification string UCHAR buffer[BUFFER_TINY]; send->p_resp.p_resp_data.cstr_address = buffer; @@ -2937,7 +2937,7 @@ void rem_port::disconnect(PACKET* sendL, PACKET* receiveL) return; } - // For WNET and XNET we should send dummy op_disconnect packet + // For XNET we should send dummy op_disconnect packet // to wakeup async port handling events on client side. // For INET it's not necessary because INET client's async port // wakes up while server performs shutdown(socket) call on its async port. @@ -2946,7 +2946,7 @@ void rem_port::disconnect(PACKET* sendL, PACKET* receiveL) PACKET *packet = &rdb->rdb_packet; if (this->port_async) { - if ((this->port_type == rem_port::XNET) || (this->port_type == rem_port::PIPE)) + if (this->port_type == rem_port::XNET) { packet->p_operation = op_disconnect; this->port_async->send(packet); diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index d7150af14e..b6ba23f236 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -396,22 +396,12 @@ int gsec(Firebird::UtilSvc* uSvc) const Firebird::string sqlRoleName(user_data->role.entered() ? user_data->role.get() : ""); Firebird::PathName serverName; - const bool useServices = !uSvc->isService(); - - switch (ISC_extract_host(databaseName, serverName, true)) - { - case ISC_PROTOCOL_TCPIP: + if (ISC_extract_host(databaseName, serverName, true) == ISC_PROTOCOL_TCPIP) serverName += ":"; - break; - case ISC_PROTOCOL_WLAN: - serverName = "\\\\" + serverName + "\\"; - break; - } + const bool useServices = !uSvc->isService(); if (!useServices) - { serverName = ""; - } Firebird::LocalStatus s; Firebird::CheckStatusWrapper statusWrapper(&s); diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index 17e842c735..df27fd696a 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -593,18 +593,13 @@ int gstat(Firebird::UtilSvc* uSvc) const Firebird::PathName connName = fileName; Firebird::PathName tempStr; -#ifdef WIN_NT - if (!ISC_analyze_pclan(fileName, tempStr)) -#endif + if (!ISC_analyze_tcp(fileName, tempStr)) { - if (!ISC_analyze_tcp(fileName, tempStr)) - { #ifndef NO_NFS - if (!ISC_analyze_nfs(fileName, tempStr)) + if (!ISC_analyze_nfs(fileName, tempStr)) #endif - { - fileName = connName; - } + { + fileName = connName; } } From 8a9374fbb9e1fa8f758e948cef2e274a045cbf8c Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 5 Jan 2022 12:51:52 +0300 Subject: [PATCH 021/187] Fixed the assertion to please EXECUTE PROCEDURE with SUSPENDs inside --- src/jrd/exe.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index d82bf63607..8425597760 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -1087,10 +1087,11 @@ static void execute_looper(thread_db* tdbb, if (savNumber) { - // There should be no other savepoint but the one started by ourselves. - // But just in case it suddenly happened, cleanup them all. - fb_assert(transaction->tra_save_point && - transaction->tra_save_point->getNumber() == savNumber); + // Unless the looper returns after SUSPEND (this preserves existing savepoints), + // there should be no other savepoint but the one started by ourselves. + fb_assert((request->req_flags & req_stall) || + (transaction->tra_save_point && + transaction->tra_save_point->getNumber() == savNumber)); while (transaction->tra_save_point && transaction->tra_save_point->getNumber() >= savNumber) From 4f3244dbff2407ba86734c4e97e1aadc97ab5f04 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 6 Jan 2022 00:06:08 +0000 Subject: [PATCH 022/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 1385e0fa58..89514e2e53 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:365 + FORMAL BUILD NUMBER:367 */ -#define PRODUCT_VER_STRING "5.0.0.365" -#define FILE_VER_STRING "WI-T5.0.0.365" -#define LICENSE_VER_STRING "WI-T5.0.0.365" -#define FILE_VER_NUMBER 5, 0, 0, 365 +#define PRODUCT_VER_STRING "5.0.0.367" +#define FILE_VER_STRING "WI-T5.0.0.367" +#define LICENSE_VER_STRING "WI-T5.0.0.367" +#define FILE_VER_NUMBER 5, 0, 0, 367 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "365" +#define FB_BUILD_NO "367" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 3dd44d693b..21a1dae7c1 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=365 +BuildNum=367 NowAt=`pwd` cd `dirname $0` From 066785bcae507c372fe2fb2533e40c714b943744 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 6 Jan 2022 10:01:48 -0300 Subject: [PATCH 023/187] Mark nodes and stream classes as final as possible. --- src/dsql/AggNodes.h | 18 +++---- src/dsql/BoolNodes.h | 10 ++-- src/dsql/ExprNodes.h | 90 +++++++++++++++++------------------ src/dsql/StmtNodes.h | 74 ++++++++++++++-------------- src/dsql/WinNodes.h | 22 ++++----- src/jrd/ConfigTable.h | 2 +- src/jrd/ExtEngineManager.cpp | 6 +-- src/jrd/KeywordsTable.h | 2 +- src/jrd/RecordSourceNodes.h | 16 +++---- src/jrd/TimeZone.h | 2 +- src/jrd/recsrc/RecordSource.h | 22 ++++----- 11 files changed, 132 insertions(+), 132 deletions(-) diff --git a/src/dsql/AggNodes.h b/src/dsql/AggNodes.h index 2d122c41d2..ad1dae36fa 100644 --- a/src/dsql/AggNodes.h +++ b/src/dsql/AggNodes.h @@ -30,7 +30,7 @@ namespace Jrd { -class AvgAggNode : public AggNode +class AvgAggNode final : public AggNode { public: explicit AvgAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg = NULL); @@ -65,7 +65,7 @@ private: ULONG tempImpure; }; -class ListAggNode : public AggNode +class ListAggNode final : public AggNode { public: explicit ListAggNode(MemoryPool& pool, bool aDistinct, ValueExprNode* aArg = NULL, @@ -102,7 +102,7 @@ private: NestConst delimiter; }; -class CountAggNode : public AggNode +class CountAggNode final : public AggNode { public: explicit CountAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg = NULL); @@ -128,7 +128,7 @@ protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; -class SumAggNode : public AggNode +class SumAggNode final : public AggNode { public: explicit SumAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg = NULL); @@ -158,7 +158,7 @@ protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; -class MaxMinAggNode : public AggNode +class MaxMinAggNode final : public AggNode { public: enum MaxMinType @@ -192,7 +192,7 @@ public: const MaxMinType type; }; -class StdDevAggNode : public AggNode +class StdDevAggNode final : public AggNode { public: enum StdDevType @@ -244,7 +244,7 @@ private: ULONG impure2Offset; }; -class CorrAggNode : public AggNode +class CorrAggNode final : public AggNode { public: enum CorrType @@ -304,7 +304,7 @@ private: ULONG impure2Offset; }; -class RegrAggNode : public AggNode +class RegrAggNode final : public AggNode { public: enum RegrType @@ -369,7 +369,7 @@ private: ULONG impure2Offset; }; -class RegrCountAggNode : public AggNode +class RegrCountAggNode final : public AggNode { public: explicit RegrCountAggNode(MemoryPool& pool, diff --git a/src/dsql/BoolNodes.h b/src/dsql/BoolNodes.h index 9235082ef3..cbdd6cfc43 100644 --- a/src/dsql/BoolNodes.h +++ b/src/dsql/BoolNodes.h @@ -31,7 +31,7 @@ namespace Jrd { class SubQuery; -class BinaryBoolNode : public TypedNode +class BinaryBoolNode final : public TypedNode { public: BinaryBoolNode(MemoryPool& pool, UCHAR aBlrOp, BoolExprNode* aArg1 = NULL, @@ -67,7 +67,7 @@ public: }; -class ComparativeBoolNode : public TypedNode +class ComparativeBoolNode final : public TypedNode { public: enum DsqlFlag : UCHAR @@ -126,7 +126,7 @@ public: }; -class MissingBoolNode : public TypedNode +class MissingBoolNode final : public TypedNode { public: explicit MissingBoolNode(MemoryPool& pool, ValueExprNode* aArg = NULL, bool aDsqlUnknown = false); @@ -159,7 +159,7 @@ public: }; -class NotBoolNode : public TypedNode +class NotBoolNode final : public TypedNode { public: explicit NotBoolNode(MemoryPool& pool, BoolExprNode* aArg = NULL); @@ -193,7 +193,7 @@ public: }; -class RseBoolNode : public TypedNode +class RseBoolNode final : public TypedNode { public: RseBoolNode(MemoryPool& pool, UCHAR aBlrOp, RecordSourceNode* aDsqlRse = NULL); diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index ba81a90344..b3d2b13212 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -40,7 +40,7 @@ class RelationSourceNode; class ValueListNode; -class ArithmeticNode : public TypedNode +class ArithmeticNode final : public TypedNode { public: ArithmeticNode(MemoryPool& pool, UCHAR aBlrOp, bool aDialect1, @@ -124,7 +124,7 @@ public: }; -class ArrayNode : public TypedNode +class ArrayNode final : public TypedNode { public: ArrayNode(MemoryPool& pool, FieldNode* aField); @@ -171,7 +171,7 @@ public: }; -class AtNode : public TypedNode +class AtNode final : public TypedNode { public: AtNode(MemoryPool& pool, ValueExprNode* aDateTimeArg = NULL, ValueExprNode* aZoneArg = NULL); @@ -204,7 +204,7 @@ public: }; -class BoolAsValueNode : public TypedNode +class BoolAsValueNode final : public TypedNode { public: explicit BoolAsValueNode(MemoryPool& pool, BoolExprNode* aBoolean = NULL); @@ -237,7 +237,7 @@ public: }; -class CastNode : public TypedNode +class CastNode final : public TypedNode { public: explicit CastNode(MemoryPool& pool, ValueExprNode* aSource = NULL, dsql_fld* aDsqlField = NULL); @@ -281,7 +281,7 @@ public: }; -class CoalesceNode : public TypedNode +class CoalesceNode final : public TypedNode { public: explicit CoalesceNode(MemoryPool& pool, ValueListNode* aArgs = NULL) @@ -322,7 +322,7 @@ public: }; -class CollateNode : public TypedNode +class CollateNode final : public TypedNode { public: CollateNode(MemoryPool& pool, ValueExprNode* aArg, const MetaName& aCollation); @@ -384,7 +384,7 @@ public: }; -class ConcatenateNode : public TypedNode +class ConcatenateNode final : public TypedNode { public: explicit ConcatenateNode(MemoryPool& pool, ValueExprNode* aArg1 = NULL, ValueExprNode* aArg2 = NULL); @@ -417,7 +417,7 @@ public: }; -class CurrentDateNode : public TypedNode +class CurrentDateNode final : public TypedNode { public: explicit CurrentDateNode(MemoryPool& pool) @@ -439,7 +439,7 @@ public: }; -class CurrentTimeNode : public TypedNode +class CurrentTimeNode final : public TypedNode { public: CurrentTimeNode(MemoryPool& pool, unsigned aPrecision) @@ -466,7 +466,7 @@ public: }; -class CurrentTimeStampNode : public TypedNode +class CurrentTimeStampNode final : public TypedNode { public: CurrentTimeStampNode(MemoryPool& pool, unsigned aPrecision) @@ -493,7 +493,7 @@ public: }; -class CurrentRoleNode : public TypedNode +class CurrentRoleNode final : public TypedNode { public: explicit CurrentRoleNode(MemoryPool& pool) @@ -516,7 +516,7 @@ public: }; -class CurrentUserNode : public TypedNode +class CurrentUserNode final : public TypedNode { public: explicit CurrentUserNode(MemoryPool& pool) @@ -539,7 +539,7 @@ public: }; -class DecodeNode : public TypedNode +class DecodeNode final : public TypedNode { public: explicit DecodeNode(MemoryPool& pool, ValueExprNode* aTest = NULL, @@ -615,7 +615,7 @@ private: }; -class DerivedExprNode : public TypedNode +class DerivedExprNode final : public TypedNode { public: explicit DerivedExprNode(MemoryPool& pool) @@ -682,7 +682,7 @@ public: }; -class DomainValidationNode : public TypedNode +class DomainValidationNode final : public TypedNode { public: explicit DomainValidationNode(MemoryPool& pool) @@ -711,7 +711,7 @@ public: }; -class ExtractNode : public TypedNode +class ExtractNode final : public TypedNode { public: ExtractNode(MemoryPool& pool, UCHAR aBlrSubOp, ValueExprNode* aArg = NULL); @@ -745,7 +745,7 @@ public: }; -class FieldNode : public TypedNode +class FieldNode final : public TypedNode { public: FieldNode(MemoryPool& pool, dsql_ctx* context = NULL, dsql_fld* field = NULL, ValueListNode* indices = NULL); @@ -823,7 +823,7 @@ public: }; -class GenIdNode : public TypedNode +class GenIdNode final : public TypedNode { public: GenIdNode(MemoryPool& pool, bool aDialect1, @@ -868,7 +868,7 @@ private: }; -class InternalInfoNode : public TypedNode +class InternalInfoNode final : public TypedNode { public: struct InfoAttr @@ -905,7 +905,7 @@ public: }; -class LiteralNode : public TypedNode +class LiteralNode final : public TypedNode { public: explicit LiteralNode(MemoryPool& pool); @@ -952,7 +952,7 @@ public: }; -class DsqlAliasNode : public TypedNode +class DsqlAliasNode final : public TypedNode { public: DsqlAliasNode(MemoryPool& pool, const MetaName& aName, ValueExprNode* aValue) @@ -1005,7 +1005,7 @@ public: }; -class DsqlMapNode : public TypedNode +class DsqlMapNode final : public TypedNode { public: DsqlMapNode(MemoryPool& pool, dsql_ctx* aContext, dsql_map* aMap); @@ -1055,7 +1055,7 @@ public: }; -class DerivedFieldNode : public TypedNode +class DerivedFieldNode final : public TypedNode { public: DerivedFieldNode(MemoryPool& pool, const MetaName& aName, USHORT aScope, @@ -1131,7 +1131,7 @@ public: }; -class LocalTimeNode : public TypedNode +class LocalTimeNode final : public TypedNode { public: LocalTimeNode(MemoryPool& pool, unsigned aPrecision) @@ -1158,7 +1158,7 @@ public: }; -class LocalTimeStampNode : public TypedNode +class LocalTimeStampNode final : public TypedNode { public: LocalTimeStampNode(MemoryPool& pool, unsigned aPrecision) @@ -1185,7 +1185,7 @@ public: }; -class NegateNode : public TypedNode +class NegateNode final : public TypedNode { public: explicit NegateNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -1216,7 +1216,7 @@ public: }; -class NullNode : public TypedNode +class NullNode final : public TypedNode { private: friend class Firebird::GlobalPtr; @@ -1288,7 +1288,7 @@ class WindowClause : public DsqlNode public: // ListExprNode has no relation with this but works perfectly here for now. - class Frame : public TypedNode + class Frame final : public TypedNode { public: enum class Bound : UCHAR @@ -1360,7 +1360,7 @@ public: NestConst value; }; - class FrameExtent : public TypedNode + class FrameExtent final : public TypedNode { public: enum class Unit : UCHAR @@ -1521,7 +1521,7 @@ public: // OVER is used only in DSQL. In the engine, normal aggregate functions are used in partitioned // maps. -class OverNode : public TypedNode +class OverNode final : public TypedNode { public: explicit OverNode(MemoryPool& pool, AggNode* aAggExpr, const MetaName* aWindowName); @@ -1562,7 +1562,7 @@ public: }; -class ParameterNode : public TypedNode +class ParameterNode final : public TypedNode { private: // CVC: This is a guess for the length of the parameter for LIKE and others, when the @@ -1615,7 +1615,7 @@ public: }; -class RecordKeyNode : public TypedNode +class RecordKeyNode final : public TypedNode { public: RecordKeyNode(MemoryPool& pool, UCHAR aBlrOp, const MetaName& aDsqlQualifier = NULL); @@ -1693,7 +1693,7 @@ public: }; -class ScalarNode : public TypedNode +class ScalarNode final : public TypedNode { public: explicit ScalarNode(MemoryPool& pool) @@ -1751,7 +1751,7 @@ public: }; -class StmtExprNode : public TypedNode +class StmtExprNode final : public TypedNode { public: explicit StmtExprNode(MemoryPool& pool) @@ -1810,7 +1810,7 @@ public: }; -class StrCaseNode : public TypedNode +class StrCaseNode final : public TypedNode { public: StrCaseNode(MemoryPool& pool, UCHAR aBlrOp, ValueExprNode* aArg = NULL); @@ -1844,7 +1844,7 @@ public: }; -class StrLenNode : public TypedNode +class StrLenNode final : public TypedNode { public: StrLenNode(MemoryPool& pool, UCHAR aBlrSubOp, ValueExprNode* aArg = NULL); @@ -1879,7 +1879,7 @@ public: // This node is used for DSQL subqueries and for legacy (BLR-only) functionality. -class SubQueryNode : public TypedNode +class SubQueryNode final : public TypedNode { public: explicit SubQueryNode(MemoryPool& pool, UCHAR aBlrOp, RecordSourceNode* aDsqlRse = NULL, @@ -1937,7 +1937,7 @@ public: }; -class SubstringNode : public TypedNode +class SubstringNode final : public TypedNode { public: explicit SubstringNode(MemoryPool& pool, ValueExprNode* aExpr = NULL, @@ -1977,7 +1977,7 @@ public: }; -class SubstringSimilarNode : public TypedNode +class SubstringSimilarNode final : public TypedNode { public: explicit SubstringSimilarNode(MemoryPool& pool, ValueExprNode* aExpr = NULL, @@ -2015,7 +2015,7 @@ public: }; -class SysFuncCallNode : public TypedNode +class SysFuncCallNode final : public TypedNode { public: explicit SysFuncCallNode(MemoryPool& pool, const MetaName& aName, @@ -2050,7 +2050,7 @@ public: }; -class TrimNode : public TypedNode +class TrimNode final : public TypedNode { public: explicit TrimNode(MemoryPool& pool, UCHAR aWhere, @@ -2088,7 +2088,7 @@ public: }; -class UdfCallNode : public TypedNode +class UdfCallNode final : public TypedNode { private: struct Impure @@ -2139,7 +2139,7 @@ private: }; -class ValueIfNode : public TypedNode +class ValueIfNode final : public TypedNode { public: explicit ValueIfNode(MemoryPool& pool, BoolExprNode* aCondition = NULL, @@ -2182,7 +2182,7 @@ public: }; -class VariableNode : public TypedNode +class VariableNode final : public TypedNode { public: explicit VariableNode(MemoryPool& pool); diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index a3bca654ea..c9fe09b141 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -119,7 +119,7 @@ enum OverrideClause : UCHAR }; -class AssignmentNode : public TypedNode +class AssignmentNode final : public TypedNode { public: explicit AssignmentNode(MemoryPool& pool) @@ -153,7 +153,7 @@ public: }; -class BlockNode : public TypedNode +class BlockNode final : public TypedNode { public: explicit BlockNode(MemoryPool& pool) @@ -209,7 +209,7 @@ public: }; -class ContinueLeaveNode : public TypedNode +class ContinueLeaveNode final : public TypedNode { public: explicit ContinueLeaveNode(MemoryPool& pool, UCHAR aBlrOp) @@ -247,7 +247,7 @@ public: }; -class CursorStmtNode : public TypedNode +class CursorStmtNode final : public TypedNode { public: explicit CursorStmtNode(MemoryPool& pool, UCHAR aCursorOp, const MetaName& aDsqlName = "", @@ -284,7 +284,7 @@ public: }; -class DeclareCursorNode : public TypedNode +class DeclareCursorNode final : public TypedNode { public: static const USHORT CUR_TYPE_NONE = 0; @@ -328,7 +328,7 @@ public: }; -class DeclareLocalTableNode : public TypedNode +class DeclareLocalTableNode final : public TypedNode { public: struct Impure @@ -367,7 +367,7 @@ public: }; -class DeclareSubFuncNode : public TypedNode +class DeclareSubFuncNode final : public TypedNode { public: explicit DeclareSubFuncNode(MemoryPool& pool, const MetaName& aName) @@ -421,7 +421,7 @@ public: }; -class DeclareSubProcNode : public TypedNode +class DeclareSubProcNode final : public TypedNode { public: explicit DeclareSubProcNode(MemoryPool& pool, const MetaName& aName) @@ -473,7 +473,7 @@ public: }; -class DeclareVariableNode : public TypedNode +class DeclareVariableNode final : public TypedNode { public: explicit DeclareVariableNode(MemoryPool& pool) @@ -502,7 +502,7 @@ public: }; -class EraseNode : public TypedNode +class EraseNode final : public TypedNode { public: explicit EraseNode(MemoryPool& pool) @@ -555,7 +555,7 @@ public: }; -class ErrorHandlerNode : public TypedNode +class ErrorHandlerNode final : public TypedNode { public: explicit ErrorHandlerNode(MemoryPool& pool) @@ -581,7 +581,7 @@ public: }; -class ExecProcedureNode : public TypedNode +class ExecProcedureNode final : public TypedNode { public: explicit ExecProcedureNode(MemoryPool& pool, @@ -627,7 +627,7 @@ public: }; -class ExecStatementNode : public TypedNode +class ExecStatementNode final : public TypedNode { public: explicit ExecStatementNode(MemoryPool& pool) @@ -683,7 +683,7 @@ public: }; -class IfNode : public TypedNode +class IfNode final : public TypedNode { public: explicit IfNode(MemoryPool& pool) @@ -711,7 +711,7 @@ public: }; -class InAutonomousTransactionNode : public TypedNode +class InAutonomousTransactionNode final : public TypedNode { struct Impure { @@ -741,7 +741,7 @@ public: }; -class InitVariableNode : public TypedNode +class InitVariableNode final : public TypedNode { public: explicit InitVariableNode(MemoryPool& pool) @@ -770,7 +770,7 @@ public: }; -class ExecBlockNode : public TypedNode +class ExecBlockNode final : public TypedNode { public: explicit ExecBlockNode(MemoryPool& pool) @@ -797,7 +797,7 @@ public: }; -class ExceptionNode : public TypedNode +class ExceptionNode final : public TypedNode { public: ExceptionNode(MemoryPool& pool, const MetaName& name, @@ -839,7 +839,7 @@ public: }; -class ExitNode : public TypedNode +class ExitNode final : public TypedNode { public: explicit ExitNode(MemoryPool& pool) @@ -853,7 +853,7 @@ public: }; -class ForNode : public TypedNode +class ForNode final : public TypedNode { public: explicit ForNode(MemoryPool& pool) @@ -919,7 +919,7 @@ public: }; -class HandlerNode : public TypedNode +class HandlerNode final : public TypedNode { public: explicit HandlerNode(MemoryPool& pool) @@ -943,7 +943,7 @@ public: }; -class LabelNode : public TypedNode +class LabelNode final : public TypedNode { public: explicit LabelNode(MemoryPool& pool) @@ -969,7 +969,7 @@ public: }; -class LineColumnNode : public TypedNode +class LineColumnNode final : public TypedNode { public: explicit LineColumnNode(MemoryPool& pool, ULONG aLine, ULONG aColumn, StmtNode* aStatement) @@ -990,7 +990,7 @@ private: }; -class LoopNode : public TypedNode +class LoopNode final : public TypedNode { public: explicit LoopNode(MemoryPool& pool) @@ -1020,7 +1020,7 @@ public: }; -class MergeNode : public TypedNode +class MergeNode final : public TypedNode { public: struct Matched @@ -1122,7 +1122,7 @@ public: }; -class ModifyNode : public TypedNode +class ModifyNode final : public TypedNode { public: explicit ModifyNode(MemoryPool& pool) @@ -1171,7 +1171,7 @@ public: }; -class PostEventNode : public TypedNode +class PostEventNode final : public TypedNode { public: explicit PostEventNode(MemoryPool& pool) @@ -1197,7 +1197,7 @@ public: }; -class ReceiveNode : public TypedNode +class ReceiveNode final : public TypedNode { public: explicit ReceiveNode(MemoryPool& pool) @@ -1225,7 +1225,7 @@ public: }; -class StoreNode : public TypedNode +class StoreNode final : public TypedNode { public: explicit StoreNode(MemoryPool& pool) @@ -1268,7 +1268,7 @@ public: }; -class SelectNode : public TypedNode +class SelectNode final : public TypedNode { public: explicit SelectNode(MemoryPool& pool) @@ -1301,7 +1301,7 @@ public: // This is only for GPRE's cmp_set_generator(). -class SetGeneratorNode : public TypedNode +class SetGeneratorNode final : public TypedNode { public: SetGeneratorNode(MemoryPool& pool, const MetaName& name, ValueExprNode* aValue = NULL) @@ -1331,7 +1331,7 @@ public: }; -class StallNode : public TypedNode +class StallNode final : public TypedNode { public: explicit StallNode(MemoryPool& pool) @@ -1377,7 +1377,7 @@ public: }; -class ReturnNode : public TypedNode +class ReturnNode final : public TypedNode { public: explicit ReturnNode(MemoryPool& pool, ValueExprNode* val = NULL) @@ -1395,7 +1395,7 @@ public: }; -class SavepointEncloseNode : public TypedNode +class SavepointEncloseNode final : public TypedNode { public: explicit SavepointEncloseNode(MemoryPool& pool, StmtNode* stmt) @@ -1423,7 +1423,7 @@ public: }; -class SessionManagementWrapperNode : public TypedNode +class SessionManagementWrapperNode final : public TypedNode { public: explicit SessionManagementWrapperNode(MemoryPool& pool, SessionManagementNode* aWrapped, @@ -1830,7 +1830,7 @@ public: }; -class TruncateLocalTableNode : public TypedNode +class TruncateLocalTableNode final : public TypedNode { public: explicit TruncateLocalTableNode(MemoryPool& pool) @@ -1868,7 +1868,7 @@ public: }; -class UpdateOrInsertNode : public TypedNode +class UpdateOrInsertNode final : public TypedNode { public: explicit UpdateOrInsertNode(MemoryPool& pool) diff --git a/src/dsql/WinNodes.h b/src/dsql/WinNodes.h index 54f584d6f3..918f82cd5d 100644 --- a/src/dsql/WinNodes.h +++ b/src/dsql/WinNodes.h @@ -31,7 +31,7 @@ namespace Jrd { // DENSE_RANK function. -class DenseRankWinNode : public WinFuncNode +class DenseRankWinNode final : public WinFuncNode { public: explicit DenseRankWinNode(MemoryPool& pool); @@ -60,7 +60,7 @@ protected: }; // RANK function. -class RankWinNode : public WinFuncNode +class RankWinNode final : public WinFuncNode { public: explicit RankWinNode(MemoryPool& pool); @@ -93,7 +93,7 @@ private: }; // PERCENT_RANK function. -class PercentRankWinNode : public WinFuncNode +class PercentRankWinNode final : public WinFuncNode { public: explicit PercentRankWinNode(MemoryPool& pool); @@ -128,7 +128,7 @@ private: }; // CUME_DIST function. -class CumeDistWinNode : public WinFuncNode +class CumeDistWinNode final : public WinFuncNode { public: explicit CumeDistWinNode(MemoryPool& pool); @@ -158,7 +158,7 @@ protected: }; // ROW_NUMBER function. -class RowNumberWinNode : public WinFuncNode +class RowNumberWinNode final : public WinFuncNode { public: explicit RowNumberWinNode(MemoryPool& pool); @@ -187,7 +187,7 @@ protected: }; // FIRST_VALUE function. -class FirstValueWinNode : public WinFuncNode +class FirstValueWinNode final : public WinFuncNode { public: explicit FirstValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -213,7 +213,7 @@ protected: }; // LAST_VALUE function. -class LastValueWinNode : public WinFuncNode +class LastValueWinNode final : public WinFuncNode { public: explicit LastValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -239,7 +239,7 @@ protected: }; // NTH_VALUE function. -class NthValueWinNode : public WinFuncNode +class NthValueWinNode final : public WinFuncNode { public: enum @@ -319,7 +319,7 @@ protected: }; // LAG function. -class LagWinNode : public LagLeadWinNode +class LagWinNode final : public LagLeadWinNode { public: explicit LagWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, @@ -338,7 +338,7 @@ protected: }; // LEAD function. -class LeadWinNode : public LagLeadWinNode +class LeadWinNode final : public LagLeadWinNode { public: explicit LeadWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, @@ -357,7 +357,7 @@ protected: }; // NTILE function. -class NTileWinNode : public WinFuncNode +class NTileWinNode final : public WinFuncNode { public: explicit NTileWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL); diff --git a/src/jrd/ConfigTable.h b/src/jrd/ConfigTable.h index 24df4bfd1f..61601b7fde 100644 --- a/src/jrd/ConfigTable.h +++ b/src/jrd/ConfigTable.h @@ -45,7 +45,7 @@ private: }; -class ConfigTableScan : public VirtualTableScan +class ConfigTableScan final : public VirtualTableScan { public: ConfigTableScan(CompilerScratch* csb, const Firebird::string& alias, diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 93a0cefa01..9016d5f90b 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -152,7 +152,7 @@ namespace // Initialize output parameters with their domains default value or NULL. // Kind of blr_init_variable, but for parameters. - class InitParameterNode : public TypedNode + class InitParameterNode final : public TypedNode { public: InitParameterNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, @@ -421,7 +421,7 @@ namespace }; // External trigger node. - class ExtTriggerNode : public TypedNode + class ExtTriggerNode final : public TypedNode { public: ExtTriggerNode(MemoryPool& pool, const ExtEngineManager::Trigger* aTrigger) @@ -1247,7 +1247,7 @@ void ExtEngineManager::closeAttachment(thread_db* tdbb, Attachment* attachment) FbLocalStatus status; engine->closeAttachment(&status, attInfo->context); //// FIXME: log status - // Check whether the engine is used by other attachments. + // Check whether the engine is used by other attachments. // If no one uses, release it. bool close = true; WriteLockGuard writeGuard(enginesLock, FB_FUNCTION); diff --git a/src/jrd/KeywordsTable.h b/src/jrd/KeywordsTable.h index 06ab0e094c..0aeb424a13 100644 --- a/src/jrd/KeywordsTable.h +++ b/src/jrd/KeywordsTable.h @@ -45,7 +45,7 @@ public: }; -class KeywordsTableScan : public VirtualTableScan +class KeywordsTableScan final : public VirtualTableScan { public: KeywordsTableScan(CompilerScratch* csb, const Firebird::string& alias, diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index d45297bd37..a564b88121 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -283,7 +283,7 @@ public: }; -class LocalTableSourceNode : public TypedNode +class LocalTableSourceNode final : public TypedNode { public: explicit LocalTableSourceNode(MemoryPool& pool, const MetaName& aDsqlName = NULL) @@ -352,7 +352,7 @@ public: SSHORT context = 0; // user-specified context number for the local table reference }; -class RelationSourceNode : public TypedNode +class RelationSourceNode final : public TypedNode { public: explicit RelationSourceNode(MemoryPool& pool, const MetaName& aDsqlName = NULL) @@ -428,7 +428,7 @@ public: SSHORT context; // user-specified context number for the relation reference }; -class ProcedureSourceNode : public TypedNode +class ProcedureSourceNode final : public TypedNode { public: explicit ProcedureSourceNode(MemoryPool& pool, @@ -524,7 +524,7 @@ private: bool isSubRoutine; }; -class AggregateSourceNode : public TypedNode +class AggregateSourceNode final : public TypedNode { public: explicit AggregateSourceNode(MemoryPool& pool) @@ -589,7 +589,7 @@ public: bool dsqlWindow; }; -class UnionSourceNode : public TypedNode +class UnionSourceNode final : public TypedNode { public: explicit UnionSourceNode(MemoryPool& pool) @@ -654,7 +654,7 @@ public: bool recursive; // union node is a recursive union }; -class WindowSourceNode : public TypedNode +class WindowSourceNode final : public TypedNode { public: struct Window @@ -722,7 +722,7 @@ private: Firebird::ObjectsArray windows; }; -class RseNode : public TypedNode +class RseNode final : public TypedNode { public: static const USHORT FLAG_VARIANT = 0x01; // variant (not invariant?) @@ -871,7 +871,7 @@ public: bool dsqlExplicitJoin; }; -class SelectExprNode : public TypedNode +class SelectExprNode final : public TypedNode { public: explicit SelectExprNode(MemoryPool& pool) diff --git a/src/jrd/TimeZone.h b/src/jrd/TimeZone.h index b734733b7b..4c6c8eafaf 100644 --- a/src/jrd/TimeZone.h +++ b/src/jrd/TimeZone.h @@ -43,7 +43,7 @@ public: TimeZoneSnapshot(thread_db* tdbb, MemoryPool& pool); }; -class TimeZonesTableScan : public VirtualTableScan +class TimeZonesTableScan final : public VirtualTableScan { public: TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, jrd_rel* relation); diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 83b1b7777b..4a28f85dda 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -144,7 +144,7 @@ namespace Jrd // Primary (table scan) access methods - class FullTableScan : public RecordStream + class FullTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -171,7 +171,7 @@ namespace Jrd Firebird::Array m_dbkeyRanges; }; - class BitmapTableScan : public RecordStream + class BitmapTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -196,7 +196,7 @@ namespace Jrd NestConst const m_inversion; }; - class IndexTableScan : public RecordStream + class IndexTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -251,7 +251,7 @@ namespace Jrd FB_SIZE_T m_offset; }; - class ExternalTableScan : public RecordStream + class ExternalTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -303,7 +303,7 @@ namespace Jrd const Firebird::string m_alias; }; - class ProcedureScan : public RecordStream + class ProcedureScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -775,7 +775,7 @@ namespace Jrd bool m_oneRowWhenEmpty; }; - class AggregatedStream : public BaseAggWinStream + class AggregatedStream final : public BaseAggWinStream { public: AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream, @@ -793,7 +793,7 @@ namespace Jrd using FrameExtent = WindowClause::FrameExtent; using Exclusion = WindowClause::Exclusion; - class WindowStream : public BaseAggWinStream + class WindowStream final : public BaseAggWinStream { private: struct AdjustFunctor @@ -830,7 +830,7 @@ namespace Jrd }; public: - struct Impure : public BaseAggWinStream::Impure + struct Impure final : public BaseAggWinStream::Impure { impure_value* orderValues; SINT64 partitionPending, rangePending; @@ -1148,7 +1148,7 @@ namespace Jrd Firebird::Array m_keys; }; - class LocalTableStream : public RecordStream + class LocalTableStream final : public RecordStream { public: LocalTableStream(CompilerScratch* csb, StreamType stream, const DeclareLocalTableNode* table); @@ -1166,7 +1166,7 @@ namespace Jrd const DeclareLocalTableNode* m_table; }; - class Union : public RecordStream + class Union final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -1198,7 +1198,7 @@ namespace Jrd StreamList m_streams; }; - class RecursiveStream : public RecordStream + class RecursiveStream final : public RecordStream { static const FB_SIZE_T MAX_RECURSE_LEVEL = 1024; From 2d2b2a195a0f693447a1b9d11a24926678ce5425 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 6 Jan 2022 10:10:56 -0300 Subject: [PATCH 024/187] Replace FB_FINAL by final. --- src/auth/AuthDbg.h | 4 ++-- src/auth/SecDbCache.h | 2 +- .../SecureRemotePassword/client/SrpClient.cpp | 2 +- .../manage/SrpManagement.cpp | 2 +- .../SecureRemotePassword/server/SrpServer.cpp | 2 +- src/auth/SecurityDatabase/LegacyClient.h | 2 +- src/auth/SecurityDatabase/LegacyManagement.h | 2 +- src/auth/SecurityDatabase/LegacyServer.cpp | 2 +- src/burp/mvol.cpp | 2 +- src/common/MsgMetadata.h | 2 +- src/common/classes/BatchCompletionState.h | 2 +- src/common/classes/Hash.h | 12 +++++----- src/common/classes/ImplementHelper.h | 2 +- src/common/common.h | 15 ------------ src/common/config/config.h | 8 +++---- src/common/security.h | 4 ++-- src/jrd/CryptoManager.h | 4 ++-- src/jrd/Database.h | 2 +- src/jrd/EngineInterface.h | 22 ++++++++--------- src/jrd/ExtEngineManager.h | 4 ++-- src/jrd/Mapping.cpp | 2 +- src/jrd/Monitoring.h | 2 +- src/jrd/extds/ExtDS.h | 2 +- src/jrd/extds/ValidatePassword.cpp | 6 ++--- src/jrd/jrd.h | 2 +- src/jrd/trace/TraceConfigStorage.h | 10 ++++---- src/jrd/trace/TraceObjects.cpp | 2 +- src/lock/print.cpp | 2 +- src/plugins/crypt/arc4/Arc4.cpp | 2 +- src/plugins/crypt/chacha/ChaCha.cpp | 2 +- src/remote/client/interface.cpp | 20 ++++++++-------- src/remote/remote.h | 10 ++++---- src/remote/server/server.cpp | 2 +- src/utilities/ntrace/PluginLogWriter.h | 2 +- src/utilities/ntrace/TracePluginImpl.h | 2 +- src/utilities/ntrace/traceplugin.cpp | 2 +- src/yvalve/DistributedTransaction.cpp | 2 +- src/yvalve/MasterImplementation.cpp | 4 ++-- src/yvalve/PluginManager.cpp | 10 ++++---- src/yvalve/YObjects.h | 24 +++++++++---------- src/yvalve/utl.cpp | 8 +++---- src/yvalve/why.cpp | 6 ++--- 42 files changed, 103 insertions(+), 118 deletions(-) diff --git a/src/auth/AuthDbg.h b/src/auth/AuthDbg.h index ab3c49e233..b556bd537f 100644 --- a/src/auth/AuthDbg.h +++ b/src/auth/AuthDbg.h @@ -46,7 +46,7 @@ namespace Auth { // The idea of debug plugin is to send some data from server to client, // modify them on client and return result (which becomes login name) to the server -class DebugServer FB_FINAL : +class DebugServer final : public Firebird::StdPlugin > { public: @@ -61,7 +61,7 @@ private: Firebird::RefPtr config; }; -class DebugClient FB_FINAL : +class DebugClient final : public Firebird::StdPlugin > { public: diff --git a/src/auth/SecDbCache.h b/src/auth/SecDbCache.h index c29037f463..1937e4a7ca 100644 --- a/src/auth/SecDbCache.h +++ b/src/auth/SecDbCache.h @@ -55,7 +55,7 @@ public: class PluginDatabases; -class CachedSecurityDatabase FB_FINAL +class CachedSecurityDatabase final : public Firebird::RefCntIface > { public: diff --git a/src/auth/SecureRemotePassword/client/SrpClient.cpp b/src/auth/SecureRemotePassword/client/SrpClient.cpp index 10da02f5ff..3d88446c51 100644 --- a/src/auth/SecureRemotePassword/client/SrpClient.cpp +++ b/src/auth/SecureRemotePassword/client/SrpClient.cpp @@ -59,7 +59,7 @@ protected: virtual RemotePassword* remotePasswordFactory() = 0; }; -template class SrpClientImpl FB_FINAL : public SrpClient +template class SrpClientImpl final : public SrpClient { public: explicit SrpClientImpl(IPluginConfig* ipc) diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index ce17331521..341c1f2f66 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -52,7 +52,7 @@ Firebird::GlobalPtr keys; namespace Auth { -class SrpManagement FB_FINAL : public Firebird::StdPlugin > +class SrpManagement final : public Firebird::StdPlugin > { public: explicit SrpManagement(Firebird::IPluginConfig* par) diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp index f8a7474f0b..3aafc985c3 100644 --- a/src/auth/SecureRemotePassword/server/SrpServer.cpp +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -244,7 +244,7 @@ private: }; -template class SrpServerImpl FB_FINAL : public SrpServer +template class SrpServerImpl final : public SrpServer { public: explicit SrpServerImpl(IPluginConfig* ipc) diff --git a/src/auth/SecurityDatabase/LegacyClient.h b/src/auth/SecurityDatabase/LegacyClient.h index 24c5843b27..c271f94520 100644 --- a/src/auth/SecurityDatabase/LegacyClient.h +++ b/src/auth/SecurityDatabase/LegacyClient.h @@ -35,7 +35,7 @@ namespace Auth { // Required to stop analyzing rest of plugins before first roundtrip to server // if legacy login is present in DPB -class SecurityDatabaseClient FB_FINAL : +class SecurityDatabaseClient final : public Firebird::StdPlugin > { public: diff --git a/src/auth/SecurityDatabase/LegacyManagement.h b/src/auth/SecurityDatabase/LegacyManagement.h index 5d2a309f7a..65136edb6a 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.h +++ b/src/auth/SecurityDatabase/LegacyManagement.h @@ -33,7 +33,7 @@ namespace Auth { -class SecurityDatabaseManagement FB_FINAL : +class SecurityDatabaseManagement final : public Firebird::StdPlugin > { public: diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 0c7e59a855..03715e9615 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -125,7 +125,7 @@ namespace Auth { GlobalPtr instances; -class SecurityDatabaseServer FB_FINAL : +class SecurityDatabaseServer final : public StdPlugin > { public: diff --git a/src/burp/mvol.cpp b/src/burp/mvol.cpp index a89744b001..7557d40bd8 100644 --- a/src/burp/mvol.cpp +++ b/src/burp/mvol.cpp @@ -134,7 +134,7 @@ static ULONG unzip_read_block(BurpGlobals*, UCHAR*, FB_SIZE_T); // Portion of data passed to crypt plugin const ULONG CRYPT_STEP = 256; -class DbInfo FB_FINAL : public Firebird::RefCntIface > +class DbInfo final : public Firebird::RefCntIface > { public: DbInfo(BurpGlobals* bg) diff --git a/src/common/MsgMetadata.h b/src/common/MsgMetadata.h index b194d6c433..f39307764e 100644 --- a/src/common/MsgMetadata.h +++ b/src/common/MsgMetadata.h @@ -310,7 +310,7 @@ public: RefPtr attachment; }; -class MetadataBuilder FB_FINAL : +class MetadataBuilder final : public RefCntIface > { public: diff --git a/src/common/classes/BatchCompletionState.h b/src/common/classes/BatchCompletionState.h index ab29db0959..7a6a51a953 100644 --- a/src/common/classes/BatchCompletionState.h +++ b/src/common/classes/BatchCompletionState.h @@ -34,7 +34,7 @@ namespace Firebird { virtual void transliterate(IStatus* status) = 0; }; - class BatchCompletionState FB_FINAL : + class BatchCompletionState final : public DisposeIface > { public: diff --git a/src/common/classes/Hash.h b/src/common/classes/Hash.h index a4fa774643..dbd22f02dd 100644 --- a/src/common/classes/Hash.h +++ b/src/common/classes/Hash.h @@ -355,7 +355,7 @@ namespace Firebird virtual void finish(dsc& result) = 0; }; - class WeakHashContext FB_FINAL : public HashContext + class WeakHashContext final : public HashContext { public: virtual void update(const void* data, FB_SIZE_T length); @@ -389,31 +389,31 @@ namespace Firebird UCharBuffer buffer; }; - class Md5HashContext FB_FINAL : public LibTomCryptHashContext + class Md5HashContext final : public LibTomCryptHashContext { public: Md5HashContext(MemoryPool& pool); }; - class Sha1HashContext FB_FINAL : public LibTomCryptHashContext + class Sha1HashContext final : public LibTomCryptHashContext { public: Sha1HashContext(MemoryPool& pool); }; - class Sha256HashContext FB_FINAL : public LibTomCryptHashContext + class Sha256HashContext final : public LibTomCryptHashContext { public: Sha256HashContext(MemoryPool& pool); }; - class Sha512HashContext FB_FINAL : public LibTomCryptHashContext + class Sha512HashContext final : public LibTomCryptHashContext { public: Sha512HashContext(MemoryPool& pool); }; - class Crc32HashContext FB_FINAL : public HashContext + class Crc32HashContext final : public HashContext { public: Crc32HashContext(MemoryPool& pool); diff --git a/src/common/classes/ImplementHelper.h b/src/common/classes/ImplementHelper.h index 474abc918f..5da017d1b8 100644 --- a/src/common/classes/ImplementHelper.h +++ b/src/common/classes/ImplementHelper.h @@ -293,7 +293,7 @@ public: // when yvalve is starting fb_shutdown(). This causes almost unavoidable segfault. // To avoid it this class is added - it detects spontaneous (not by PluginManager) // module unload and notifies PluginManager about this said fact. -class UnloadDetectorHelper FB_FINAL : +class UnloadDetectorHelper final : public VersionedIface > { public: diff --git a/src/common/common.h b/src/common/common.h index c73511cfa1..a3646879e7 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -880,21 +880,6 @@ void GDS_breakpoint(int); #define FB_CONST64(a) (a##LL) #endif -// Check for "final" keyword support -#ifdef CPP_11 -#define FB_FINAL final -#else -#ifdef __GNUC__ -#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || (__GNUC__ >= 5)) -#define FB_FINAL __final -#endif -#endif -// Please add support for other compilers here -#ifndef FB_FINAL -#define FB_FINAL -#endif -#endif - #define FB_UNUSED(value) do { if (value) {} } while (false) #define FB_UNUSED_VAR(value) (void) value diff --git a/src/common/config/config.h b/src/common/config/config.h index d8bcdd7577..c5b86233d1 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -65,9 +65,9 @@ type getParameterName() const; form, for world-wide (global) parameters static type getParameterName(); - should be used. Also, for world-wide parameters, values of default + should be used. Also, for world-wide parameters, values of default config instance (see getDefaultConfig()) should be used. - 5. Macros CONFIG_GET_GLOBAL_XXX and CONFIG_GET_PER_DB_XXX helps to + 5. Macros CONFIG_GET_GLOBAL_XXX and CONFIG_GET_PER_DB_XXX helps to declare and implement trivial getXXX functions and to enforce rule (4). **/ @@ -432,7 +432,7 @@ public: // CONFIG_GET_GLOBAL_XXX (CONFIG_GET_PER_DB_XXX) set of macros helps to - // create trivial static (non-static) getXXX functions. + // create trivial static (non-static) getXXX functions. // Correctness of declaration and implementation is enforced with help // of entries[XXX].is_global. @@ -627,7 +627,7 @@ public: }; // Implementation of interface to access master configuration file -class FirebirdConf FB_FINAL : +class FirebirdConf final : public RefCntIface > { public: diff --git a/src/common/security.h b/src/common/security.h index 33ad1e943a..c9ed3ca65c 100644 --- a/src/common/security.h +++ b/src/common/security.h @@ -232,7 +232,7 @@ public: IntField u, g; }; -class StackUserData FB_FINAL : public UserData +class StackUserData final : public UserData { public: void* operator new(size_t, void* memory) throw() @@ -241,7 +241,7 @@ public: } }; -class DynamicUserData FB_FINAL : public UserData +class DynamicUserData final : public UserData { public: #ifdef DEBUG_GDS_ALLOC diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index f0c3d291de..afc67c24db 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -262,7 +262,7 @@ private: static const int BIG_VALUE = 1000000; }; -class CryptoManager FB_FINAL : public Firebird::PermanentStorage, public BarSync::IBar +class CryptoManager final : public Firebird::PermanentStorage, public BarSync::IBar { public: typedef Firebird::GetPlugins Factory; @@ -327,7 +327,7 @@ private: class DbInfo; friend class DbInfo; - class DbInfo FB_FINAL : public Firebird::RefCntIface > + class DbInfo final : public Firebird::RefCntIface > { public: DbInfo(CryptoManager* cm) diff --git a/src/jrd/Database.h b/src/jrd/Database.h index 236bd4e64d..24fe561283 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -370,7 +370,7 @@ public: bool exist; }; - class Linger FB_FINAL : + class Linger final : public Firebird::RefCntIface > { public: diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 943bf4fae8..edc19703f5 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -48,7 +48,7 @@ class JStatement; class JAttachment; class JProvider; -class JBlob FB_FINAL : +class JBlob final : public Firebird::RefCntIface > { public: @@ -92,7 +92,7 @@ private: void internalClose(Firebird::CheckStatusWrapper* status); }; -class JTransaction FB_FINAL : +class JTransaction final : public Firebird::RefCntIface > { public: @@ -151,7 +151,7 @@ private: void internalDisconnect(Firebird::CheckStatusWrapper* status); }; -class JResultSet FB_FINAL : +class JResultSet final : public Firebird::RefCntIface > { public: @@ -193,7 +193,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JBatch FB_FINAL : +class JBatch final : public Firebird::RefCntIface > { public: @@ -239,7 +239,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JReplicator FB_FINAL : +class JReplicator final : public Firebird::RefCntIface > { public: @@ -274,7 +274,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JStatement FB_FINAL : +class JStatement final : public Firebird::RefCntIface > { public: @@ -325,7 +325,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JRequest FB_FINAL : +class JRequest final : public Firebird::RefCntIface > { public: @@ -365,7 +365,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JEvents FB_FINAL : public Firebird::RefCntIface > +class JEvents final : public Firebird::RefCntIface > { public: // IEvents implementation @@ -394,7 +394,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JAttachment FB_FINAL : +class JAttachment final : public Firebird::RefCntIface > { public: @@ -492,7 +492,7 @@ private: void internalDropDatabase(Firebird::CheckStatusWrapper* status); }; -class JService FB_FINAL : +class JService final : public Firebird::RefCntIface > { public: @@ -515,7 +515,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JProvider FB_FINAL : +class JProvider final : public Firebird::StdPlugin > { public: diff --git a/src/jrd/ExtEngineManager.h b/src/jrd/ExtEngineManager.h index e124cc628c..9ae59dd9a6 100644 --- a/src/jrd/ExtEngineManager.h +++ b/src/jrd/ExtEngineManager.h @@ -55,14 +55,14 @@ struct impure_value; struct record_param; -class ExtEngineManager FB_FINAL : public Firebird::PermanentStorage +class ExtEngineManager final : public Firebird::PermanentStorage { private: class AttachmentImpl; template class ContextManager; class TransactionImpl; - class RoutineMetadata FB_FINAL : + class RoutineMetadata final : public Firebird::VersionedIface >, public Firebird::PermanentStorage { diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index e4e1047c08..edc9918e93 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -636,7 +636,7 @@ public: static const ULONG FLAG_DELIVER = 0x2; }; -class MappingIpc FB_FINAL : public Firebird::IpcObject +class MappingIpc final : public Firebird::IpcObject { static const USHORT MAPPING_VERSION = 1; static const size_t DEFAULT_SIZE = 1024 * 1024; diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index 7e8d266c61..ca3e8459ee 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -244,7 +244,7 @@ struct MonitoringHeader : public Firebird::MemoryHeader }; -class MonitoringData FB_FINAL : public Firebird::PermanentStorage, public Firebird::IpcObject +class MonitoringData final : public Firebird::PermanentStorage, public Firebird::IpcObject { static const USHORT MONITOR_VERSION = 5; static const ULONG DEFAULT_SIZE = 1048576; diff --git a/src/jrd/extds/ExtDS.h b/src/jrd/extds/ExtDS.h index 0ed2120165..ff0b91e975 100644 --- a/src/jrd/extds/ExtDS.h +++ b/src/jrd/extds/ExtDS.h @@ -315,7 +315,7 @@ public: }; private: - class IdleTimer FB_FINAL : + class IdleTimer final : public Firebird::RefCntIface > { public: diff --git a/src/jrd/extds/ValidatePassword.cpp b/src/jrd/extds/ValidatePassword.cpp index 4212f8ace4..5f12bc61aa 100644 --- a/src/jrd/extds/ValidatePassword.cpp +++ b/src/jrd/extds/ValidatePassword.cpp @@ -39,7 +39,7 @@ using namespace Firebird; namespace { -class DummyCryptKey FB_FINAL : +class DummyCryptKey final : public Firebird::AutoIface > { public: @@ -66,7 +66,7 @@ public: class SBlock; -class CBlock FB_FINAL : public RefCntIface > +class CBlock final : public RefCntIface > { public: CBlock(const string& p_login, const string& p_password) @@ -119,7 +119,7 @@ private: SBlock* sBlock; }; -class SBlock FB_FINAL : public AutoIface > +class SBlock final : public AutoIface > { public: explicit SBlock(CBlock* par) diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 66c7e5206f..593fac66ac 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -369,7 +369,7 @@ const USHORT WIN_garbage_collect = 8; // scan left a page for garbage collector #ifdef USE_ITIMER -class TimeoutTimer FB_FINAL : +class TimeoutTimer final : public Firebird::RefCntIface > { public: diff --git a/src/jrd/trace/TraceConfigStorage.h b/src/jrd/trace/TraceConfigStorage.h index 19c985c785..3d623158f8 100644 --- a/src/jrd/trace/TraceConfigStorage.h +++ b/src/jrd/trace/TraceConfigStorage.h @@ -44,7 +44,7 @@ namespace Jrd { Slots are sorted by session id. Slot for new session is always last slot. When session is removed its slot is marked as unused. - Unused slot could be reused: slot itself moved at last position in slots array, + Unused slot could be reused: slot itself moved at last position in slots array, higher slots are moved down on its former place, slot data is not moved. Slot is reused with best-fit algorithm. */ @@ -78,11 +78,11 @@ struct TraceCSHeader : public Firebird::MemoryHeader Slot slots[TRACE_STORAGE_MAX_SLOTS]; }; -static_assert(sizeof(TraceCSHeader) < TraceCSHeader::TRACE_STORAGE_MIN_SIZE, +static_assert(sizeof(TraceCSHeader) < TraceCSHeader::TRACE_STORAGE_MIN_SIZE, "TraceCSHeader not fits TRACE_STORAGE_MIN_SIZE"); -class ConfigStorage FB_FINAL : public Firebird::GlobalStorage, public Firebird::IpcObject, public Firebird::Reasons +class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::IpcObject, public Firebird::Reasons { public: enum GET_FLAGS {ALL, FLAGS, AUTH}; @@ -116,7 +116,7 @@ private: void checkAudit(); - class TouchFile FB_FINAL : + class TouchFile final : public Firebird::RefCntIface > { public: @@ -166,7 +166,7 @@ private: ULONG allocSlot(ULONG slotSize); void markDeleted(TraceCSHeader::Slot * slot); - // remove unused space between slots data + // remove unused space between slots data void compact(); bool validate(); diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 64ac68d778..7b364c719f 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -496,7 +496,7 @@ const char* TraceTriggerImpl::getRelationName() /// TraceLogWriterImpl -class TraceLogWriterImpl FB_FINAL : +class TraceLogWriterImpl final : public RefCntIface > { public: diff --git a/src/lock/print.cpp b/src/lock/print.cpp index dae655264f..fa1ba8f26d 100644 --- a/src/lock/print.cpp +++ b/src/lock/print.cpp @@ -140,7 +140,7 @@ using namespace Firebird; namespace { - class sh_mem FB_FINAL : public IpcObject + class sh_mem final : public IpcObject { public: explicit sh_mem(bool p_consistency, const char* filename) diff --git a/src/plugins/crypt/arc4/Arc4.cpp b/src/plugins/crypt/arc4/Arc4.cpp index 5319d6ff46..6f46de5310 100644 --- a/src/plugins/crypt/arc4/Arc4.cpp +++ b/src/plugins/crypt/arc4/Arc4.cpp @@ -85,7 +85,7 @@ private: namespace Crypt { -class Arc4 FB_FINAL : public StdPlugin > +class Arc4 final : public StdPlugin > { public: explicit Arc4(IPluginConfig*) diff --git a/src/plugins/crypt/chacha/ChaCha.cpp b/src/plugins/crypt/chacha/ChaCha.cpp index 2748a82191..02cb9c76f7 100644 --- a/src/plugins/crypt/chacha/ChaCha.cpp +++ b/src/plugins/crypt/chacha/ChaCha.cpp @@ -89,7 +89,7 @@ private: template -class ChaCha FB_FINAL : public StdPlugin, CheckStatusWrapper> > +class ChaCha final : public StdPlugin, CheckStatusWrapper> > { public: explicit ChaCha(IPluginConfig*) diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 38091ad2c0..8d0ee89e7c 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -154,7 +154,7 @@ namespace Remote { class Attachment; class Statement; -class Blob FB_FINAL : public RefCntIface > +class Blob final : public RefCntIface > { public: // IBlob implementation @@ -204,7 +204,7 @@ int Blob::release() return 0; } -class Transaction FB_FINAL : public RefCntIface > +class Transaction final : public RefCntIface > { public: // ITransaction implementation @@ -275,7 +275,7 @@ int Transaction::release() return 0; } -class ResultSet FB_FINAL : public RefCntIface > +class ResultSet final : public RefCntIface > { public: // IResultSet implementation @@ -330,7 +330,7 @@ int ResultSet::release() return 0; } -class Batch FB_FINAL : public RefCntIface > +class Batch final : public RefCntIface > { public: static const ULONG DEFER_BATCH_LIMIT = 64; @@ -579,7 +579,7 @@ int Batch::release() return 0; } -class Replicator FB_FINAL : public RefCntIface > +class Replicator final : public RefCntIface > { public: // IReplicator implementation @@ -614,7 +614,7 @@ int Replicator::release() return 0; } -class Statement FB_FINAL : public RefCntIface > +class Statement final : public RefCntIface > { public: // IStatement implementation @@ -720,7 +720,7 @@ int Statement::release() return 0; } -class Request FB_FINAL : public RefCntIface > +class Request final : public RefCntIface > { public: // IRequest implementation @@ -770,7 +770,7 @@ int Request::release() return 0; } -class Events FB_FINAL : public RefCntIface > +class Events final : public RefCntIface > { public: // IEvents implementation @@ -814,7 +814,7 @@ int Events::release() return 0; } -class Attachment FB_FINAL : public RefCntIface > +class Attachment final : public RefCntIface > { public: // IAttachment implementation @@ -921,7 +921,7 @@ int Attachment::release() return 0; } -class Service FB_FINAL : public RefCntIface > +class Service final : public RefCntIface > { public: // IService implementation diff --git a/src/remote/remote.h b/src/remote/remote.h index befc838c65..ae398431cb 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -714,7 +714,7 @@ public: }; // CryptKey implementation -class InternalCryptKey FB_FINAL : +class InternalCryptKey final : public Firebird::VersionedIface >, public Firebird::GlobalStorage { @@ -816,7 +816,7 @@ typedef Firebird::GetPlugins AuthClientPlugins; // Representation of authentication data, visible for plugin // Transfered in format, depending upon type of the packet (phase of handshake) -class RmtAuthBlock FB_FINAL : +class RmtAuthBlock final : public Firebird::VersionedIface > { public: @@ -840,7 +840,7 @@ private: }; -class ClntAuthBlock FB_FINAL : +class ClntAuthBlock final : public Firebird::RefCntIface > { private: @@ -857,7 +857,7 @@ private: Firebird::AutoPtr remAuthBlock; //Authentication block if present unsigned nextKey; // First key to be analyzed - class ClientCrypt FB_FINAL : + class ClientCrypt final : public Firebird::VersionedIface > { public: @@ -921,7 +921,7 @@ public: // Transfered from client data in format, suitable for plugins access typedef Firebird::GetPlugins AuthServerPlugins; -class SrvAuthBlock FB_FINAL : +class SrvAuthBlock final : public Firebird::VersionedIface >, public Firebird::GlobalStorage { diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index d1e3965144..1cc35256da 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -907,7 +907,7 @@ private: GlobalPtr GlobalPortLock::mtx; #endif -class Callback FB_FINAL : public RefCntIface > +class Callback final : public RefCntIface > { public: explicit Callback(Rdb* aRdb, Rvnt* aEvent) diff --git a/src/utilities/ntrace/PluginLogWriter.h b/src/utilities/ntrace/PluginLogWriter.h index ea1be06349..da992ae584 100644 --- a/src/utilities/ntrace/PluginLogWriter.h +++ b/src/utilities/ntrace/PluginLogWriter.h @@ -53,7 +53,7 @@ struct PluginLogWriterHeader : public Firebird::MemoryHeader { }; -class PluginLogWriter FB_FINAL : +class PluginLogWriter final : public Firebird::RefCntIface >, public Firebird::IpcObject { diff --git a/src/utilities/ntrace/TracePluginImpl.h b/src/utilities/ntrace/TracePluginImpl.h index 95ebbfb2f3..2e0e337bb9 100644 --- a/src/utilities/ntrace/TracePluginImpl.h +++ b/src/utilities/ntrace/TracePluginImpl.h @@ -41,7 +41,7 @@ // Bring in off_t #include -class TracePluginImpl FB_FINAL : +class TracePluginImpl final : public Firebird::RefCntIface > { public: diff --git a/src/utilities/ntrace/traceplugin.cpp b/src/utilities/ntrace/traceplugin.cpp index 6fe0c013b4..f77cd9fe0b 100644 --- a/src/utilities/ntrace/traceplugin.cpp +++ b/src/utilities/ntrace/traceplugin.cpp @@ -32,7 +32,7 @@ #include "TraceConfiguration.h" #include "TracePluginImpl.h" -class TraceFactoryImpl FB_FINAL : +class TraceFactoryImpl final : public Firebird::StdPlugin > { public: diff --git a/src/yvalve/DistributedTransaction.cpp b/src/yvalve/DistributedTransaction.cpp index 4fb4ab06f2..b770051546 100644 --- a/src/yvalve/DistributedTransaction.cpp +++ b/src/yvalve/DistributedTransaction.cpp @@ -41,7 +41,7 @@ using namespace Why; namespace { -class DTransaction FB_FINAL : public RefCntIface > +class DTransaction final : public RefCntIface > { public: DTransaction() diff --git a/src/yvalve/MasterImplementation.cpp b/src/yvalve/MasterImplementation.cpp index e6e94b648f..db27aead27 100644 --- a/src/yvalve/MasterImplementation.cpp +++ b/src/yvalve/MasterImplementation.cpp @@ -56,7 +56,7 @@ namespace Why { // getStatus() // -class UserStatus FB_FINAL : public Firebird::DisposeIface > +class UserStatus final : public Firebird::DisposeIface > { }; @@ -145,7 +145,7 @@ FB_BOOLEAN MasterImplementation::getProcessExiting() return true; #ifdef WIN_NT - // Sometime, when user process exits not calling fb_shutdown and timer thread should + // Sometime, when user process exits not calling fb_shutdown and timer thread should // be terminated already, wait for its handle with zero timeout returns WAIT_TIMEOUT. // Usage of small non-zero timeout seems fixed such cases. diff --git a/src/yvalve/PluginManager.cpp b/src/yvalve/PluginManager.cpp index 2004c6087b..4befc582b2 100644 --- a/src/yvalve/PluginManager.cpp +++ b/src/yvalve/PluginManager.cpp @@ -113,7 +113,7 @@ namespace bool flShutdown = false; - class ConfigParameterAccess FB_FINAL : + class ConfigParameterAccess final : public RefCntIface > { public: @@ -147,7 +147,7 @@ namespace const ConfigFile::Parameter* par; }; - class ConfigAccess FB_FINAL : + class ConfigAccess final : public RefCntIface > { public: @@ -432,7 +432,7 @@ namespace // Provides most of configuration services for plugins, // except per-database configuration in databases.conf - class ConfiguredPlugin FB_FINAL : + class ConfiguredPlugin final : public RefCntIface > { public: @@ -517,7 +517,7 @@ namespace }; // Provides per-database configuration from databases.conf. - class FactoryParameter FB_FINAL : + class FactoryParameter final : public RefCntIface > { public: @@ -770,7 +770,7 @@ namespace // Provides access to plugins of given type / name. - class PluginSet FB_FINAL : public RefCntIface > + class PluginSet final : public RefCntIface > { public: // IPluginSet implementation diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index eef9fe9023..a75fa444df 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -204,7 +204,7 @@ private: typedef AtomicYPtr AtomicAttPtr; typedef AtomicYPtr AtomicTraPtr; -class YEvents FB_FINAL : +class YEvents final : public YHelper > { public: @@ -227,7 +227,7 @@ private: Firebird::AtomicCounter destroyed; }; -class YRequest FB_FINAL : +class YRequest final : public YHelper > { public: @@ -257,7 +257,7 @@ public: isc_req_handle* userHandle; }; -class YTransaction FB_FINAL : +class YTransaction final : public YHelper > { public: @@ -313,7 +313,7 @@ private: typedef Firebird::RefPtr NextTransaction; -class YBlob FB_FINAL : +class YBlob final : public YHelper > { public: @@ -341,7 +341,7 @@ public: AtomicTraPtr transaction; }; -class YResultSet FB_FINAL : +class YResultSet final : public YHelper > { public: @@ -373,7 +373,7 @@ public: YStatement* statement; }; -class YBatch FB_FINAL : +class YBatch final : public YHelper > { public: @@ -405,7 +405,7 @@ public: }; -class YReplicator FB_FINAL : +class YReplicator final : public YHelper > { public: @@ -440,7 +440,7 @@ private: bool input; }; -class YStatement FB_FINAL : +class YStatement final : public YHelper > { public: @@ -502,7 +502,7 @@ public: Firebird::Mutex enterMutex; }; -class YAttachment FB_FINAL : +class YAttachment final : public YHelper >, public EnterCount { @@ -590,7 +590,7 @@ public: Firebird::StatusHolder savedStatus; // Do not use raise() method of this class in yValve. }; -class YService FB_FINAL : +class YService final : public YHelper >, public EnterCount { @@ -623,7 +623,7 @@ private: bool utf8Connection; // Client talks to us using UTF8, else - system default charset }; -class Dispatcher FB_FINAL : +class Dispatcher final : public Firebird::StdPlugin > { public: @@ -652,7 +652,7 @@ private: Firebird::ICryptKeyCallback* cryptCallback; }; -class UtilInterface FB_FINAL : +class UtilInterface final : public Firebird::AutoIface > { // IUtil implementation diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index 096c1b8f3c..c8947cbafc 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -874,7 +874,7 @@ unsigned UtilInterface::getClientVersion() } // End-user proxy for ClumpletReader & Writer -class XpbBuilder FB_FINAL : public DisposeIface > +class XpbBuilder final : public DisposeIface > { public: XpbBuilder(unsigned kind, const unsigned char* buf, unsigned len) @@ -1217,7 +1217,7 @@ IXpbBuilder* UtilInterface::getXpbBuilder(CheckStatusWrapper* status, } } -class DecFloat16 FB_FINAL : public AutoIface > +class DecFloat16 final : public AutoIface > { public: // IDecFloat16 implementation @@ -1278,7 +1278,7 @@ IDecFloat16* UtilInterface::getDecFloat16(CheckStatusWrapper* status) return &decFloat16; } -class DecFloat34 FB_FINAL : public AutoIface > +class DecFloat34 final : public AutoIface > { public: // IDecFloat34 implementation @@ -1339,7 +1339,7 @@ IDecFloat34* UtilInterface::getDecFloat34(CheckStatusWrapper* status) return &decFloat34; } -class IfaceInt128 FB_FINAL : public AutoIface > +class IfaceInt128 final : public AutoIface > { public: // IInt128 implementation diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index e816b6b29a..89b239a2af 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -128,7 +128,7 @@ static const struct { // Class-wrapper around external SQLDA. // Can be used as local variable, but do it with care -class SQLDAMetadata FB_FINAL : +class SQLDAMetadata final : public RefCntIface > { friend class SQLDAMetadataLauncher; @@ -2800,7 +2800,7 @@ int API_ROUTINE gds__enable_subsystem(TEXT* /*subsystem*/) namespace { - class WaitCallback FB_FINAL : + class WaitCallback final : public RefCntIface > { public: @@ -2864,7 +2864,7 @@ ISC_STATUS API_ROUTINE isc_wait_for_event(ISC_STATUS* userStatus, isc_db_handle* namespace { - class QueCallback FB_FINAL : public RefCntIface > + class QueCallback final : public RefCntIface > { public: QueCallback(FPTR_EVENT_CALLBACK aAst, void* aArg) From ac200b2a008a0304a8e89dc5ebb0e91469323580 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 7 Jan 2022 00:06:06 +0000 Subject: [PATCH 025/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 89514e2e53..1ab92ab442 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:367 + FORMAL BUILD NUMBER:369 */ -#define PRODUCT_VER_STRING "5.0.0.367" -#define FILE_VER_STRING "WI-T5.0.0.367" -#define LICENSE_VER_STRING "WI-T5.0.0.367" -#define FILE_VER_NUMBER 5, 0, 0, 367 +#define PRODUCT_VER_STRING "5.0.0.369" +#define FILE_VER_STRING "WI-T5.0.0.369" +#define LICENSE_VER_STRING "WI-T5.0.0.369" +#define FILE_VER_NUMBER 5, 0, 0, 369 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "367" +#define FB_BUILD_NO "369" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 21a1dae7c1..94f358c35a 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=367 +BuildNum=369 NowAt=`pwd` cd `dirname $0` From 978c99a5ed81cdf5f847e97eb77d8fb3e21bf9d6 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 7 Jan 2022 19:21:12 +0300 Subject: [PATCH 026/187] This should fix broken port parsing in the Classic server command line --- src/remote/server/os/win32/srvr_w32.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index 9eaf7c9572..ac0546fae3 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -582,9 +582,9 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) if (*p) { TEXT buffer[32]; - const char* end = buffer + sizeof(buffer) - 1; char* pp = buffer; - while (*p && *p != ' ' && pp < end) + const char* ppend = buffer + sizeof(buffer) - 1; + while (*p && *p != ' ' && pp < ppend) *pp++ = *p++; *pp++ = '\0'; @@ -633,11 +633,8 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) const char* piend = protocol_inet + sizeof(protocol_inet) - 1; *pi++ = '/'; - while (*p && *p != ' ') - { - if (pi < piend) - *pi++ = *p; - } + while (*p && *p != ' ' && pi < piend) + *pi++ = *p++; *pi++ = '\0'; } break; From dcce18c9a43d7a0e0998f875bdf3e0feca035e56 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 8 Jan 2022 00:05:58 +0000 Subject: [PATCH 027/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 1ab92ab442..d8b7e08dfa 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:369 + FORMAL BUILD NUMBER:370 */ -#define PRODUCT_VER_STRING "5.0.0.369" -#define FILE_VER_STRING "WI-T5.0.0.369" -#define LICENSE_VER_STRING "WI-T5.0.0.369" -#define FILE_VER_NUMBER 5, 0, 0, 369 +#define PRODUCT_VER_STRING "5.0.0.370" +#define FILE_VER_STRING "WI-T5.0.0.370" +#define LICENSE_VER_STRING "WI-T5.0.0.370" +#define FILE_VER_NUMBER 5, 0, 0, 370 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "369" +#define FB_BUILD_NO "370" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 94f358c35a..422c859dbd 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=369 +BuildNum=370 NowAt=`pwd` cd `dirname $0` From 42fca9a943f60d6d22f7ec406f199749e2dc0e65 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 10 Jan 2022 14:16:44 +0300 Subject: [PATCH 028/187] One more postfix after the WNET cleanup --- src/remote/server/os/win32/srvr_w32.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index ac0546fae3..02801058e6 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -585,7 +585,17 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) char* pp = buffer; const char* ppend = buffer + sizeof(buffer) - 1; while (*p && *p != ' ' && pp < ppend) - *pp++ = *p++; + { + if (*p == '@') + { + p++; + *pp++ = '\0'; + connection_handle = (HANDLE)_atoi64(buffer); + pp = buffer; + } + else + *pp++ = *p++; + } *pp++ = '\0'; if (connection_handle == INVALID_HANDLE_VALUE) From 22f3f4ea718e7f20602f0c9fcc6d398e89fc7b38 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 11 Jan 2022 00:05:47 +0000 Subject: [PATCH 029/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index d8b7e08dfa..2820d50661 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:370 + FORMAL BUILD NUMBER:371 */ -#define PRODUCT_VER_STRING "5.0.0.370" -#define FILE_VER_STRING "WI-T5.0.0.370" -#define LICENSE_VER_STRING "WI-T5.0.0.370" -#define FILE_VER_NUMBER 5, 0, 0, 370 +#define PRODUCT_VER_STRING "5.0.0.371" +#define FILE_VER_STRING "WI-T5.0.0.371" +#define LICENSE_VER_STRING "WI-T5.0.0.371" +#define FILE_VER_NUMBER 5, 0, 0, 371 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "370" +#define FB_BUILD_NO "371" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 422c859dbd..94de816b4f 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=370 +BuildNum=371 NowAt=`pwd` cd `dirname $0` From c38234314374644aa40ee580591defe700b1ea8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Cincura=20=E2=86=B9?= Date: Fri, 14 Jan 2022 14:57:46 +0100 Subject: [PATCH 030/187] Fix typo in example. (#7100) --- examples/interfaces/12.batch_isc.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/interfaces/12.batch_isc.cpp b/examples/interfaces/12.batch_isc.cpp index db1ddd197a..09c5e90f98 100644 --- a/examples/interfaces/12.batch_isc.cpp +++ b/examples/interfaces/12.batch_isc.cpp @@ -145,7 +145,7 @@ int main() /*IProvider* prov = master->getDispatcher(); IAttachment* att = NULL;*/ IUtil* utl = master->getUtilInterface(); - IStatement* statemt = NULL; + IStatement* statement = NULL; ITransaction* tra = NULL; IBatch* batch = NULL; IBatchCompletionState* cs = NULL; @@ -185,7 +185,7 @@ int main() if (isc_dsql_prepare(st, &tr, &stmt, 0, sqlStmt1, 3, NULL)) raiseError(status, st); // and get it's interface - if (fb_get_statement_interface(st, &statemt, &stmt)) + if (fb_get_statement_interface(st, &statement, &stmt)) raiseError(status, st); // Message to store in a table @@ -202,7 +202,7 @@ int main() pb->insertInt(&status, IBatch::TAG_RECORD_COUNTS, 1); // create batch - batch = statemt->createBatch(&status, meta, + batch = statement->createBatch(&status, meta, pb->getBufferLength(&status), pb->getBuffer(&status)); // fill batch with data record by record @@ -223,8 +223,8 @@ int main() batch = NULL; // unprepare statement - statemt->release(); - statemt = NULL; + statement->release(); + statement = NULL; if (isc_dsql_free_statement(st, &stmt, DSQL_unprepare)) raiseError(status, st); @@ -237,7 +237,7 @@ int main() if (isc_dsql_prepare(st, &tr, &stmt, 0, sqlStmt2, 3, NULL)) raiseError(status, st); // and get it's interface - if (fb_get_statement_interface(st, &statemt, &stmt)) + if (fb_get_statement_interface(st, &statement, &stmt)) raiseError(status, st); // Message to store in a table @@ -255,7 +255,7 @@ int main() pb->insertInt(&status, IBatch::TAG_BLOB_POLICY, IBatch::BLOB_ID_ENGINE); // create batch - batch = statemt->createBatch(&status, meta, + batch = statement->createBatch(&status, meta, pb->getBufferLength(&status), pb->getBuffer(&status)); // create blob @@ -283,8 +283,8 @@ int main() batch = NULL; // unprepare statement - statemt->release(); - statemt = NULL; + statement->release(); + statement = NULL; if (isc_dsql_free_statement(st, &stmt, DSQL_drop)) raiseError(status, st); @@ -312,8 +312,8 @@ int main() batch->release(); if (tra) tra->release(); - if (statemt) - statemt->release(); + if (statement) + statement->release(); // close handles if not closed if (blb) From d74959983a1879603e46bbd14354a20476b04d61 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 15 Jan 2022 00:06:07 +0000 Subject: [PATCH 031/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 2820d50661..7a712d2450 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:371 + FORMAL BUILD NUMBER:372 */ -#define PRODUCT_VER_STRING "5.0.0.371" -#define FILE_VER_STRING "WI-T5.0.0.371" -#define LICENSE_VER_STRING "WI-T5.0.0.371" -#define FILE_VER_NUMBER 5, 0, 0, 371 +#define PRODUCT_VER_STRING "5.0.0.372" +#define FILE_VER_STRING "WI-T5.0.0.372" +#define LICENSE_VER_STRING "WI-T5.0.0.372" +#define FILE_VER_NUMBER 5, 0, 0, 372 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "371" +#define FB_BUILD_NO "372" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 94de816b4f..315540ba1f 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=371 +BuildNum=372 NowAt=`pwd` cd `dirname $0` From bca683b9e5db5b4a080c05c5b7d8c7976ceb0f3b Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sat, 22 Jan 2022 20:57:55 -0300 Subject: [PATCH 032/187] Fix #7108 - Firebird does not find an record when adding a foreign key. --- src/jrd/btr.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 100e3c5b25..073b143cfd 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -1241,7 +1241,8 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id // isNull = !EVL_field(relation, record, tail->idx_field, desc_ptr); - if (!isNull && desc_ptr->dsc_dtype == dtype_text) + if (!isNull && desc_ptr->dsc_dtype == dtype_text && + tail->idx_field < record->getFormat()->fmt_desc.getCount()) { // That's necessary for NO-PAD collations. INTL_adjust_text_descriptor(tdbb, desc_ptr); @@ -1280,7 +1281,8 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id key->key_nulls |= 1 << n; else { - if (desc_ptr->dsc_dtype == dtype_text) + if (desc_ptr->dsc_dtype == dtype_text && + tail->idx_field < record->getFormat()->fmt_desc.getCount()) { // That's necessary for NO-PAD collations. INTL_adjust_text_descriptor(tdbb, desc_ptr); From 06620fafcc01b6a14b6a7edb132672508081cb22 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sun, 23 Jan 2022 00:05:24 +0000 Subject: [PATCH 033/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 7a712d2450..8ecc0c7ddf 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:372 + FORMAL BUILD NUMBER:373 */ -#define PRODUCT_VER_STRING "5.0.0.372" -#define FILE_VER_STRING "WI-T5.0.0.372" -#define LICENSE_VER_STRING "WI-T5.0.0.372" -#define FILE_VER_NUMBER 5, 0, 0, 372 +#define PRODUCT_VER_STRING "5.0.0.373" +#define FILE_VER_STRING "WI-T5.0.0.373" +#define LICENSE_VER_STRING "WI-T5.0.0.373" +#define FILE_VER_NUMBER 5, 0, 0, 373 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "372" +#define FB_BUILD_NO "373" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 315540ba1f..48e9dafccb 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=372 +BuildNum=373 NowAt=`pwd` cd `dirname $0` From 0f03bc998545c7cef6d88ad11c9ca9d4feb537e1 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 24 Jan 2022 21:51:38 -0300 Subject: [PATCH 034/187] Fix #7112 - Avoid unload of plugins in MacOS due to problematic reload of them. --- src/yvalve/PluginManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/yvalve/PluginManager.cpp b/src/yvalve/PluginManager.cpp index 4befc582b2..b462ab99bd 100644 --- a/src/yvalve/PluginManager.cpp +++ b/src/yvalve/PluginManager.cpp @@ -941,6 +941,9 @@ namespace current = rc; startModule(masterInterface); current = NULL; +#ifdef DARWIN // Plugin unload disabled in MacOS - GH-7112 + rc->addRef(); +#endif return rc; } From 9475d9b9cf38a56812f93c429115050cc9300be2 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 25 Jan 2022 18:21:55 +0300 Subject: [PATCH 035/187] Fixed #7099: Incomplete op_batch_cs response with TAG_MULTIERROR --- src/remote/protocol.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index 926091c4d0..2976578008 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -997,10 +997,9 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) // Process status vectors ULONG pos = 0u; - LocalStatus to; DEB_RBATCH(fprintf(stderr, "BatRem: xdr sv %d\n", b->p_batch_vectors)); - for (unsigned i = 0; i < b->p_batch_vectors; ++i, ++pos) + for (unsigned i = 0; i < b->p_batch_vectors; ++pos) { DynamicStatusVector s; DynamicStatusVector* ptr = NULL; @@ -1012,6 +1011,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) if (pos == IBatchCompletionState::NO_MORE_ERRORS) return P_FALSE(xdrs, p); + LocalStatus to; statement->rsr_batch_ics->getStatus(&status_vector, &to, pos); if (status_vector.getState() & IStatus::STATE_ERRORS) continue; @@ -1028,17 +1028,19 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) if (xdrs->x_op == XDR_DECODE) { Firebird::Arg::StatusVector sv(ptr->value()); + LocalStatus to; sv.copyTo(&to); delete ptr; statement->rsr_batch_cs->regErrorAt(pos, &to); } + ++i; } // Process status-less errors pos = 0u; DEB_RBATCH(fprintf(stderr, "BatRem: xdr err %d\n", b->p_batch_errors)); - for (unsigned i = 0; i < b->p_batch_errors; ++i, ++pos) + for (unsigned i = 0; i < b->p_batch_errors; ++pos) { if (xdrs->x_op == XDR_ENCODE) { @@ -1047,6 +1049,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) if (pos == IBatchCompletionState::NO_MORE_ERRORS) return P_FALSE(xdrs, p); + LocalStatus to; statement->rsr_batch_ics->getStatus(&status_vector, &to, pos); if (!(status_vector.getState() & IStatus::STATE_ERRORS)) continue; @@ -1058,6 +1061,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) { statement->rsr_batch_cs->regErrorAt(pos, nullptr); } + ++i; } return P_TRUE(xdrs, p); From b2f196257cab0e0d6c68fbb4e22a430268a0fd12 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 26 Jan 2022 00:05:44 +0000 Subject: [PATCH 036/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 8ecc0c7ddf..27efcff820 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:373 + FORMAL BUILD NUMBER:375 */ -#define PRODUCT_VER_STRING "5.0.0.373" -#define FILE_VER_STRING "WI-T5.0.0.373" -#define LICENSE_VER_STRING "WI-T5.0.0.373" -#define FILE_VER_NUMBER 5, 0, 0, 373 +#define PRODUCT_VER_STRING "5.0.0.375" +#define FILE_VER_STRING "WI-T5.0.0.375" +#define LICENSE_VER_STRING "WI-T5.0.0.375" +#define FILE_VER_NUMBER 5, 0, 0, 375 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "373" +#define FB_BUILD_NO "375" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 48e9dafccb..8262ff6a8f 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=373 +BuildNum=375 NowAt=`pwd` cd `dirname $0` From efd5f6a7e1f18aef9026130d2e145bfeb27e0b20 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Wed, 26 Jan 2022 17:06:43 +0200 Subject: [PATCH 037/187] Fixed bug #7103 : FB service hangs and can not be stopped after several 'DELETE FROM MON$STATEMENTS' being issued in order to stop ES/EDS which waits record for updating. --- src/jrd/extds/InternalDS.cpp | 62 +++++++++++++++++------------------- src/jrd/extds/IscDS.cpp | 16 ++++++++++ 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 2f92d71725..0ab41a1969 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -126,24 +126,6 @@ InternalConnection::~InternalConnection() { } -// Status helper -class IntStatus : public Firebird::FbLocalStatus -{ -public: - explicit IntStatus(FbStatusVector *p) - : FbLocalStatus(), v(p) - {} - - ~IntStatus() - { - if (v) - fb_utils::copyStatus(v, &(*this)); - } - -private: - FbStatusVector *v; -}; - void InternalConnection::attach(thread_db* tdbb) { fb_assert(!m_attachment); @@ -344,10 +326,9 @@ void InternalTransaction::doStart(FbStatusVector* status, thread_db* tdbb, Clump JAttachment* att = m_IntConnection.getJrdAtt(); EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); - IntStatus s(status); m_transaction.assignRefNoIncr( - att->startTransaction(&s, tpb.getBufferLength(), tpb.getBuffer())); + att->startTransaction(status, tpb.getBufferLength(), tpb.getBuffer())); if (m_transaction) m_transaction->getHandle()->tra_callback_count = localTran->tra_callback_count; @@ -373,15 +354,13 @@ void InternalTransaction::doCommit(FbStatusVector* status, thread_db* tdbb, bool } else { - IntStatus s(status); - EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); if (retain) - m_transaction->commitRetaining(&s); + m_transaction->commitRetaining(status); else { - m_transaction->commit(&s); - if (!(s->getState() & IStatus::STATE_ERRORS)) + m_transaction->commit(status); + if (!(status->getState() & IStatus::STATE_ERRORS)) m_transaction.clear(); } } @@ -402,23 +381,40 @@ void InternalTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bo if (!retain) { m_transaction = NULL; // release and nullify } + return; } - else - { - IntStatus s(status); + ISC_STATUS err = 0; + { EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); if (retain) - m_transaction->rollbackRetaining(&s); + m_transaction->rollbackRetaining(status); else + m_transaction->rollback(status); + + if (status->getState() & IStatus::STATE_ERRORS) + err = status->getErrors()[1]; + + if (err == isc_cancelled) { - m_transaction->rollback(&s); - if (!(s->getState() & IStatus::STATE_ERRORS)) - m_transaction.clear(); + FbLocalStatus temp; + JAttachment* jAtt = m_IntConnection.getJrdAtt(); + jAtt->cancelOperation(&temp, fb_cancel_disable); + + status->init(); + if (retain) + m_transaction->rollbackRetaining(status); + else + m_transaction->rollback(status); + + err = (status->getState() & IStatus::STATE_ERRORS) ? + status->getErrors()[1] : 0; + + jAtt->cancelOperation(&temp, fb_cancel_enable); } } - if ((status->getErrors()[1] == isc_att_shutdown || status->getErrors()[1] == isc_shutdown) && !retain) + if ((!err || err == isc_att_shutdown || err == isc_shutdown) && !retain) { m_transaction.clear(); status->init(); diff --git a/src/jrd/extds/IscDS.cpp b/src/jrd/extds/IscDS.cpp index 10ab7f5c70..167428b2c6 100644 --- a/src/jrd/extds/IscDS.cpp +++ b/src/jrd/extds/IscDS.cpp @@ -396,6 +396,22 @@ void IscTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bool re else m_iscProvider.isc_rollback_transaction(status, &m_handle); + if ((status->getState() & IStatus::STATE_ERRORS) && + (status->getErrors()[1] == isc_cancelled)) + { + FbLocalStatus temp; + FB_API_HANDLE db = m_iscConnection.getAPIHandle(); + m_iscProvider.fb_cancel_operation(&temp, &db, fb_cancel_disable); + + status->init(); + if (retain) + m_iscProvider.isc_rollback_retaining(status, &m_handle); + else + m_iscProvider.isc_rollback_transaction(status, &m_handle); + + m_iscProvider.fb_cancel_operation(&temp, &db, fb_cancel_enable); + } + if ((status->getState() & IStatus::STATE_ERRORS) && isConnectionBrokenError(status) && !retain) { From 31acc4ff56e13a03516db6e125997891aa3ddaa6 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 27 Jan 2022 00:05:42 +0000 Subject: [PATCH 038/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 27efcff820..95690b625d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:375 + FORMAL BUILD NUMBER:376 */ -#define PRODUCT_VER_STRING "5.0.0.375" -#define FILE_VER_STRING "WI-T5.0.0.375" -#define LICENSE_VER_STRING "WI-T5.0.0.375" -#define FILE_VER_NUMBER 5, 0, 0, 375 +#define PRODUCT_VER_STRING "5.0.0.376" +#define FILE_VER_STRING "WI-T5.0.0.376" +#define LICENSE_VER_STRING "WI-T5.0.0.376" +#define FILE_VER_NUMBER 5, 0, 0, 376 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "375" +#define FB_BUILD_NO "376" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 8262ff6a8f..ff03221b45 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=375 +BuildNum=376 NowAt=`pwd` cd `dirname $0` From b9e10514469b58aace3d65a453ffacddd1f628a4 Mon Sep 17 00:00:00 2001 From: Jonathan Frutos <11614150+e787@users.noreply.github.com> Date: Thu, 27 Jan 2022 02:57:19 +0100 Subject: [PATCH 039/187] Add initial MacOS ARM support (#7116) Co-authored-by: Jonathan Frutos <> --- builds/posix/prefix.darwin_aarch64 | 49 ++++++++++++++++++++++++++++++ configure.ac | 14 +++++++++ src/common/common.h | 4 +++ src/isql/InputDevices.cpp | 2 ++ src/jrd/license.h | 4 +-- 5 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 builds/posix/prefix.darwin_aarch64 diff --git a/builds/posix/prefix.darwin_aarch64 b/builds/posix/prefix.darwin_aarch64 new file mode 100644 index 0000000000..e9e9ae180f --- /dev/null +++ b/builds/posix/prefix.darwin_aarch64 @@ -0,0 +1,49 @@ +# The contents of this file are subject to the Interbase Public +# License Version 1.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy +# of the License at http://www.Inprise.com/IPL.html +# +# Software distributed under the License is distributed on an +# "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express +# or implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code was created by Inprise Corporation +# and its predecessors. Portions created by Inprise Corporation are +# +# Copyright (C) 2000 Inprise Corporation +# All Rights Reserved. +# Contributor(s): ______________________________________. +# Start of file prefix.darwin: $(VERSION) @PLATFORM@ +# 2 Oct 2002, Nickolay Samofatov - Major Cleanup +# +# Default build from 10.9 using Clang +# +# Build instructions +# set CFLAGS='-I (ICUDIR)/icu/source/common' (ucnv.h) +# set LDFLAGS='-L(ICUDIR)/icu/source/lib' (-licuuc) +# set CXXFLAGS='-I (ICUDIR)/icu/source/common -I ICUDIR/icu/source/i18n' +# where ICUDIR is where you installed ICU +# configure using --with-builtin-tommath +# or add the relevant -I, -L for an installed version of libtommath + +#DYLD_PRINT_ENV=1 +#export DYLD_PRINT_ENV + +#DYLD_PRINT_LIBRARIES=1 +#export DYLD_PRINT_LIBRARIES + +MACOSX_DEPLOYMENT_TARGET=12.0 +export MACOSX_DEPLOYMENT_TARGET + +PROD_FLAGS=-DDARWIN -DARM64 -pipe -O2 -MMD -fPIC -fno-common -mmacosx-version-min=12.0 +DEV_FLAGS=-ggdb -DDARWIN -DARM64 -pipe -MMD -fPIC -fno-omit-frame-pointer -fno-common -Wall -fno-optimize-sibling-calls -mmacosx-version-min=12.0 -Wno-non-virtual-dtor +CXXFLAGS:=$(CXXFLAGS) -fvisibility-inlines-hidden -fvisibility=hidden -stdlib=libc++ + +UNDEF_PLATFORM= + +LINK_LIBS+=-liconv +#MATHLIB=$(ROOT)/extern/libtommath/.libs/libtommath.a +SO_LINK_LIBS+=-liconv + +include $(ROOT)/gen/darwin.defaults diff --git a/configure.ac b/configure.ac index e845e3cf89..ecde8c0d35 100644 --- a/configure.ac +++ b/configure.ac @@ -86,6 +86,20 @@ dnl Test for special ar options? AR_OPT_CHECK=false case "$build" in + aarch64-*-darwin*) + MAKEFILE_PREFIX=darwin_aarch64 + MAKEFILE_POSTFIX=darwin + PLATFORM=DARWIN + INSTALL_PREFIX=darwin + AC_DEFINE(DARWIN, 1, [Define this if OS is DARWIN]) + XE_APPEND(-framework CoreFoundation,LIBS) + EDITLINE_FLG=Y + SHRLIB_EXT=dylib + CPU_TYPE=ARM64 + EXPORT_SYMBOLS_STYLE=darwin + RAW_DEVICES_FLG=N + ;; + x*64-*-darwin*) MAKEFILE_PREFIX=darwin_x86_64 MAKEFILE_POSTFIX=darwin diff --git a/src/common/common.h b/src/common/common.h index a3646879e7..f58b065dcb 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -230,6 +230,10 @@ #ifdef ARM #define FB_CPU CpuArm #endif /* ARM */ +#ifdef ARM64 +#define DARWIN64 +#define FB_CPU CpuArm64 +#endif /* ARM64 */ #ifdef __ppc__ #define powerpc #define FB_CPU CpuPowerPc diff --git a/src/isql/InputDevices.cpp b/src/isql/InputDevices.cpp index 24e8ae1e08..faae0a84e7 100644 --- a/src/isql/InputDevices.cpp +++ b/src/isql/InputDevices.cpp @@ -25,6 +25,8 @@ #if defined(DARWIN) && !defined(IOS) #if defined(i386) || defined(__x86_64__) #include +#elif defined(__aarch64__) +#include #else #include #endif diff --git a/src/jrd/license.h b/src/jrd/license.h index 1fbcff04d9..c162f1d3bc 100644 --- a/src/jrd/license.h +++ b/src/jrd/license.h @@ -125,8 +125,8 @@ #if defined(__ppc__) || defined(__ppc64__) #define FB_PLATFORM "UP" // Darwin/PowerPC #endif -#if defined(ARM) -#define FB_PLATFORM "UA" +#if defined(ARM) || defined(__aarch64__) +#define FB_PLATFORM "UA" // Darwin/ARM #endif #endif From 249dbc9fd4565cae52271915e6565664e9319cdf Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 27 Jan 2022 13:25:14 +0300 Subject: [PATCH 040/187] Fixed #7113: Wrong path in object pascal readme.md --- builds/posix/Makefile.in.examples | 3 +++ configure.ac | 2 ++ examples/object_pascal/Readme.md | 5 +++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/builds/posix/Makefile.in.examples b/builds/posix/Makefile.in.examples index 9f8177b121..d0f8973830 100644 --- a/builds/posix/Makefile.in.examples +++ b/builds/posix/Makefile.in.examples @@ -124,6 +124,9 @@ $(EXAMPLES_FB)/README: $(CP) $(ROOT)/examples/stat/*.* $(EXAMPLES_FB)/stat/ $(CP) $(ROOT)/examples/udf/*.* $(EXAMPLES_FB)/udf/ $(CP) $(ROOT)/examples/udr/*.* $(EXAMPLES_FB)/udr/ + $(CP) $(ROOT)/examples/object_pascal/*.* $(EXAMPLES_FB)/object_pascal/ + $(CP) $(ROOT)/examples/object_pascal/[mM]ake* $(EXAMPLES_FB)/object_pascal/ + $(CP) $(ROOT)/examples/object_pascal/common/*.* $(EXAMPLES_FB)/object_pascal/common/ # $(CP) intlemp.fdb $(EXAMPLES_FB)/empbuild/ $(CP) $(ROOT)/examples/readme $(EXAMPLES_FB)/README $(CP) $(ROOT)/examples/empbuild/employe2.sql $(EXAMPLES_FB)/empbuild/ diff --git a/configure.ac b/configure.ac index ecde8c0d35..f6aa655f98 100644 --- a/configure.ac +++ b/configure.ac @@ -1299,6 +1299,8 @@ dnl # output mkdir -p gen/\$fb_tgt/firebird/examples/extauth mkdir -p gen/\$fb_tgt/firebird/examples/include mkdir -p gen/\$fb_tgt/firebird/examples/interfaces + mkdir -p gen/\$fb_tgt/firebird/examples/object_pascal + mkdir -p gen/\$fb_tgt/firebird/examples/object_pascal/common mkdir -p gen/\$fb_tgt/firebird/examples/package mkdir -p gen/\$fb_tgt/firebird/examples/stat mkdir -p gen/\$fb_tgt/firebird/examples/udf diff --git a/examples/object_pascal/Readme.md b/examples/object_pascal/Readme.md index 6c30083d98..8d8cbf73c0 100644 --- a/examples/object_pascal/Readme.md +++ b/examples/object_pascal/Readme.md @@ -40,9 +40,10 @@ you would do the following to create a project from 03.select.pas: - Open select.lpr as a project - When prompted choose 'Simple Program' as the project template - Go into Project options and add the following paths: - /opt/firebird/include/Firebird +``` + /usr/include/Firebird common - +``` You can then compile and run the example through the debugger. From fcf5ad3e6c6d198d6030bb0bd702e87eca1d9936 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 27 Jan 2022 14:36:15 +0300 Subject: [PATCH 041/187] Fixed #7106: Wrong detection of must-be-delimited user names --- src/common/utils.cpp | 35 ++++++++++++++++++++--------- src/include/firebird/impl/msg/jrd.h | 2 ++ src/include/gen/Firebird.pas | 2 ++ src/jrd/scl.epp | 6 ++++- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index c1076713c5..9380cedbf5 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1613,6 +1613,11 @@ bool containsErrorCode(const ISC_STATUS* v, ISC_STATUS code) return false; } +inline bool sqlSymbolChar(char c, bool first) +{ + return (isdigit(c) && !first) || isalpha(c) || c == '_' || c == '$'; +} + const char* dpbItemUpper(const char* s, FB_SIZE_T l, Firebird::string& buf) { if (l && (s[0] == '"' || s[0] == '\'')) @@ -1625,30 +1630,38 @@ const char* dpbItemUpper(const char* s, FB_SIZE_T l, Firebird::string& buf) { if (s[i] == end_quote) { - if (++i >= l || s[i] != end_quote) - break; // delimited quote, done processing + if (++i >= l) + { + if (ascii && s[0] == '\'') + buf.upper(); + + return buf.c_str(); + } + + if (s[i] != end_quote) + { + buf.assign(&s[i], l - i); + (Firebird::Arg::Gds(isc_quoted_str_bad) << buf).raise(); + } // skipped the escape quote, continue processing } - - if (s[i] & 0x80) + else if (!sqlSymbolChar(s[i], i == 1)) ascii = false; + buf += s[i]; } - if (ascii && s[0] == '\'') - buf.upper(); - - return buf.c_str(); + buf.assign(1, s[0]); + (Firebird::Arg::Gds(isc_quoted_str_miss) << buf).raise(); } // non-quoted string - try to uppercase for (FB_SIZE_T i = 0; i < l; ++i) { - if (!(s[i] & 0x80)) - buf += toupper(s[i]); - else + if (!sqlSymbolChar(s[i], i == 0)) return NULL; // contains non-ascii data + buf += toupper(s[i]); } return buf.c_str(); diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 33b6f9831f..808e2d6f56 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -956,3 +956,5 @@ FB_IMPL_MSG(JRD, 954, tom_key_length, -901, "22", "023", "Invalid key length @1, FB_IMPL_MSG(JRD, 955, inf_invalid_args, -901, "HY", "000", "Invalid information arguments") FB_IMPL_MSG(JRD, 956, sysf_invalid_null_empty, -901, "22", "023", "Empty or NULL parameter @1 is not accepted") FB_IMPL_MSG(JRD, 957, bad_loctab_num, -901, "HY", "000", "Undefined local table number @1") +FB_IMPL_MSG(JRD, 958, quoted_str_bad, -901, "22", "024", "Invalid text <@1> after quoted string") +FB_IMPL_MSG(JRD, 959, quoted_str_miss, -901, "22", "024", "Missing terminating quote <@1> in the end of quoted string") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 9b770f67b2..61d849b86e 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5151,6 +5151,8 @@ const isc_inf_invalid_args = 335545275; isc_sysf_invalid_null_empty = 335545276; isc_bad_loctab_num = 335545277; + isc_quoted_str_bad = 335545278; + isc_quoted_str_miss = 335545279; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 361589ba99..b0bed3b8f3 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -1776,7 +1776,11 @@ void UserId::makeRoleName(Firebird::MetaString& role, const int dialect) // Invoke utility twice: first to strip quotes, next to uppercase if needed // For unquoted string nothing bad happens fb_utils::dpbItemUpper(role); - fb_utils::dpbItemUpper(role); + { + Firebird::string tmp(role); + tmp.upper(); + role = tmp; + } break; case SQL_DIALECT_V6_TRANSITION: From 2d985a856a8a1e7e0b6da189cdf55feb90a87fd9 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 28 Jan 2022 00:06:00 +0000 Subject: [PATCH 042/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 95690b625d..5f589cb3c3 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:376 + FORMAL BUILD NUMBER:379 */ -#define PRODUCT_VER_STRING "5.0.0.376" -#define FILE_VER_STRING "WI-T5.0.0.376" -#define LICENSE_VER_STRING "WI-T5.0.0.376" -#define FILE_VER_NUMBER 5, 0, 0, 376 +#define PRODUCT_VER_STRING "5.0.0.379" +#define FILE_VER_STRING "WI-T5.0.0.379" +#define LICENSE_VER_STRING "WI-T5.0.0.379" +#define FILE_VER_NUMBER 5, 0, 0, 379 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "376" +#define FB_BUILD_NO "379" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index ff03221b45..a31aa9a886 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=376 +BuildNum=379 NowAt=`pwd` cd `dirname $0` From b4b7a4dd997edb8a49c2607fc59b7e09245e7430 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 28 Jan 2022 11:52:19 +0300 Subject: [PATCH 043/187] Remove incorrect assertion --- src/jrd/opt.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index d212417ed1..5b4ce226b1 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -303,8 +303,6 @@ namespace { // Ideally, we should never get here. But just in case it happened, handle it. - fb_assert(false); - for (auto& subRiver : rivers) { const auto subRsb = subRiver->getRecordSource(); From 23fa6d479a49fd44308280387c20d5295049dd18 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 28 Jan 2022 11:53:03 +0300 Subject: [PATCH 044/187] Fix the remaining part of #4085: RDB information stored inconsistently after a CREATE INDEX --- src/dsql/DdlNodes.epp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index e8f6557f9d..3be22e61e5 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -6746,6 +6746,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc definition.unique = constraint.type != Constraint::TYPE_FK; if (constraint.index->descending) definition.descending = true; + definition.inactive = false; definition.columns = constraint.columns; definition.refRelation = constraint.refRelation; definition.refColumns = constraint.refColumns; @@ -9830,6 +9831,7 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, definition.relation = relation->dsqlName; definition.unique = unique; definition.descending = descending; + definition.inactive = false; if (columns) { From 7d431406558925450510f0fce65a7a870a66d8e0 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 28 Jan 2022 16:09:02 +0300 Subject: [PATCH 045/187] Fixed #3357 (Bad execution plan if some stream depends on multiple streams via a function) and its kissing cousin #7118 (Chained JOIN .. USING across the same column names may be optimized badly) --- src/jrd/Optimizer.cpp | 109 +++++++++++++++++++----------------------- src/jrd/Optimizer.h | 2 +- 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/src/jrd/Optimizer.cpp b/src/jrd/Optimizer.cpp index 4c1cd6a94c..b2bf260624 100644 --- a/src/jrd/Optimizer.cpp +++ b/src/jrd/Optimizer.cpp @@ -2673,61 +2673,53 @@ void OptimizerInnerJoin::calculateStreamInfo() * all streams. * **************************************/ + StreamList streams; - FB_SIZE_T i = 0; - // First get the base cost without any relation to an other inner join stream. - for (i = 0; i < innerStreams.getCount(); i++) + // First get the base cost without any relation to any other inner join stream + for (auto innerStream : innerStreams) { - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[innerStreams[i]->stream]; + streams.add(innerStream->stream); + + const auto csb_tail = &csb->csb_rpt[innerStream->stream]; csb_tail->activate(); - OptimizerRetrieval optimizerRetrieval(pool, optimizer, innerStreams[i]->stream, + OptimizerRetrieval optimizerRetrieval(pool, optimizer, innerStream->stream, false, false, sort); AutoPtr candidate(optimizerRetrieval.getCost()); - innerStreams[i]->baseCost = candidate->cost; - innerStreams[i]->baseSelectivity = candidate->selectivity; - innerStreams[i]->baseIndexes = candidate->indexes; - innerStreams[i]->baseUnique = candidate->unique; - innerStreams[i]->baseNavigated = candidate->navigated; + innerStream->baseCost = candidate->cost; + innerStream->baseSelectivity = candidate->selectivity; + innerStream->baseIndexes = candidate->indexes; + innerStream->baseUnique = candidate->unique; + innerStream->baseNavigated = candidate->navigated; csb_tail->deactivate(); } - for (i = 0; i < innerStreams.getCount(); i++) - { - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[innerStreams[i]->stream]; - csb_tail->activate(); + StreamStateHolder stateHolder(csb, streams); + stateHolder.activate(); - // Find streams that have a indexed relationship to this - // stream and add the information. - for (FB_SIZE_T j = 0; j < innerStreams.getCount(); j++) - { - if (innerStreams[j]->stream != innerStreams[i]->stream) - getIndexedRelationship(innerStreams[i], innerStreams[j]); - } + // Collect stream inter-dependencies + for (const auto innerStream : innerStreams) + getIndexedRelationships(innerStream); - csb_tail->deactivate(); - } - - // Sort the streams based on independecy and cost. - // Except when a PLAN was forced. + // Unless PLAN is enforced, sort the streams based on independecy and cost if (!plan && (innerStreams.getCount() > 1)) { StreamInfoList tempStreams(pool); - for (i = 0; i < innerStreams.getCount(); i++) + for (const auto innerStream : innerStreams) { FB_SIZE_T index = 0; for (; index < tempStreams.getCount(); index++) { // First those streams which can't be used by other streams - // or can't depend on a stream. - if (innerStreams[i]->isIndependent() && !tempStreams[index]->isIndependent()) + // or can't depend on a stream + if (innerStream->isIndependent() && !tempStreams[index]->isIndependent()) break; // Next those with the lowest previous expected streams - const int compare = innerStreams[i]->previousExpectedStreams - + const int compare = innerStream->previousExpectedStreams - tempStreams[index]->previousExpectedStreams; if (compare < 0) @@ -2736,11 +2728,11 @@ void OptimizerInnerJoin::calculateStreamInfo() if (compare == 0) { // Next those with the cheapest base cost - if (innerStreams[i]->baseCost < tempStreams[index]->baseCost) + if (innerStream->baseCost < tempStreams[index]->baseCost) break; } } - tempStreams.insert(index, innerStreams[i]); + tempStreams.insert(index, innerStream); } // Finally update the innerStreams with the sorted streams @@ -3073,8 +3065,7 @@ void OptimizerInnerJoin::findBestOrder(StreamType position, InnerJoinStreamInfo* innerStreams[i]->used = streamFlags[i]; } -void OptimizerInnerJoin::getIndexedRelationship(InnerJoinStreamInfo* baseStream, - InnerJoinStreamInfo* testStream) +void OptimizerInnerJoin::getIndexedRelationships(InnerJoinStreamInfo* testStream) { /************************************** * @@ -3091,38 +3082,38 @@ void OptimizerInnerJoin::getIndexedRelationship(InnerJoinStreamInfo* baseStream, * expected stream to the testStream. * **************************************/ - - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[testStream->stream]; - csb_tail->activate(); + const auto csb_tail = &csb->csb_rpt[testStream->stream]; OptimizerRetrieval optimizerRetrieval(pool, optimizer, testStream->stream, false, false, NULL); AutoPtr candidate(optimizerRetrieval.getCost()); - if (candidate->dependentFromStreams.exist(baseStream->stream)) + for (auto baseStream : innerStreams) { - // If we could use more conjunctions on the testing stream - // with the base stream active as without the base stream - // then the test stream has a indexed relationship with the base stream. - IndexRelationship* indexRelationship = FB_NEW_POOL(pool) IndexRelationship(); - indexRelationship->stream = testStream->stream; - indexRelationship->unique = candidate->unique; - indexRelationship->cost = candidate->cost; - indexRelationship->cardinality = candidate->unique ? - csb_tail->csb_cardinality : csb_tail->csb_cardinality * candidate->selectivity; - - // indexRelationship are kept sorted on cost and unique in the indexRelations array. - // The unique and cheapest indexed relatioships are on the first position. - FB_SIZE_T index = 0; - for (; index < baseStream->indexedRelationships.getCount(); index++) + if (baseStream->stream != testStream->stream && + candidate->dependentFromStreams.exist(baseStream->stream)) { - if (cheaperRelationship(indexRelationship, baseStream->indexedRelationships[index])) - break; - } - baseStream->indexedRelationships.insert(index, indexRelationship); - testStream->previousExpectedStreams++; - } + // If we could use more conjunctions on the testing stream + // with the base stream active as without the base stream + // then the test stream has a indexed relationship with the base stream. + IndexRelationship* indexRelationship = FB_NEW_POOL(pool) IndexRelationship(); + indexRelationship->stream = testStream->stream; + indexRelationship->unique = candidate->unique; + indexRelationship->cost = candidate->cost; + indexRelationship->cardinality = candidate->unique ? + csb_tail->csb_cardinality : csb_tail->csb_cardinality * candidate->selectivity; - csb_tail->deactivate(); + // indexRelationship are kept sorted on cost and unique in the indexRelations array. + // The unique and cheapest indexed relationships are on the first position. + FB_SIZE_T index = 0; + for (; index < baseStream->indexedRelationships.getCount(); index++) + { + if (cheaperRelationship(indexRelationship, baseStream->indexedRelationships[index])) + break; + } + baseStream->indexedRelationships.insert(index, indexRelationship); + testStream->previousExpectedStreams++; + } + } } InnerJoinStreamInfo* OptimizerInnerJoin::getStreamInfo(StreamType stream) diff --git a/src/jrd/Optimizer.h b/src/jrd/Optimizer.h index 26a146291d..4c73ad4d55 100644 --- a/src/jrd/Optimizer.h +++ b/src/jrd/Optimizer.h @@ -299,7 +299,7 @@ protected: void estimateCost(StreamType stream, double* cost, double* resulting_cardinality, bool start) const; void findBestOrder(StreamType position, InnerJoinStreamInfo* stream, IndexedRelationships* processList, double cost, double cardinality); - void getIndexedRelationship(InnerJoinStreamInfo* baseStream, InnerJoinStreamInfo* testStream); + void getIndexedRelationships(InnerJoinStreamInfo* testStream); InnerJoinStreamInfo* getStreamInfo(StreamType stream); #ifdef OPT_DEBUG void printBestOrder() const; From eca0a27dc6d54155dcf77cca5cb5e8afc73d6b46 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Fri, 28 Jan 2022 17:32:35 +0300 Subject: [PATCH 046/187] Postfix for #7106: Wrong detection of must-be-delimited user names: avoid bugs with single-character locales --- src/common/utils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 9380cedbf5..2104103d0d 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1615,6 +1615,8 @@ bool containsErrorCode(const ISC_STATUS* v, ISC_STATUS code) inline bool sqlSymbolChar(char c, bool first) { + if (c & 0x80) + return false; return (isdigit(c) && !first) || isalpha(c) || c == '_' || c == '$'; } From 6e9814d365f740149175c43e1bd1b0b7aeca8287 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 29 Jan 2022 00:05:58 +0000 Subject: [PATCH 047/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 5f589cb3c3..2b7b19d1f6 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:379 + FORMAL BUILD NUMBER:383 */ -#define PRODUCT_VER_STRING "5.0.0.379" -#define FILE_VER_STRING "WI-T5.0.0.379" -#define LICENSE_VER_STRING "WI-T5.0.0.379" -#define FILE_VER_NUMBER 5, 0, 0, 379 +#define PRODUCT_VER_STRING "5.0.0.383" +#define FILE_VER_STRING "WI-T5.0.0.383" +#define LICENSE_VER_STRING "WI-T5.0.0.383" +#define FILE_VER_NUMBER 5, 0, 0, 383 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "379" +#define FB_BUILD_NO "383" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index a31aa9a886..dd11f4ca6d 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=379 +BuildNum=383 NowAt=`pwd` cd `dirname $0` From e3dcdafc4c4095a076b4d180cc9692d41ff8e080 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Sat, 29 Jan 2022 12:18:47 +0200 Subject: [PATCH 048/187] Fixed bug #7119 : Database statistics service could not find existing table(s) --- src/jrd/svc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 6a21b6032e..7c67dd1fd8 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -2897,7 +2897,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) spb.getString(s); bool inStr = false; - for (FB_SIZE_T i = 0; i < s.length(); ++i) + for (FB_SIZE_T i = 0; i < s.length(); ) { if (s[i] == SVC_TRMNTR) { From 99966caac57533358e327bd4b473bc31d32b4aa4 Mon Sep 17 00:00:00 2001 From: Roman Simakov Date: Wed, 26 Jan 2022 17:29:35 +0300 Subject: [PATCH 049/187] Frontport fix #7087: Database downgrade via gbak may be broken (#7097) Depricate existing published boolean attributes in 4.0. Add length in {put/get}_boolean functions. Add compatibility to read depricated attributes from earlier backups. Since now to put a boolean value use just put_boolean. Use get_boolean(X, false) to read new boolean attributes. --- src/burp/backup.epp | 1 + src/burp/burp.h | 8 +++++++- src/burp/restore.epp | 36 +++++++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index e2bf9603fd..46243922c1 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -1961,6 +1961,7 @@ void put_boolean(att_type attribute, const FB_BOOLEAN value) BurpGlobals* tdgbl = BurpGlobals::getSpecific(); put(tdgbl, attribute); + put(tdgbl, (UCHAR) 1); put(tdgbl, value ? 1u : 0u); } diff --git a/src/burp/burp.h b/src/burp/burp.h index eb2ea3ac15..11b2581d4f 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -252,8 +252,9 @@ enum att_type { att_SQL_dialect, // SQL dialect that it speaks att_db_read_only, // Is the database ReadOnly? att_database_linger, // Disconnection timeout - att_database_sql_security,// default sql security value + att_database_sql_security_deprecated, // can be removed later att_replica_mode, // replica mode + att_database_sql_security, // default sql security value // Relation attributes @@ -275,6 +276,7 @@ enum att_type { att_relation_flags, att_relation_ext_file_name, // name of file for external tables att_relation_type, + att_relation_sql_security_deprecated, // can be removed later att_relation_sql_security, // Field attributes (used for both global and local fields) @@ -409,6 +411,7 @@ enum att_type { att_trig_engine_name, att_trig_entrypoint, att_trig_type2, + att_trig_sql_security_deprecated, // can be removed later att_trig_sql_security, // Function attributes @@ -433,6 +436,7 @@ enum att_type { att_function_owner_name, att_function_legacy_flag, att_function_deterministic_flag, + att_function_sql_security_deprecated, // can be removed later att_function_sql_security, // Function argument attributes @@ -529,6 +533,7 @@ enum att_type { att_procedure_entrypoint, att_procedure_package_name, att_procedure_private_flag, + att_procedure_sql_security_deprecated, // can be removed later att_procedure_sql_security, // Stored procedure parameter attributes @@ -630,6 +635,7 @@ enum att_type { att_package_security_class, att_package_owner_name, att_package_description, + att_package_sql_security_deprecated, // can be removed later att_package_sql_security, // Database creators diff --git a/src/burp/restore.epp b/src/burp/restore.epp index b09b9e3018..178058a4cd 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -192,8 +192,13 @@ static inline UCHAR get(BurpGlobals* tdgbl) return tdgbl->get(); } -static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl) +static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl, bool deprecated) { + if (!deprecated) + { + const UCHAR length = get(tdgbl); + fb_assert(length == 1); + } return get(tdgbl) ? FB_TRUE : FB_FALSE; } @@ -5142,10 +5147,11 @@ bool get_function(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 89); break; + case att_function_sql_security_deprecated: case att_function_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_function_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -5274,9 +5280,10 @@ bool get_function(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 89); break; + case att_function_sql_security_deprecated: case att_function_sql_security: if (tdgbl->RESTORE_format >= 11) - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_function_sql_security_deprecated); else bad_attribute(scan_next_attr, attribute, 89); break; @@ -7417,10 +7424,11 @@ bool get_package(BurpGlobals* tdgbl) X.RDB$DESCRIPTION.NULL = FALSE; break; + case att_package_sql_security_deprecated: case att_package_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_package_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -7634,10 +7642,11 @@ bool get_procedure(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 290); break; + case att_procedure_sql_security_deprecated: case att_procedure_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_procedure_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -7759,9 +7768,10 @@ bool get_procedure(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 290); break; + case att_procedure_sql_security_deprecated: case att_procedure_sql_security: if (tdgbl->RESTORE_format >= 11) - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_procedure_sql_security_deprecated); else bad_attribute(scan_next_attr, attribute, 290); break; @@ -8443,9 +8453,10 @@ bool get_relation(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 111); break; + case att_relation_sql_security_deprecated: case att_relation_sql_security: sql_security_null = false; - sql_security = get_boolean(tdgbl); + sql_security = get_boolean(tdgbl, attribute == att_relation_sql_security_deprecated); break; default: @@ -9784,10 +9795,11 @@ bool get_trigger(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 134); break; + case att_trig_sql_security_deprecated: case att_trig_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_trig_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -9930,9 +9942,10 @@ bool get_trigger(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 134); break; + case att_trig_sql_security_deprecated: case att_trig_sql_security: if (tdgbl->RESTORE_format >= 11) - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_trig_sql_security_deprecated); else bad_attribute(scan_next_attr, attribute, 134); break; @@ -11009,6 +11022,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file } break; + case att_database_sql_security_deprecated: case att_database_sql_security: if (tdgbl->RESTORE_format >= 11) { @@ -11017,7 +11031,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file FOR (REQUEST_HANDLE req_handle5) X IN RDB$DATABASE MODIFY X USING - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_database_sql_security_deprecated); END_MODIFY; ON_ERROR general_on_error(); @@ -11028,7 +11042,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file END_ERROR; } else - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_database_sql_security_deprecated); } else { From e443f02dd34af31058448bf2b7d38f0d11206a7c Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sun, 30 Jan 2022 00:05:48 +0000 Subject: [PATCH 050/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 2b7b19d1f6..89de0946e1 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:383 + FORMAL BUILD NUMBER:385 */ -#define PRODUCT_VER_STRING "5.0.0.383" -#define FILE_VER_STRING "WI-T5.0.0.383" -#define LICENSE_VER_STRING "WI-T5.0.0.383" -#define FILE_VER_NUMBER 5, 0, 0, 383 +#define PRODUCT_VER_STRING "5.0.0.385" +#define FILE_VER_STRING "WI-T5.0.0.385" +#define LICENSE_VER_STRING "WI-T5.0.0.385" +#define FILE_VER_NUMBER 5, 0, 0, 385 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "383" +#define FB_BUILD_NO "385" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index dd11f4ca6d..11523e954e 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=383 +BuildNum=385 NowAt=`pwd` cd `dirname $0` From 3c22c23874e2212601eb3d31969d7d1b5229aa5f Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 30 Jan 2022 12:11:05 -0300 Subject: [PATCH 051/187] Improvement #4769 - Allow sub-routines to access variables/parameters defined at the outer/parent level [CORE4449]. Remove blr_parameter3. Comment unused blr_run_count. --- doc/sql.extensions/README.subroutines.txt | 9 +- src/dsql/DdlNodes.epp | 6 + src/dsql/DsqlCompilerScratch.cpp | 43 +++- src/dsql/DsqlCompilerScratch.h | 13 +- src/dsql/ExprNodes.cpp | 300 ++++++++++++++++------ src/dsql/ExprNodes.h | 12 +- src/dsql/Nodes.h | 4 +- src/dsql/StmtNodes.cpp | 128 ++++++++- src/dsql/StmtNodes.h | 50 +++- src/dsql/gen.cpp | 8 +- src/include/firebird/impl/blr.h | 8 +- src/jrd/RecordSourceNodes.cpp | 3 - src/jrd/blp.h | 1 + src/jrd/evl.cpp | 34 +-- src/jrd/exe.cpp | 72 ++---- src/jrd/exe.h | 11 +- src/jrd/par.cpp | 1 + src/yvalve/gds.cpp | 44 +++- 18 files changed, 533 insertions(+), 214 deletions(-) diff --git a/doc/sql.extensions/README.subroutines.txt b/doc/sql.extensions/README.subroutines.txt index 5de6db5494..0caeabcabe 100644 --- a/doc/sql.extensions/README.subroutines.txt +++ b/doc/sql.extensions/README.subroutines.txt @@ -11,7 +11,7 @@ Description: Syntax: ::= - DECLARE [VARIABLE] [ := ]; + DECLARE [VARIABLE] [ = ]; | DECLARE [VARIABLE] CURSOR FOR (); | @@ -46,8 +46,11 @@ Syntax: Limitations: 1) Subroutines may not be nested in another subroutine. They are only supported in the main routine. - 2) Currently, a subroutine may not directly access or use variables or cursors of the - main statements. This may be allowed in the future. + 2) Currently, a subroutine may not directly access cursors of the main routine/block. + This may be allowed in the future. + 3) Since FB 5 subroutines may use variables and parameters from the main routine/block. + 4) Variables and parameters that are accessed by subroutines may have a small performance + penalty (even in the main routine) when being read. Notes: 1) Starting in FB 4, subroutines may be recursive or call others subroutines. diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 3be22e61e5..8ebcfca04f 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -2342,6 +2342,8 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch->cursorNumber = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -3235,6 +3237,8 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch->cursorNumber = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -3751,6 +3755,8 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d dsqlScratch->scopeLevel++; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); // dimitr: I see no reason to deny EXIT command in triggers, diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index f1550fe348..f3c21dbc06 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -345,30 +345,24 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE)) { // Check not implemented sub-functions. - - GenericMap >::ConstAccessor funcAccessor(&subFunctions); - - for (bool found = funcAccessor.getFirst(); found; found = funcAccessor.getNext()) + for (const auto& funcPair : subFunctions) { - if (!funcAccessor.current()->second->dsqlBlock) + if (!funcPair.second->dsqlBlock) { status_exception::raise( Arg::Gds(isc_subfunc_not_impl) << - funcAccessor.current()->first.c_str()); + funcPair.first.c_str()); } } // Check not implemented sub-procedures. - - GenericMap >::ConstAccessor procAccessor(&subProcedures); - - for (bool found = procAccessor.getFirst(); found; found = procAccessor.getNext()) + for (const auto& procPair : subProcedures) { - if (!procAccessor.current()->second->dsqlBlock) + if (!procPair.second->dsqlBlock) { status_exception::raise( Arg::Gds(isc_subproc_not_impl) << - procAccessor.current()->first.c_str()); + procPair.first.c_str()); } } } @@ -430,6 +424,31 @@ void DsqlCompilerScratch::putLocalVariable(dsql_var* variable, const DeclareVari ++hiddenVarsNumber; } +// Put maps in subroutines for outer variables/parameters usage. +void DsqlCompilerScratch::putOuterMaps() +{ + if (!outerMessagesMap.count() && !outerVarsMap.count()) + return; + + appendUChar(blr_outer_map); + + for (auto& pair : outerVarsMap) + { + appendUChar(blr_outer_map_variable); + appendUShort(pair.first); + appendUShort(pair.second); + } + + for (auto& pair : outerMessagesMap) + { + appendUChar(blr_outer_map_message); + appendUShort(pair.first); + appendUShort(pair.second); + } + + appendUChar(blr_end); +} + // Make a variable. dsql_var* DsqlCompilerScratch::makeVariable(dsql_fld* field, const char* name, const dsql_var::Type type, USHORT msgNumber, USHORT itemNumber, USHORT localNumber) diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index 632fbccb13..beb0bb1eb4 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -117,10 +117,12 @@ public: outputVariables(p), returningClause(nullptr), currCteAlias(NULL), + mainScratch(aMainScratch), + outerMessagesMap(p), + outerVarsMap(p), ctes(p), cteAliases(p), psql(false), - mainScratch(aMainScratch), subFunctions(p), subProcedures(p) { @@ -181,6 +183,7 @@ public: void putLocalVariables(CompoundStmtNode* parameters, USHORT locals); void putLocalVariable(dsql_var* variable, const DeclareVariableNode* hostParam, const MetaName& collationName); + void putOuterMaps(); dsql_var* makeVariable(dsql_fld*, const char*, const dsql_var::Type type, USHORT, USHORT, USHORT); dsql_var* resolveVariable(const MetaName& varName); @@ -313,14 +316,16 @@ public: Firebird::Array outputVariables; ReturningClause* returningClause; const Firebird::string* const* currCteAlias; + DsqlCompilerScratch* mainScratch; + Firebird::NonPooledMap outerMessagesMap; // + Firebird::NonPooledMap outerVarsMap; // private: Firebird::HalfStaticArray ctes; // common table expressions Firebird::HalfStaticArray cteAliases; // CTE aliases in recursive members bool psql; - DsqlCompilerScratch* mainScratch; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + Firebird::LeftPooledMap subFunctions; + Firebird::LeftPooledMap subProcedures; }; class PsqlChanger diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 23bc600f0d..c26204b47a 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -9408,7 +9408,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) //-------------------- -static RegisterNode regParameterNode({blr_parameter, blr_parameter2, blr_parameter3}); +static RegisterNode regParameterNode({blr_parameter, blr_parameter2}); ParameterNode::ParameterNode(MemoryPool& pool) : TypedNode(pool) @@ -9417,27 +9417,28 @@ ParameterNode::ParameterNode(MemoryPool& pool) DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { - MessageNode* message = NULL; - USHORT n = csb->csb_blr_reader.getByte(); + MessageNode* message = nullptr; + const USHORT messageNum = csb->csb_blr_reader.getByte(); - if (n >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[n].csb_message)) + if (messageNum >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[messageNum].csb_message)) PAR_error(csb, Arg::Gds(isc_badmsgnum)); - ParameterNode* node = FB_NEW_POOL(pool) ParameterNode(pool); - + const auto node = FB_NEW_POOL(pool) ParameterNode(pool); node->message = message; node->argNumber = csb->csb_blr_reader.getWord(); + node->outerDecl = csb->outerMessagesMap.exist(messageNum); - const Format* format = message->format; + const auto format = message->format; if (node->argNumber >= format->fmt_count) PAR_error(csb, Arg::Gds(isc_badparnum)); if (blrOp != blr_parameter) { - ParameterNode* flagNode = FB_NEW_POOL(pool) ParameterNode(pool); + const auto flagNode = FB_NEW_POOL(pool) ParameterNode(pool); flagNode->message = message; flagNode->argNumber = csb->csb_blr_reader.getWord(); + flagNode->outerDecl = node->outerDecl; if (flagNode->argNumber >= format->fmt_count) PAR_error(csb, Arg::Gds(isc_badparnum)); @@ -9445,16 +9446,12 @@ DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScr node->argFlag = flagNode; } - if (blrOp == blr_parameter3) + if (node->outerDecl) { - ParameterNode* indicatorNode = FB_NEW_POOL(pool) ParameterNode(pool); - indicatorNode->message = message; - indicatorNode->argNumber = csb->csb_blr_reader.getWord(); + fb_assert(csb->mainCsb); - if (indicatorNode->argNumber >= format->fmt_count) - PAR_error(csb, Arg::Gds(isc_badparnum)); - - node->argIndicator = indicatorNode; + if (csb->mainCsb) + message->itemsUsedInSubroutines.add(node->argNumber); } return node; @@ -9469,8 +9466,8 @@ string ParameterNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, message); NODE_PRINT(printer, argNumber); NODE_PRINT(printer, argFlag); - NODE_PRINT(printer, argIndicator); NODE_PRINT(printer, argInfo); + NODE_PRINT(printer, outerDecl); return "ParameterNode"; } @@ -9490,6 +9487,7 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool()); node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr); node->dsqlParameterIndex = dsqlParameterIndex; + node->outerDecl = outerDecl; return node; } @@ -9621,6 +9619,7 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, void ParameterNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + fb_assert(!outerDecl); GEN_parameter(dsqlScratch, dsqlParameter); } @@ -9641,7 +9640,20 @@ bool ParameterNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* { const ParameterNode* o = nodeAs(other); - return o && dsqlParameter->par_index == o->dsqlParameter->par_index; + return o && outerDecl == o->outerDecl && dsqlParameter->par_index == o->dsqlParameter->par_index; +} + +jrd_req* ParameterNode::getParamRequest(jrd_req* request) const +{ + auto paramRequest = request; + + if (outerDecl) + { + while (paramRequest->getStatement()->parentStatement) + paramRequest = paramRequest->req_caller; + } + + return paramRequest; } void ParameterNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) @@ -9676,14 +9688,16 @@ ValueExprNode* ParameterNode::copy(thread_db* tdbb, NodeCopier& copier) const node->message = message; node->argFlag = copier.copy(tdbb, argFlag); - node->argIndicator = copier.copy(tdbb, argIndicator); + node->outerDecl = outerDecl; return node; } ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - argInfo = CMP_pass2_validation(tdbb, csb, + const auto paramCsb = outerDecl ? csb->mainCsb : csb; + + argInfo = CMP_pass2_validation(tdbb, paramCsb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber)); ValueExprNode::pass2(tdbb, csb); @@ -9691,8 +9705,8 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc desc; getDesc(tdbb, csb, &desc); - if (nodFlags & FLAG_VALUE) - impureOffset = csb->allocImpure(); + if (message->itemsUsedInSubroutines.exist(argNumber)) + impureOffset = csb->allocImpure(); else impureOffset = csb->allocImpure(); @@ -9701,11 +9715,28 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const { - impure_value* const impure = request->getImpure(impureOffset); - request->req_flags &= ~req_null; + dsc* retDesc; + impure_value* impureForOuter; + if (message->itemsUsedInSubroutines.exist(argNumber)) + { + impureForOuter = request->getImpure(impureOffset); + retDesc = &impureForOuter->vlu_desc; + } + else + { + impureForOuter = nullptr; + retDesc = request->getImpure(impureOffset); + } + + const auto paramRequest = getParamRequest(request); + + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); const dsc* desc; + request->req_flags &= ~req_null; + if (argFlag) { desc = EVL_expr(tdbb, request, argFlag); @@ -9715,32 +9746,37 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const desc = &message->format->fmt_desc[argNumber]; - impure->vlu_desc.dsc_address = request->getImpure( + retDesc->dsc_address = paramRequest->getImpure( message->impureOffset + (IPTR) desc->dsc_address); - impure->vlu_desc.dsc_dtype = desc->dsc_dtype; - impure->vlu_desc.dsc_length = desc->dsc_length; - impure->vlu_desc.dsc_scale = desc->dsc_scale; - impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; + retDesc->dsc_dtype = desc->dsc_dtype; + retDesc->dsc_length = desc->dsc_length; + retDesc->dsc_scale = desc->dsc_scale; + retDesc->dsc_sub_type = desc->dsc_sub_type; - if (impure->vlu_desc.dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); + if (!(request->req_flags & req_null)) + { + if (impureForOuter) + EVL_make_value(tdbb, retDesc, impureForOuter); - USHORT* impure_flags = request->getImpure( + if (retDesc->dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, retDesc); + } + + auto impureFlags = paramRequest->getImpure( message->impureFlags + (sizeof(USHORT) * argNumber)); - if (!(*impure_flags & VLU_checked)) + if (!(*impureFlags & VLU_checked)) { if (!(request->req_flags & req_null)) { USHORT maxLen = desc->dsc_length; // not adjusted length - desc = &impure->vlu_desc; - if (DTYPE_IS_TEXT(desc->dsc_dtype)) + if (DTYPE_IS_TEXT(retDesc->dsc_dtype)) { - const UCHAR* p = desc->dsc_address; + const UCHAR* p = retDesc->dsc_address; USHORT len; - switch (desc->dsc_dtype) + switch (retDesc->dsc_dtype) { case dtype_cstring: len = strnlen((const char*) p, maxLen); @@ -9748,7 +9784,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const break; case dtype_text: - len = desc->dsc_length; + len = retDesc->dsc_length; break; case dtype_varying: @@ -9758,24 +9794,24 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const break; } - CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc)); + auto charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(retDesc)); EngineCallbacks::instance->validateData(charSet, len, p); - EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(desc), len, p, maxLen); + EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(retDesc), len, p, maxLen); } - else if (desc->isBlob()) + else if (retDesc->isBlob()) { - const bid* const blobId = reinterpret_cast(desc->dsc_address); + const bid* const blobId = reinterpret_cast(retDesc->dsc_address); if (!blobId->isEmpty()) { if (!request->hasInternalStatement()) tdbb->getTransaction()->checkBlob(tdbb, blobId, NULL, false); - if (desc->getCharSet() != CS_NONE && desc->getCharSet() != CS_BINARY) + if (retDesc->getCharSet() != CS_NONE && retDesc->getCharSet() != CS_BINARY) { AutoBlb blob(tdbb, blb::open(tdbb, tdbb->getTransaction(), blobId)); - blob.getBlb()->BLB_check_well_formed(tdbb, desc); + blob.getBlb()->BLB_check_well_formed(tdbb, retDesc); } } } @@ -9784,13 +9820,13 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const if (argInfo) { EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber), - argInfo, &impure->vlu_desc, request->req_flags & req_null); + argInfo, retDesc, request->req_flags & req_null); } - *impure_flags |= VLU_checked; + *impureFlags |= VLU_checked; } - return (request->req_flags & req_null) ? NULL : &impure->vlu_desc; + return (request->req_flags & req_null) ? nullptr : retDesc; } @@ -13464,24 +13500,25 @@ static RegisterNode regVariableNode({blr_variable}); VariableNode::VariableNode(MemoryPool& pool) : TypedNode(pool), - dsqlName(pool), - dsqlVar(NULL), - varDecl(NULL), - varInfo(NULL), - varId(0) + dsqlName(pool) { } -DmlNode* VariableNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) +DmlNode* VariableNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { const USHORT n = csb->csb_blr_reader.getWord(); - vec* vector = csb->csb_variables; - - if (!vector || n >= vector->count()) - PAR_error(csb, Arg::Gds(isc_badvarnum)); VariableNode* node = FB_NEW_POOL(pool) VariableNode(pool); node->varId = n; + node->outerDecl = csb->outerVarsMap.exist(n); + + if (node->outerDecl) + { + fb_assert(csb->mainCsb); + + if (csb->mainCsb) + csb->mainCsb->csb_variables_used_in_subroutines.add(node->varId); + } return node; } @@ -13495,6 +13532,7 @@ string VariableNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, varId); NODE_PRINT(printer, varDecl); NODE_PRINT(printer, varInfo); + NODE_PRINT(printer, outerDecl); return "VariableNode"; } @@ -13505,6 +13543,35 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) node->dsqlName = dsqlName; node->dsqlVar = dsqlVar ? dsqlVar.getObject() : dsqlScratch->resolveVariable(dsqlName); + if (!node->dsqlVar && dsqlScratch->mainScratch) + { + if ((node->dsqlVar = dsqlScratch->mainScratch->resolveVariable(dsqlName))) + { + node->outerDecl = true; + + const bool execBlock = (dsqlScratch->mainScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && + !(dsqlScratch->mainScratch->flags & + (DsqlCompilerScratch::FLAG_PROCEDURE | + DsqlCompilerScratch::FLAG_TRIGGER | + DsqlCompilerScratch::FLAG_FUNCTION)); + + if (node->dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) + { + if (!dsqlScratch->outerMessagesMap.exist(node->dsqlVar->msgNumber)) + { + // 0 = input, 1 = output. Start outer messages with 2. + dsqlScratch->outerMessagesMap.put( + node->dsqlVar->msgNumber, 2 + dsqlScratch->outerMessagesMap.count()); + } + } + else + { + if (!dsqlScratch->outerVarsMap.exist(node->dsqlVar->number)) + dsqlScratch->outerVarsMap.put(node->dsqlVar->number, dsqlScratch->hiddenVarsNumber++); + } + } + } + if (!node->dsqlVar) PASS1_field_unknown(NULL, dsqlName.c_str(), this); @@ -13518,8 +13585,10 @@ void VariableNode::setParameterName(dsql_par* parameter) const void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - bool execBlock = (dsqlScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && - !(dsqlScratch->flags & + auto varScratch = outerDecl ? dsqlScratch->mainScratch : dsqlScratch; + + const bool execBlock = (varScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && + !(varScratch->flags & (DsqlCompilerScratch::FLAG_PROCEDURE | DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)); @@ -13527,7 +13596,16 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) { dsqlScratch->appendUChar(blr_parameter2); - dsqlScratch->appendUChar(dsqlVar->msgNumber); + + if (outerDecl) + { + const auto messageNumPtr = dsqlScratch->outerMessagesMap.get(dsqlVar->msgNumber); + fb_assert(messageNumPtr); + dsqlScratch->appendUChar(*messageNumPtr); + } + else + dsqlScratch->appendUChar(dsqlVar->msgNumber); + dsqlScratch->appendUShort(dsqlVar->msgItem); dsqlScratch->appendUShort(dsqlVar->msgItem + 1); } @@ -13535,7 +13613,15 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) { // If this is an EXECUTE BLOCK input parameter, use the internal variable. dsqlScratch->appendUChar(blr_variable); - dsqlScratch->appendUShort(dsqlVar->number); + + if (outerDecl) + { + const auto varNumPtr = dsqlScratch->outerVarsMap.get(dsqlVar->number); + fb_assert(varNumPtr); + dsqlScratch->appendUShort(*varNumPtr); + } + else + dsqlScratch->appendUShort(dsqlVar->number); } } @@ -13550,7 +13636,8 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o if (!o) return false; - if (dsqlVar->field != o->dsqlVar->field || + if (outerDecl != o->outerDecl || + dsqlVar->field != o->dsqlVar->field || dsqlVar->field->fld_name != o->dsqlVar->field->fld_name || dsqlVar->number != o->dsqlVar->number || dsqlVar->msgItem != o->dsqlVar->msgItem || @@ -13562,6 +13649,19 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o return true; } +jrd_req* VariableNode::getVarRequest(jrd_req* request) const +{ + auto varRequest = request; + + if (outerDecl) + { + while (varRequest->getStatement()->parentStatement) + varRequest = varRequest->req_caller; + } + + return varRequest; +} + void VariableNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) { *desc = varDecl->varDesc; @@ -13571,6 +13671,7 @@ ValueExprNode* VariableNode::copy(thread_db* tdbb, NodeCopier& copier) const { VariableNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) VariableNode(*tdbb->getDefaultPool()); node->varId = copier.csb->csb_remap_variable + varId; + node->outerDecl = outerDecl; node->varDecl = varDecl; node->varInfo = varInfo; @@ -13591,12 +13692,14 @@ ValueExprNode* VariableNode::pass1(thread_db* tdbb, CompilerScratch* csb) ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - varInfo = CMP_pass2_validation(tdbb, csb, Item(Item::TYPE_VARIABLE, varId)); + const auto varCsb = outerDecl ? csb->mainCsb : csb; + + varInfo = CMP_pass2_validation(tdbb, varCsb, Item(Item::TYPE_VARIABLE, varDecl->varId)); ValueExprNode::pass2(tdbb, csb); - if (nodFlags & FLAG_VALUE) - impureOffset = csb->allocImpure(); + if (varDecl->usedInSubRoutines) + impureOffset = csb->allocImpure(); else impureOffset = csb->allocImpure(); @@ -13605,31 +13708,68 @@ ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const { - impure_value* const impure = request->getImpure(impureOffset); - impure_value* impure2 = request->getImpure(varDecl->impureOffset); + const auto varRequest = getVarRequest(request); + const auto varImpure = varRequest->getImpure(varDecl->impureOffset); request->req_flags &= ~req_null; - if (impure2->vlu_desc.dsc_flags & DSC_null) - request->req_flags |= req_null; + dsc* desc; - impure->vlu_desc = impure2->vlu_desc; - - if (impure->vlu_desc.dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); - - if (!(impure2->vlu_flags & VLU_checked)) + if (varDecl->usedInSubRoutines) { - if (varInfo) + const auto impure = request->getImpure(impureOffset); + + if (varImpure->vlu_desc.dsc_flags & DSC_null) + request->req_flags |= req_null; + else { - EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, - &impure->vlu_desc, (impure->vlu_desc.dsc_flags & DSC_null)); + EVL_make_value(tdbb, &varImpure->vlu_desc, impure); + + if (impure->vlu_desc.dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); } - impure2->vlu_flags |= VLU_checked; + if (!(varImpure->vlu_flags & VLU_checked)) + { + if (varInfo) + { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); + + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, + &impure->vlu_desc, (varImpure->vlu_desc.dsc_flags & DSC_null)); + } + + varImpure->vlu_flags |= VLU_checked; + } + + desc = &impure->vlu_desc; + } + else + { + desc = request->getImpure(impureOffset); + + if (varImpure->vlu_desc.dsc_flags & DSC_null) + request->req_flags |= req_null; + + *desc = varImpure->vlu_desc; + + if (desc->dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, desc); + + if (!(varImpure->vlu_flags & VLU_checked)) + { + if (varInfo) + { + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, + desc, (desc->dsc_flags & DSC_null)); + } + + varImpure->vlu_flags |= VLU_checked; + } } - return (request->req_flags & req_null) ? NULL : &impure->vlu_desc; + return (request->req_flags & req_null) ? nullptr : desc; } diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index b3d2b13212..2e9fe25a66 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -1579,10 +1579,7 @@ public: ValueExprNode::getChildren(holder, dsql); if (!dsql) - { holder.add(argFlag); - holder.add(argIndicator); - } } virtual Firebird::string internalPrint(NodePrinter& printer) const; @@ -1598,6 +1595,8 @@ public: virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; + jrd_req* getParamRequest(jrd_req* request) const; + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); @@ -1608,10 +1607,10 @@ public: dsql_par* dsqlParameter = nullptr; NestConst message; NestConst argFlag; - NestConst argIndicator; NestConst argInfo; USHORT dsqlParameterIndex = 0; USHORT argNumber = 0; + bool outerDecl = false; }; @@ -2201,6 +2200,8 @@ public: dsqlDesc = desc; } + jrd_req* getVarRequest(jrd_req* request) const; + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); @@ -2212,7 +2213,8 @@ public: NestConst dsqlVar; NestConst varDecl; NestConst varInfo; - USHORT varId; + USHORT varId = 0; + bool outerDecl = false; }; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 2ba6afaa41..fbc0277ef8 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -539,8 +539,7 @@ public: static const USHORT FLAG_DOUBLE = 0x20; static const USHORT FLAG_DATE = 0x40; static const USHORT FLAG_DECFLOAT = 0x80; - static const USHORT FLAG_VALUE = 0x100; // Full value area required in impure space. - static const USHORT FLAG_INT128 = 0x200; + static const USHORT FLAG_INT128 = 0x100; explicit ExprNode(Type aType, MemoryPool& pool) : DmlNode(pool), @@ -1409,6 +1408,7 @@ public: TYPE_MERGE_SEND, TYPE_MESSAGE, TYPE_MODIFY, + TYPE_OUTER_MAP, TYPE_POST_EVENT, TYPE_RECEIVE, TYPE_RETURN, diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index df013bae04..1919a984e7 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -1767,19 +1767,16 @@ void DeclareSubFuncNode::genParameters(DsqlCompilerScratch* dsqlScratch, } } -DeclareSubFuncNode* DeclareSubFuncNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +DeclareSubFuncNode* DeclareSubFuncNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/) { + ContextPoolHolder context(tdbb, &subCsb->csb_pool); + PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); + return this; } -DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/) +DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) { - // scope needed here? - { // scope - ContextPoolHolder context(tdbb, &subCsb->csb_pool); - PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); - } - return this; } @@ -2111,12 +2108,7 @@ void DeclareSubProcNode::genParameters(DsqlCompilerScratch* dsqlScratch, } } -DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) -{ - return this; -} - -DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/) +DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/) { ContextPoolHolder context(tdbb, &subCsb->csb_pool); PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); @@ -2124,6 +2116,11 @@ DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* return this; } +DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +{ + return this; +} + const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const { // Nothing to execute. This is the declaration node. @@ -2207,6 +2204,9 @@ DeclareVariableNode* DeclareVariableNode::pass1(thread_db* tdbb, CompilerScratch fb_assert(!(*vector)[varId]); (*vector)[varId] = this; + if (!csb->mainCsb && csb->csb_variables_used_in_subroutines.exist(varId)) + usedInSubRoutines = true; + return this; } @@ -4490,6 +4490,8 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->loopLevel = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -7133,6 +7135,104 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg //-------------------- +static RegisterNode regOuterMapNode({blr_outer_map}); + +DmlNode* OuterMapNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) +{ + fb_assert(csb->mainCsb); + if (!csb->mainCsb) + PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_outer_map. Must be inside subroutine."); + + const auto node = FB_NEW_POOL(pool) OuterMapNode(pool); + + auto& blrReader = csb->csb_blr_reader; + UCHAR subCode; + + while ((subCode = blrReader.getByte()) != blr_end) + { + switch (subCode) + { + case blr_outer_map_message: + { + const USHORT outerNumber = blrReader.getWord(); + const USHORT innerNumber = blrReader.getWord(); + + csb->outerMessagesMap.put(innerNumber, outerNumber); + + const auto outerMessage = CMP_csb_element(csb->mainCsb, outerNumber)->csb_message; + if (!outerMessage) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_message: outer message does not exist"); + } + + const auto tail = CMP_csb_element(csb, innerNumber); + if (tail->csb_message) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_message: inner message already exist"); + } + + tail->csb_message = outerMessage; + + if (innerNumber > csb->csb_msg_number) + csb->csb_msg_number = innerNumber; + + break; + } + + case blr_outer_map_variable: + { + const USHORT outerNumber = blrReader.getWord(); + const USHORT innerNumber = blrReader.getWord(); + + csb->mainCsb->csb_variables_used_in_subroutines.add(outerNumber); + csb->outerVarsMap.put(innerNumber, outerNumber); + + auto& outerVariables = *csb->mainCsb->csb_variables; + if (outerNumber >= outerVariables.count() || !outerVariables[outerNumber]) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_variable: outer variable does not exist"); + } + + auto& innerVariables = *(csb->csb_variables = vec::newVector( + *tdbb->getDefaultPool(), csb->csb_variables, innerNumber + 1)); + + if (innerVariables[innerNumber]) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_variable: inner variable already exist"); + } + + innerVariables[innerNumber] = outerVariables[outerNumber]; + break; + } + + default: + PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_outer_map sub code"); + } + } + + return node; +} + +const StmtNode* OuterMapNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +{ + if (request->req_operation == jrd_req::req_evaluate) + request->req_operation = jrd_req::req_return; + + return parentStmt; +} + + +//-------------------- + + static RegisterNode regPostEventNode({blr_post, blr_post_arg}); DmlNode* PostEventNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index c9fe09b141..d8fd591ca8 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -477,9 +477,7 @@ class DeclareVariableNode final : public TypedNode(pool), - dsqlDef(NULL), - varId(0) + : TypedNode(pool) { varDesc.clear(); } @@ -498,7 +496,8 @@ public: public: NestConst dsqlDef; dsc varDesc; - USHORT varId; + USHORT varId = 0; + bool usedInSubRoutines = false; }; @@ -1093,9 +1092,7 @@ class MessageNode : public TypedNode public: explicit MessageNode(MemoryPool& pool) : TypedNode(pool), - format(NULL), - impureFlags(0), - messageNumber(0) + itemsUsedInSubroutines(pool) { } @@ -1116,9 +1113,10 @@ public: virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; public: + Firebird::SortedArray itemsUsedInSubroutines; NestConst format; - ULONG impureFlags; - USHORT messageNumber; + ULONG impureFlags = 0; + USHORT messageNumber = 0; }; @@ -1171,6 +1169,40 @@ public: }; +class OuterMapNode final : public TypedNode +{ +public: + explicit OuterMapNode(MemoryPool& pool) + : TypedNode(pool) + { + } + +public: + static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + + Firebird::string internalPrint(NodePrinter& /*printer*/) const override + { + return "OuterMapNode"; + } + + void genBlr(DsqlCompilerScratch* /*dsqlScratch*/) override + { + } + + OuterMapNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override + { + return this; + } + + OuterMapNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override + { + return this; + } + + const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; +}; + + class PostEventNode final : public TypedNode { public: diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index e0dc3944a9..d8bbb41689 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -77,14 +77,9 @@ void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch) * Emit BLR for hidden variables. * **************************************/ - if (dsqlScratch->hiddenVariables.isEmpty()) - return; - for (Array::const_iterator i = dsqlScratch->hiddenVariables.begin(); - i != dsqlScratch->hiddenVariables.end(); - ++i) + for (const auto var : dsqlScratch->hiddenVariables) { - const dsql_var* var = *i; dsqlScratch->appendUChar(blr_dcl_variable); dsqlScratch->appendUShort(var->number); GEN_descriptor(dsqlScratch, &var->desc, true); @@ -247,6 +242,7 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) if (!block) scratch->appendUChar(blr_begin); + scratch->putOuterMaps(); GEN_hidden_variables(scratch); switch (statement->getType()) diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index 9b61310e0c..3ad0d2a650 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -193,8 +193,8 @@ #define blr_agg_min (unsigned char)85 #define blr_agg_total (unsigned char)86 #define blr_agg_average (unsigned char)87 -#define blr_parameter3 (unsigned char)88 /* same as Rdb definition */ /* unsupported +#define blr_parameter3 (unsigned char)88 #define blr_run_max (unsigned char)89 #define blr_run_min (unsigned char)90 #define blr_run_total (unsigned char)91 @@ -221,7 +221,7 @@ // unused codes: 111..117 -#define blr_run_count (unsigned char)118 /* changed from 88 to avoid conflict with blr_parameter3 */ +///#define blr_run_count (unsigned char)118 #define blr_rs_stream (unsigned char)119 #define blr_exec_proc (unsigned char)120 @@ -452,4 +452,8 @@ #define blr_local_table_truncate (unsigned char) 219 #define blr_local_table_id (unsigned char) 220 +#define blr_outer_map (unsigned char) 221 +#define blr_outer_map_message (unsigned char) 1 +#define blr_outer_map_variable (unsigned char) 2 + #endif // FIREBIRD_IMPL_BLR_H diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 9605f06563..7e46554621 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -136,9 +136,6 @@ SortNode* SortNode::pass1(thread_db* tdbb, CompilerScratch* csb) SortNode* SortNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) - (*i)->nodFlags |= ExprNode::FLAG_VALUE; - for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) ExprNode::doPass2(tdbb, csb, i->getAddress()); diff --git a/src/jrd/blp.h b/src/jrd/blp.h index b6ed5eacee..b4ee1dd7a2 100644 --- a/src/jrd/blp.h +++ b/src/jrd/blp.h @@ -252,5 +252,6 @@ static const struct {"dcl_local_table", dcl_local_table}, {"local_table_truncate", one_word}, {"local_table_id", local_table}, + {"outer_map", outer_map}, {0, 0} }; diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 5d3aa24b54..5cefa6e919 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -136,25 +136,18 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) DEV_BLKCHK(node, type_nod); jrd_req* request = tdbb->getRequest(); - impure_value* impure = request->getImpure(node->impureOffset); // The only nodes that can be assigned to are: argument, field and variable. - int arg_number; - const dsc* desc; - const MessageNode* message; - Record* record; - const ParameterNode* paramNode; - const VariableNode* varNode; - const FieldNode* fieldNode; - - if ((paramNode = nodeAs(node))) + if (auto paramNode = nodeAs(node)) { - message = paramNode->message; - arg_number = paramNode->argNumber; - desc = &message->format->fmt_desc[arg_number]; + auto message = paramNode->message; + auto arg_number = paramNode->argNumber; + auto desc = &message->format->fmt_desc[arg_number]; - impure->vlu_desc.dsc_address = request->getImpure( + auto impure = request->getImpure(node->impureOffset); + + impure->vlu_desc.dsc_address = paramNode->getParamRequest(request)->getImpure( message->impureOffset + (IPTR) desc->dsc_address); impure->vlu_desc.dsc_dtype = desc->dsc_dtype; impure->vlu_desc.dsc_length = desc->dsc_length; @@ -177,15 +170,15 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) } else if (nodeIs(node)) return NULL; - else if ((varNode = nodeAs(node))) + else if (auto varNode = nodeAs(node)) { - // Calculate descriptor - impure = request->getImpure(varNode->varDecl->impureOffset); + auto impure = varNode->getVarRequest(request)->getImpure(varNode->varDecl->impureOffset); return &impure->vlu_desc; } - else if ((fieldNode = nodeAs(node))) + else if (auto fieldNode = nodeAs(node)) { - record = request->req_rpb[fieldNode->fieldStream].rpb_record; + auto record = request->req_rpb[fieldNode->fieldStream].rpb_record; + auto impure = request->getImpure(node->impureOffset); if (!EVL_field(0, record, fieldNode->fieldId, &impure->vlu_desc)) { @@ -672,11 +665,10 @@ void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, d request->req_flags = flags; } - Firebird::string s; - if (err) { ISC_STATUS status = isc_not_valid_for_var; + string s; const char* arg; if (item.type == Item::TYPE_CAST) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 8425597760..499950d0e7 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -261,6 +261,14 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo SET_TDBB(tdbb); jrd_req* request = tdbb->getRequest(); + const auto toVar = nodeAs(to); + + if (toVar && toVar->outerDecl) + request = toVar->getVarRequest(request); + + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + // Get descriptors of receiving and sending fields/parameters, variables, etc. dsc* missing = NULL; @@ -284,31 +292,39 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo null = -1; USHORT* impure_flags = NULL; - const ParameterNode* toParam; - const VariableNode* toVar; + const auto toParam = nodeAs(to); - if ((toParam = nodeAs(to))) + if (toParam) { const MessageNode* message = toParam->message; + const auto paramRequest = toParam->getParamRequest(request); if (toParam->argInfo) { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); + EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber), toParam->argInfo, from_desc, null == -1); } - impure_flags = request->getImpure( + impure_flags = paramRequest->getImpure( message->impureFlags + (sizeof(USHORT) * toParam->argNumber)); } - else if ((toVar = nodeAs(to))) + else if (toVar) { + const auto varRequest = toVar->getVarRequest(request); + if (toVar->varInfo) { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId), toVar->varInfo, from_desc, null == -1); } - impure_flags = &request->getImpure( + impure_flags = &varRequest->getImpure( toVar->varDecl->impureOffset)->vlu_flags; } @@ -321,43 +337,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo if (!null) { - // if necessary and appropriate, use the indicator variable - - if (toParam && toParam->argIndicator) - { - dsc* indicator = EVL_assign_to(tdbb, toParam->argIndicator); - temp.dsc_dtype = dtype_short; - temp.dsc_length = sizeof(SSHORT); - temp.dsc_scale = 0; - temp.dsc_sub_type = 0; - - SSHORT len; - - if ((from_desc->dsc_dtype <= dtype_varying) && (to_desc->dsc_dtype <= dtype_varying) && - (TEXT_LEN(from_desc) > TEXT_LEN(to_desc))) - { - len = TEXT_LEN(from_desc); - } - else - len = 0; - - temp.dsc_address = (UCHAR *) &len; - MOV_move(tdbb, &temp, indicator); - - if (len) - { - temp = *from_desc; - temp.dsc_length = TEXT_LEN(to_desc); - - if (temp.dsc_dtype == dtype_cstring) - temp.dsc_length += 1; - else if (temp.dsc_dtype == dtype_varying) - temp.dsc_length += 2; - - from_desc = &temp; - } - } - // Validate range for datetime values if (DTYPE_IS_DATE(from_desc->dsc_dtype)) @@ -454,7 +433,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo // Handle the null flag as appropriate for fields and message arguments. - const FieldNode* toField = nodeAs(to); if (toField) { @@ -499,12 +477,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo temp.dsc_sub_type = 0; temp.dsc_address = (UCHAR*) &null; MOV_move(tdbb, &temp, to_desc); - - if (null && toParam->argIndicator) - { - to_desc = EVL_assign_to(tdbb, toParam->argIndicator); - MOV_move(tdbb, &temp, to_desc); - } } } diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 0d04522a4d..3a0bd26a25 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -36,6 +36,7 @@ #include "../jrd/Relation.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" +#include "../common/classes/fb_pair.h" #include "../common/classes/NestConst.h" #include "iberror.h" @@ -453,12 +454,15 @@ public: csb_current_nodes(p), csb_current_for_nodes(p), csb_computing_fields(p), + csb_variables_used_in_subroutines(p), csb_pool(p), csb_map_field_info(p), csb_map_item_info(p), csb_message_pad(p), subFunctions(p), subProcedures(p), + outerMessagesMap(p), + outerVarsMap(p), csb_currentForNode(NULL), csb_currentDMLNode(NULL), csb_currentAssignTarget(NULL), @@ -517,6 +521,7 @@ public: // candidates within whose scope we are Firebird::Array csb_current_for_nodes; Firebird::SortedArray csb_computing_fields; // Computed fields being compiled + Firebird::SortedArray csb_variables_used_in_subroutines; StreamType csb_n_stream; // Next available stream USHORT csb_msg_number; // Highest used message number ULONG csb_impure; // Next offset into impure area @@ -541,8 +546,10 @@ public: bool csb_returning_expr; bool csb_implicit_cursor; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + Firebird::LeftPooledMap subFunctions; + Firebird::LeftPooledMap subProcedures; + Firebird::NonPooledMap outerMessagesMap; // + Firebird::NonPooledMap outerVarsMap; // ForNode* csb_currentForNode; StmtNode* csb_currentDMLNode; // could be StoreNode or ModifyNode diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 65513746ea..3c958d9627 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -1141,6 +1141,7 @@ void PAR_procedure_parms(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedu MemoryPool& pool = *tdbb->getDefaultPool(); // We have a few parameters. Get on with creating the message block + // Outer messages map may start with 2, but they are always in the routine start. USHORT n = ++csb->csb_msg_number; if (n < 2) csb->csb_msg_number = n = 2; diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 618ddeccb7..c0b4f3143d 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -318,6 +318,7 @@ const int op_subfunc_decl = 28; const int op_window_win = 29; const int op_erase = 30; // special due to optional blr_marks after blr_erase const int op_dcl_local_table = 31; +const int op_outer_map = 32; static const UCHAR // generic print formats @@ -406,7 +407,8 @@ static const UCHAR store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0}, marks[] = { op_byte, op_literal, op_line, op_verb, 0}, erase[] = { op_erase, 0}, - local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}; + local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}, + outer_map[] = { op_outer_map, 0 }; #include "../jrd/blp.h" @@ -3885,6 +3887,46 @@ static void blr_print_verb(gds_ctl* control, SSHORT level) break; } + case op_outer_map: + { + offset = blr_print_line(control, offset); + + static const char* subCodes[] = + { + nullptr, + "message", + "variable" + }; + + while ((blr_operator = control->ctl_blr_reader.getByte()) != blr_end) + { + blr_indent(control, level); + + if (blr_operator == 0 || blr_operator >= FB_NELEM(subCodes)) + blr_error(control, "*** invalid blr_outer_map sub code ***"); + + blr_format(control, "blr_outer_map_%s, ", subCodes[blr_operator]); + + switch (blr_operator) + { + case blr_outer_map_message: + case blr_outer_map_variable: + blr_print_word(control); + n = blr_print_word(control); + offset = blr_print_line(control, offset); + 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; From 26f08f7f2e6d5e476a8c064ddb93fce473b44c5d Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Mon, 31 Jan 2022 00:05:32 +0000 Subject: [PATCH 052/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 89de0946e1..c6f8cc07d7 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:385 + FORMAL BUILD NUMBER:386 */ -#define PRODUCT_VER_STRING "5.0.0.385" -#define FILE_VER_STRING "WI-T5.0.0.385" -#define LICENSE_VER_STRING "WI-T5.0.0.385" -#define FILE_VER_NUMBER 5, 0, 0, 385 +#define PRODUCT_VER_STRING "5.0.0.386" +#define FILE_VER_STRING "WI-T5.0.0.386" +#define LICENSE_VER_STRING "WI-T5.0.0.386" +#define FILE_VER_NUMBER 5, 0, 0, 386 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "385" +#define FB_BUILD_NO "386" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 11523e954e..9516315675 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=385 +BuildNum=386 NowAt=`pwd` cd `dirname $0` From 5bb2db6ea60b07fc44c00c4800a9e0269927a7e6 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Mon, 31 Jan 2022 13:21:45 +0300 Subject: [PATCH 053/187] Fixed 2 assertions in DEV_BUILD reproted by Dmitry, dont affect releases --- src/burp/backup.epp | 2 +- src/utilities/gstat/dba.epp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 46243922c1..72421bc4d7 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -1140,7 +1140,7 @@ void put_asciz( const att_type attribute, const TEXT* string) // We can't honor operating systems that allow longer file names. if (len >= MAX_FILE_NAME_SIZE) { - BURP_print(true, 343, SafeArg() << int(attribute) << "put_asciz()" << (MAX_FILE_NAME_SIZE - 1)); + BURP_print(false, 343, SafeArg() << int(attribute) << "put_asciz()" << (MAX_FILE_NAME_SIZE - 1)); // msg 343: text for attribute @1 is too large in @2, truncating to @3 bytes len = MAX_FILE_NAME_SIZE - 1; } diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index df27fd696a..cc54fe1d6f 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -452,10 +452,14 @@ int gstat(Firebird::UtilSvc* uSvc) const Switches::in_sw_tab_t* in_sw_tab = switches.findSwitch(str); if (!in_sw_tab) { - if (!str[1]) - str = "-*NONE*"; - dba_print(true, 20, SafeArg() << (str + 1)); // msg 20: unknown switch "%s" - print_help(); + if (!tddba->uSvc->isService()) // normally service manager doesn't pass illegal switches + { + if (!str[1]) + str = "-*NONE*"; + dba_print(true, 20, SafeArg() << (str + 1)); // msg 20: unknown switch "%s" + print_help(); + } + dba_error(1); // msg 1: found unknown switch break; // redundant } From 18c3cdb11f5a2f81c7c371f000d1e9690503de5d Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 31 Jan 2022 19:06:10 +0300 Subject: [PATCH 054/187] Extra protection against lost savepoints (triggered by assertion in QA test read-consist-sttm-restart-on-update-01) --- src/jrd/exe.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 499950d0e7..502b97c938 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -726,6 +726,12 @@ void EXE_receive(thread_db* tdbb, fb_assert(transaction->tra_save_free == savepoint); transaction->tra_save_free = savepoint->moveToStack(request->req_proc_sav_point); fb_assert(request->req_proc_sav_point == savepoint); + + // Ensure that the priorly existing savepoints are preserved, + // e.g. 10-11-12-(5-6-7) where savNumber == 5. This may happen + // due to looper savepoints being reused in subsequent invokations. + if (savepoint->getNumber() == savNumber) + break; } } catch (...) @@ -1076,6 +1082,12 @@ static void execute_looper(thread_db* tdbb, fb_assert(savepoint == transaction->tra_save_free); transaction->tra_save_free = savepoint->moveToStack(request->req_savepoints); fb_assert(savepoint != transaction->tra_save_free); + + // Ensure that the priorly existing savepoints are preserved, + // e.g. 10-11-12-(5-6-7) where savNumber == 5. This may happen + // due to looper savepoints being reused in subsequent invokations. + if (savepoint->getNumber() == savNumber) + break; } } } From b07737f5e8538e9fac4ad9cecd383899aa2c5adf Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 1 Feb 2022 00:05:35 +0000 Subject: [PATCH 055/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index c6f8cc07d7..839b2448eb 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:386 + FORMAL BUILD NUMBER:388 */ -#define PRODUCT_VER_STRING "5.0.0.386" -#define FILE_VER_STRING "WI-T5.0.0.386" -#define LICENSE_VER_STRING "WI-T5.0.0.386" -#define FILE_VER_NUMBER 5, 0, 0, 386 +#define PRODUCT_VER_STRING "5.0.0.388" +#define FILE_VER_STRING "WI-T5.0.0.388" +#define LICENSE_VER_STRING "WI-T5.0.0.388" +#define FILE_VER_NUMBER 5, 0, 0, 388 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "386" +#define FB_BUILD_NO "388" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 9516315675..b312deccb1 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=386 +BuildNum=388 NowAt=`pwd` cd `dirname $0` From a88dc507366b06b4f8ddebb339b063b8120e0124 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 1 Feb 2022 19:22:50 +0300 Subject: [PATCH 056/187] Fixed #7122: Invalid state of mapping cache after replacement of database --- src/include/firebird/impl/consts_pub.h | 1 + src/include/gen/Firebird.pas | 1 + src/jrd/Mapping.cpp | 13 ++----------- src/jrd/Mapping.h | 5 +++-- src/jrd/jrd.cpp | 14 ++++++++++++++ src/jrd/shut.cpp | 7 +++++++ src/utilities/nbackup/nbackup.cpp | 14 +++++++++++++- 7 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/include/firebird/impl/consts_pub.h b/src/include/firebird/impl/consts_pub.h index 723ef16603..1cdc2fe54e 100644 --- a/src/include/firebird/impl/consts_pub.h +++ b/src/include/firebird/impl/consts_pub.h @@ -128,6 +128,7 @@ #define isc_dpb_set_bind 93 #define isc_dpb_decfloat_round 94 #define isc_dpb_decfloat_traps 95 +#define isc_dpb_clear_map 96 /**************************************************/ diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 61d849b86e..7f9f615e1d 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -3848,6 +3848,7 @@ const isc_dpb_set_bind = byte(93); isc_dpb_decfloat_round = byte(94); isc_dpb_decfloat_traps = byte(95); + isc_dpb_clear_map = byte(96); isc_dpb_address = byte(1); isc_dpb_addr_protocol = byte(1); isc_dpb_addr_endpoint = byte(2); diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index edc9918e93..1d874c0adb 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -1235,20 +1235,11 @@ InitInstance spCache; void resetMap(const char* db, ULONG index) { - switch(index) - { - case Mapping::MAPPING_CACHE: + if(index & Mapping::MAPPING_CACHE) resetMap(db); - break; - case Mapping::SYSTEM_PRIVILEGES_CACHE: + if(index & Mapping::SYSTEM_PRIVILEGES_CACHE) spCache().invalidate(db); - break; - - default: - fb_assert(false); - break; - } } } // anonymous namespace diff --git a/src/jrd/Mapping.h b/src/jrd/Mapping.h index 64dccddd30..b7ac46ea8d 100644 --- a/src/jrd/Mapping.h +++ b/src/jrd/Mapping.h @@ -77,8 +77,9 @@ public: void clearMainHandle(); // possible clearCache() flags - static const USHORT MAPPING_CACHE = 0; - static const USHORT SYSTEM_PRIVILEGES_CACHE = 1; + static const USHORT MAPPING_CACHE = 0x01; + static const USHORT SYSTEM_PRIVILEGES_CACHE = 0x02; + static const USHORT ALL_CACHE = ~0u; // Helper statuc functions to perform cleanup & shutdown. static void clearCache(const char* dbName, USHORT id); static void shutdownIpc(); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index c6539a42bc..a65090931b 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1085,6 +1085,7 @@ namespace Jrd ULONG dpb_remote_flags; ReplicaMode dpb_replica_mode; bool dpb_set_db_replica; + bool dpb_clear_map; // here begin compound objects // for constructor to work properly dpb_user_name @@ -1931,6 +1932,12 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch break; } + // Clear old mapping cache data on request. + // Unfortunately have to do it w/o access rights check - to check access rights engine + // needs correct mapping which sometimes can't be guaranteed before cleaning cache. + if (options.dpb_clear_map) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + // Check for correct credentials supplied UserId userId; @@ -3048,6 +3055,9 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch else dbb->dbb_database_name = dbb->dbb_filename; + // Clear old mapping cache data (if present) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + // Initialize backup difference subsystem. This must be done before WAL and shadowing // is enabled because nbackup it is a lower level subsystem dbb->dbb_backup_manager = FB_NEW_POOL(*dbb->dbb_permanent) BackupManager(tdbb, @@ -7156,6 +7166,10 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli rdr.getString(dpb_decfloat_traps); break; + case isc_dpb_clear_map: + dpb_clear_map = rdr.getBoolean(); + break; + default: break; } diff --git a/src/jrd/shut.cpp b/src/jrd/shut.cpp index 1b18a3835c..6b06cd8dee 100644 --- a/src/jrd/shut.cpp +++ b/src/jrd/shut.cpp @@ -26,6 +26,7 @@ #include "../jrd/scl.h" #include "../jrd/nbak.h" #include "../jrd/ods.h" +#include "../jrd/Mapping.h" #include "../jrd/cch_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" @@ -222,6 +223,9 @@ void SHUT_database(thread_db* tdbb, SSHORT flag, SSHORT delay, Sync* guard) check_backup_state(tdbb); } + // Clear old mapping cache data (if present) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + attachment->att_flags |= ATT_shutdown_manager; // Database is being shutdown. First notification gives shutdown type and delay in seconds. @@ -401,6 +405,9 @@ void SHUT_online(thread_db* tdbb, SSHORT flag, Sync* guard) check_backup_state(tdbb); } + // Clear old mapping cache data (if present) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + // Reset shutdown flag on database header page WIN window(HEADER_PAGE_NUMBER); diff --git a/src/utilities/nbackup/nbackup.cpp b/src/utilities/nbackup/nbackup.cpp index f66e95b0d4..26a1542173 100644 --- a/src/utilities/nbackup/nbackup.cpp +++ b/src/utilities/nbackup/nbackup.cpp @@ -278,7 +278,7 @@ public: run_db_triggers(_run_db_triggers), direct_io(_direct_io), dbase(INVALID_HANDLE_VALUE), backup(INVALID_HANDLE_VALUE), decompress(_deco), childId(0), db_size_pages(0), - m_odsNumber(0), m_silent(false), m_printed(false) + m_odsNumber(0), m_silent(false), m_printed(false), m_flash_map(false) { // Recognition of local prefix allows to work with // database using TCP/IP loopback while reading file locally. @@ -344,6 +344,7 @@ private: USHORT m_odsNumber; bool m_silent; // are we already handling an exception? bool m_printed; // pr_error() was called to print status vector + bool m_flash_map; // clear mapping cache on attach // IO functions FB_SIZE_T read_file(FILE_HANDLE &file, void *buffer, FB_SIZE_T bufsize); @@ -1007,6 +1008,9 @@ void NBackup::attach_database() if (!run_db_triggers) dpb.insertByte(isc_dpb_no_db_triggers, 1); + if (m_flash_map) + dpb.insertByte(isc_dpb_clear_map, 1); + if (m_silent) { ISC_STATUS_ARRAY temp; @@ -1670,6 +1674,14 @@ void NBackup::restore_database(const BackupFiles& files, bool repl_seq, bool inc { close_database(); fixup_database(repl_seq, inc_rest); + + m_silent = true; + m_flash_map = true; + run_db_triggers = false; + + attach_database(); + detach_database(); + return; } if (!inc_rest || curLevel) From f1447fc0c07d2da89ec268c57e53bbe41083b360 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 2 Feb 2022 00:05:46 +0000 Subject: [PATCH 057/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 839b2448eb..d01ca1b2c1 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:388 + FORMAL BUILD NUMBER:389 */ -#define PRODUCT_VER_STRING "5.0.0.388" -#define FILE_VER_STRING "WI-T5.0.0.388" -#define LICENSE_VER_STRING "WI-T5.0.0.388" -#define FILE_VER_NUMBER 5, 0, 0, 388 +#define PRODUCT_VER_STRING "5.0.0.389" +#define FILE_VER_STRING "WI-T5.0.0.389" +#define LICENSE_VER_STRING "WI-T5.0.0.389" +#define FILE_VER_NUMBER 5, 0, 0, 389 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "388" +#define FB_BUILD_NO "389" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index b312deccb1..3667291cc5 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=388 +BuildNum=389 NowAt=`pwd` cd `dirname $0` From 1a072f43d844ef1c3c6fd1acf3c81d2010e2790d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 6 Feb 2022 21:41:11 -0300 Subject: [PATCH 058/187] Add table MON$COMPILED_STATEMENTS and columns (#7050) Add table MON$COMPILED_STATEMENTS and columns MON$STATEMENTS.MON$COMPILED_STATEMENT_ID and MON$CALL_STACK.MON$COMPILED_STATEMENT_ID. --- src/dsql/dsql.cpp | 4 +- src/jrd/Attachment.cpp | 1 + src/jrd/Attachment.h | 1 + src/jrd/Database.h | 6 +++ src/jrd/JrdStatement.cpp | 16 ++++--- src/jrd/JrdStatement.h | 8 ++++ src/jrd/Monitoring.cpp | 80 ++++++++++++++++++++++++++++++---- src/jrd/Monitoring.h | 1 + src/jrd/exe.cpp | 9 +++- src/jrd/names.h | 3 ++ src/jrd/opt.cpp | 6 +-- src/jrd/opt_proto.h | 2 +- src/jrd/relations.h | 12 +++++ src/jrd/req.h | 17 +++++++- src/jrd/trace/TraceObjects.cpp | 2 +- 15 files changed, 147 insertions(+), 21 deletions(-) diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index d73d619c58..43661f79ee 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -2206,7 +2206,9 @@ static void sql_info(thread_db* tdbb, { const bool detailed = (item == isc_info_sql_explain_plan); string plan = tdbb->getAttachment()->stringToUserCharSet(tdbb, - OPT_get_plan(tdbb, request->req_request, detailed)); + OPT_get_plan(tdbb, + (request->req_request ? request->req_request->getStatement() : nullptr), + detailed)); if (plan.hasData()) { diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index d3f5530447..2cfbfb0c62 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -225,6 +225,7 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb, JProvider* provider att_ss_user(NULL), att_user_ids(*pool), att_active_snapshots(*pool), + att_statements(*pool), att_requests(*pool), att_lock_owner_id(Database::getLockOwnerId()), att_backup_state_counter(0), diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 563940ffd6..1e42f2a263 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -545,6 +545,7 @@ private: StableAttachmentPart* att_stable; public: + Firebird::SortedArray att_statements; // Statements belonging to attachment Firebird::SortedArray att_requests; // Requests belonging to attachment Lock* att_id_lock; // Attachment lock (if any) AttNumber att_attachment_id; // Attachment ID diff --git a/src/jrd/Database.h b/src/jrd/Database.h index 24fe561283..64a46534ce 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -59,6 +59,7 @@ #include "../jrd/RandomGenerator.h" #include "../common/os/guid.h" #include "../common/os/os_utils.h" +#include "../jrd/ods.h" #include "../jrd/sbm.h" #include "../jrd/flu.h" #include "../jrd/RuntimeStatistics.h" @@ -582,6 +583,11 @@ public: return (dbb_replica_mode == mode); } + USHORT getEncodedOdsVersion() const + { + return ENCODE_ODS(dbb_ods_version, dbb_minor_version); + } + private: Database(MemoryPool* p, Firebird::IPluginConfig* pConf, bool shared) : dbb_permanent(p), diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index 2af48dcb89..1ce4864d13 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -304,6 +304,8 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, if (internalFlag) statement->flags |= FLAG_INTERNAL; + tdbb->getAttachment()->att_statements.add(statement); + return statement; } @@ -407,7 +409,6 @@ jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) // Create the request. jrd_req* const request = FB_NEW_POOL(*pool) jrd_req(attachment, this, parentStats); - request->setRequestId(dbb->generateStatementId()); requests[level] = request; @@ -645,14 +646,19 @@ void JrdStatement::release(thread_db* tdbb) for (jrd_req** instance = requests.begin(); instance != requests.end(); ++instance) EXE_release(tdbb, *instance); + const auto attachment = tdbb->getAttachment(); + + FB_SIZE_T pos; + if (attachment->att_statements.find(this, pos)) + attachment->att_statements.remove(pos); + else + fb_assert(false); + sqlText = NULL; // Sub statement pool is the same of the main statement, so don't delete it. if (!parentStatement) - { - Jrd::Attachment* const att = tdbb->getAttachment(); - att->deletePool(pool); - } + attachment->deletePool(pool); } // Check that we have enough rights to access all resources this list of triggers touches. diff --git a/src/jrd/JrdStatement.h b/src/jrd/JrdStatement.h index ed67f30dfa..1234b11add 100644 --- a/src/jrd/JrdStatement.h +++ b/src/jrd/JrdStatement.h @@ -48,6 +48,13 @@ public: static JrdStatement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); static jrd_req* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + StmtNumber getStatementId() const + { + if (!id) + id = JRD_get_thread_data()->getDatabase()->generateStatementId(); + return id; + } + const Routine* getRoutine() const; bool isActive() const; @@ -68,6 +75,7 @@ public: unsigned flags; // statement flags unsigned blrVersion; ULONG impureSize; // Size of impure area + mutable StmtNumber id; // statement identifier Firebird::Array rpbsSetup; Firebird::Array requests; // vector of requests ExternalAccessList externalList; // Access to procedures/triggers to be checked diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index 17b6767982..be19a879fe 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -446,6 +446,9 @@ MonitoringSnapshot::MonitoringSnapshot(thread_db* tdbb, MemoryPool& pool) RecordBuffer* const dbb_buffer = allocBuffer(tdbb, pool, rel_mon_database); RecordBuffer* const att_buffer = allocBuffer(tdbb, pool, rel_mon_attachments); RecordBuffer* const tra_buffer = allocBuffer(tdbb, pool, rel_mon_transactions); + RecordBuffer* const cmp_stmt_buffer = dbb->getEncodedOdsVersion() >= ODS_13_1 ? + allocBuffer(tdbb, pool, rel_mon_compiled_statements) : + nullptr; RecordBuffer* const stmt_buffer = allocBuffer(tdbb, pool, rel_mon_statements); RecordBuffer* const call_buffer = allocBuffer(tdbb, pool, rel_mon_calls); RecordBuffer* const io_stat_buffer = allocBuffer(tdbb, pool, rel_mon_io_stats); @@ -561,6 +564,9 @@ MonitoringSnapshot::MonitoringSnapshot(thread_db* tdbb, MemoryPool& pool) case rel_mon_transactions: buffer = tra_buffer; break; + case rel_mon_compiled_statements: + buffer = cmp_stmt_buffer; + break; case rel_mon_statements: buffer = stmt_buffer; break; @@ -1039,7 +1045,7 @@ void Monitoring::putAttachment(SnapshotData::DumpRecord& record, const Jrd::Atta // statement timeout, milliseconds record.storeInteger(f_mon_att_stmt_timeout, attachment->getStatementTimeout()); - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_version) >= ODS_13_1) + if (dbb->getEncodedOdsVersion() >= ODS_13_1) { char timeZoneBuffer[TimeZoneUtil::MAX_SIZE]; TimeZoneUtil::format(timeZoneBuffer, sizeof(timeZoneBuffer), attachment->att_current_timezone); @@ -1134,11 +1140,49 @@ void Monitoring::putTransaction(SnapshotData::DumpRecord& record, const jrd_tra* } +void Monitoring::putStatement(SnapshotData::DumpRecord& record, const JrdStatement* statement, const string& plan) +{ + fb_assert(statement); + + record.reset(rel_mon_compiled_statements); + + // compiled statement id + record.storeInteger(f_mon_cmp_stmt_id, statement->getStatementId()); + + // sql text + if (statement->sqlText) + record.storeString(f_mon_cmp_stmt_sql_text, *statement->sqlText); + + // explained plan + if (plan.hasData()) + record.storeString(f_mon_cmp_stmt_expl_plan, plan); + + // object name/type + if (const auto routine = statement->getRoutine()) + { + if (routine->getName().package.hasData()) + record.storeString(f_mon_cmp_stmt_pkg_name, routine->getName().package); + + record.storeString(f_mon_cmp_stmt_name, routine->getName().identifier); + record.storeInteger(f_mon_cmp_stmt_type, routine->getObjectType()); + } + else if (!statement->triggerName.isEmpty()) + { + record.storeString(f_mon_cmp_stmt_name, statement->triggerName); + record.storeInteger(f_mon_cmp_stmt_type, obj_trigger); + } + + record.write(); +} + + void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* request, const string& plan) { fb_assert(request); + const auto dbb = request->req_attachment->att_database; + record.reset(rel_mon_statements); // request id @@ -1181,6 +1225,10 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req // statement timeout, milliseconds record.storeInteger(f_mon_stmt_timeout, request->req_timeout); + + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + record.storeInteger(f_mon_stmt_cmp_stmt_id, statement->getStatementId()); + record.write(); putStatistics(record, request->req_stats, stat_id, stat_statement); @@ -1192,7 +1240,9 @@ void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* reques { fb_assert(request); + const auto dbb = request->req_attachment->att_database; const jrd_req* initialRequest = request->req_caller; + while (initialRequest->req_caller) { initialRequest = initialRequest->req_caller; @@ -1241,6 +1291,9 @@ void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* reques record.storeInteger(f_mon_call_src_column, request->req_src_column); } + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + record.storeInteger(f_mon_call_cmp_stmt_id, statement->getStatementId()); + // statistics const int stat_id = fb_utils::genUniqueId(); record.storeGlobalId(f_mon_call_stat_id, getGlobalId(stat_id)); @@ -1432,18 +1485,29 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) } } + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + { + // Statement information + + for (const auto statement : attachment->att_statements) + { + if (!(statement->flags & (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) + { + const string plan = OPT_get_plan(tdbb, statement, true); + putStatement(record, statement, plan); + } + } + } + // Request information - for (const jrd_req* const* i = attachment->att_requests.begin(); - i != attachment->att_requests.end(); - ++i) + for (const auto request : attachment->att_requests) { - const jrd_req* const request = *i; + const auto statement = request->getStatement(); - if (!(request->getStatement()->flags & - (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) + if (!(statement->flags & (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) { - const string plan = OPT_get_plan(tdbb, request, true); + const string plan = OPT_get_plan(tdbb, statement, true); putRequest(record, request, plan); } } diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index ca3e8459ee..5fb0fa2dea 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -389,6 +389,7 @@ private: static void putAttachment(SnapshotData::DumpRecord&, const Attachment*); static void putTransaction(SnapshotData::DumpRecord&, const jrd_tra*); + static void putStatement(SnapshotData::DumpRecord&, const JrdStatement*, const Firebird::string&); static void putRequest(SnapshotData::DumpRecord&, const jrd_req*, const Firebird::string&); static void putCall(SnapshotData::DumpRecord&, const jrd_req*); static void putStatistics(SnapshotData::DumpRecord&, const RuntimeStatistics&, int, int); diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 502b97c938..82d84864af 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -860,7 +860,14 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) if (transaction->tra_flags & TRA_prepared) ERR_post(Arg::Gds(isc_req_no_trans)); - JrdStatement* statement = request->getStatement(); + const auto dbb = tdbb->getDatabase(); + const auto statement = request->getStatement(); + + // Generate request id. + request->setRequestId( + request->isRequestIdUnassigned() && request->isRoot() ? + statement->getStatementId() : + dbb->generateStatementId()); /* Post resources to transaction block. In particular, the interest locks on relations/indices are copied to the transaction, which is very diff --git a/src/jrd/names.h b/src/jrd/names.h index ece88e406d..1c81cafc3c 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -457,3 +457,6 @@ NAME("MON$SESSION_TIMEZONE", nam_mon_session_tz) NAME("RDB$KEYWORDS", nam_keywords) NAME("RDB$KEYWORD_NAME", nam_keyword_name) NAME("RDB$KEYWORD_RESERVED", nam_keyword_reserved) + +NAME("MON$COMPILED_STATEMENTS", nam_mon_compiled_statements) +NAME("MON$COMPILED_STATEMENT_ID", nam_mon_cmp_stmt_id) diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index 5b4ce226b1..546a12464e 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -439,7 +439,7 @@ static const UCHAR sort_dtypes[] = }; -string OPT_get_plan(thread_db* tdbb, const jrd_req* request, bool detailed) +string OPT_get_plan(thread_db* tdbb, const JrdStatement* statement, bool detailed) { /************************************** * @@ -453,9 +453,9 @@ string OPT_get_plan(thread_db* tdbb, const jrd_req* request, bool detailed) **************************************/ string plan; - if (request) + if (statement) { - const Array& fors = request->getStatement()->fors; + const Array& fors = statement->fors; for (FB_SIZE_T i = 0; i < fors.getCount(); i++) { diff --git a/src/jrd/opt_proto.h b/src/jrd/opt_proto.h index af6284dbd3..233d8ee3ee 100644 --- a/src/jrd/opt_proto.h +++ b/src/jrd/opt_proto.h @@ -41,7 +41,7 @@ namespace Jrd { class MapNode; } -Firebird::string OPT_get_plan(Jrd::thread_db* tdbb, const Jrd::jrd_req* request, bool detailed); +Firebird::string OPT_get_plan(Jrd::thread_db* tdbb, const Jrd::JrdStatement* statement, bool detailed); Jrd::RecordSource* OPT_compile(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, Jrd::RseNode* rse, Jrd::BoolExprNodeStack* parent_stack); void OPT_compile_relation(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation, Jrd::CompilerScratch* csb, diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 2e00e9c488..2fbfe56596 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -561,6 +561,7 @@ RELATION(nam_mon_statements, rel_mon_statements, ODS_11_1, rel_virtual) FIELD(f_mon_stmt_expl_plan, nam_mon_expl_plan, fld_source, 0, ODS_11_1) FIELD(f_mon_stmt_timeout, nam_stmt_timeout, fld_stmt_timeout, 0, ODS_13_0) FIELD(f_mon_stmt_timer, nam_stmt_timer, fld_stmt_timer, 0, ODS_13_0) + FIELD(f_mon_stmt_cmp_stmt_id, nam_mon_cmp_stmt_id, fld_stmt_id, 0, ODS_13_1) END_RELATION // Relation 37 (MON$CALL_STACK) @@ -575,6 +576,7 @@ RELATION(nam_mon_calls, rel_mon_calls, ODS_11_1, rel_virtual) FIELD(f_mon_call_src_column, nam_mon_src_column, fld_src_info, 0, ODS_11_1) FIELD(f_mon_call_stat_id, nam_mon_stat_id, fld_stat_id, 0, ODS_11_1) FIELD(f_mon_call_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, ODS_12_0) + FIELD(f_mon_call_cmp_stmt_id, nam_mon_cmp_stmt_id, fld_stmt_id, 0, ODS_13_1) END_RELATION // Relation 38 (MON$IO_STATS) @@ -741,3 +743,13 @@ RELATION(nam_keywords, rel_keywords, ODS_13_1, rel_virtual) FIELD(f_keyword_name, nam_keyword_name, fld_keyword_name, 0, ODS_13_1) FIELD(f_keyword_reserved, nam_keyword_reserved, fld_keyword_reserved, 0, ODS_13_1) END_RELATION + +// Relation 55 (MON$COMPILED_STATEMENTS) +RELATION(nam_mon_compiled_statements, rel_mon_compiled_statements, ODS_13_1, rel_virtual) + FIELD(f_mon_cmp_stmt_id, nam_mon_cmp_stmt_id, fld_stmt_id, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_sql_text, nam_mon_sql_text, fld_source, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_expl_plan, nam_mon_expl_plan, fld_source, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_name, nam_mon_obj_name, fld_gnr_name, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_type, nam_mon_obj_type, fld_obj_type, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, ODS_13_1) +END_RELATION diff --git a/src/jrd/req.h b/src/jrd/req.h index 55b8057fb5..aa0736a606 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -317,10 +317,25 @@ public: CS_METADATA : req_attachment->att_charset; } + bool isRoot() const + { + return statement->requests.hasData() && this == statement->requests[0]; + } + + bool isRequestIdUnassigned() const + { + return req_id == 0; + } + StmtNumber getRequestId() const { if (!req_id) - req_id = JRD_get_thread_data()->getDatabase()->generateStatementId(); + { + req_id = isRoot() ? + statement->getStatementId() : + JRD_get_thread_data()->getDatabase()->generateStatementId(); + } + return req_id; } diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 7b364c719f..9cc507aa1d 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -212,7 +212,7 @@ void TraceSQLStatementImpl::fillPlan(bool explained) { m_planExplained = explained; if (m_stmt->req_request) - m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->req_request, m_planExplained); + m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->req_request->getStatement(), m_planExplained); } } From 391e7ef4fcd48838145075e72d29532c3dd00f2b Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 7 Feb 2022 15:52:12 -0300 Subject: [PATCH 059/187] Refactor statement and request parts so multiple DSQL requests could be created from a single compiled statement. Pave the ground for compiled statement cache. --- builds/win32/msvc15/engine.vcxproj | 4 + builds/win32/msvc15/engine.vcxproj.filters | 12 + src/dsql/DdlNodes.epp | 12 +- src/dsql/DdlNodes.h | 4 +- src/dsql/DsqlBatch.cpp | 24 +- src/dsql/DsqlBatch.h | 8 +- src/dsql/DsqlCompilerScratch.h | 13 +- src/dsql/DsqlCursor.cpp | 10 +- src/dsql/DsqlCursor.h | 6 +- src/dsql/DsqlRequests.cpp | 1123 ++++++++++++++ src/dsql/DsqlRequests.h | 271 ++++ src/dsql/DsqlStatements.cpp | 298 ++++ src/dsql/DsqlStatements.h | 292 ++++ src/dsql/Nodes.h | 8 +- src/dsql/PackageNodes.epp | 60 +- src/dsql/Parser.cpp | 10 +- src/dsql/Parser.h | 15 +- src/dsql/StmtNodes.cpp | 113 +- src/dsql/StmtNodes.h | 24 +- src/dsql/dsql.cpp | 1557 ++------------------ src/dsql/dsql.h | 321 +--- src/dsql/dsql_proto.h | 14 +- src/dsql/gen.cpp | 20 +- src/dsql/gen_proto.h | 2 +- src/dsql/make_proto.h | 2 +- src/dsql/metd_proto.h | 2 +- src/dsql/parse.y | 25 +- src/jrd/EngineInterface.h | 8 +- src/jrd/JrdStatement.cpp | 3 + src/jrd/PreparedStatement.cpp | 8 +- src/jrd/PreparedStatement.h | 6 +- src/jrd/ResultSet.cpp | 8 +- src/jrd/cmp.cpp | 58 +- src/jrd/cmp_proto.h | 5 +- src/jrd/exe_proto.h | 4 +- src/jrd/extds/InternalDS.cpp | 44 +- src/jrd/jrd.cpp | 73 +- src/jrd/jrd_proto.h | 5 +- src/jrd/opt.cpp | 6 +- src/jrd/opt_proto.h | 2 +- src/jrd/req.h | 4 +- src/jrd/trace/TraceDSQLHelpers.h | 24 +- src/jrd/trace/TraceJrdHelpers.h | 8 +- src/jrd/trace/TraceObjects.cpp | 20 +- src/jrd/trace/TraceObjects.h | 16 +- 45 files changed, 2452 insertions(+), 2100 deletions(-) create mode 100644 src/dsql/DsqlRequests.cpp create mode 100644 src/dsql/DsqlRequests.h create mode 100644 src/dsql/DsqlStatements.cpp create mode 100644 src/dsql/DsqlStatements.h diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj index 89a580cde6..03e6ee6fd6 100644 --- a/builds/win32/msvc15/engine.vcxproj +++ b/builds/win32/msvc15/engine.vcxproj @@ -41,6 +41,8 @@ + + @@ -186,6 +188,8 @@ + + diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index aa5308dc3e..43ca7bbb6b 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -138,6 +138,12 @@ DSQL + + DSQL + + + DSQL + DSQL @@ -545,6 +551,12 @@ Header files + + Header files + + + Header files + Header files diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 8ebcfca04f..2e8f65c333 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -2354,7 +2354,7 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(false); dsqlScratch->appendUChar(blr_end); @@ -2879,7 +2879,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); bool modified = false; - DsqlCompiledStatement* statement = dsqlScratch->getStatement(); + DsqlStatement* statement = dsqlScratch->getStatement(); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) P IN RDB$PROCEDURES @@ -2973,7 +2973,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch P.RDB$PROCEDURE_TYPE.NULL = FALSE; P.RDB$PROCEDURE_TYPE = (USHORT) - (statement->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ? + (statement->getFlags() & DsqlStatement::FLAG_SELECTABLE ? prc_selectable : prc_executable); } } @@ -3249,7 +3249,7 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); dsqlScratch->appendUChar(blr_end); @@ -3780,7 +3780,7 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d // The statement type may have been set incorrectly when parsing // the trigger actions, so reset it to reflect the fact that this // is a data definition statement; also reset the ddl node. - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); } invalid = false; @@ -10742,7 +10742,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd (Arg::Gds(isc_adm_task_denied) << Arg::Gds(isc_miss_prvlg) << "CHANGE_MAPPING_RULES").raise(); if (from) - fromUtf8 = from->toUtf8(dsqlScratch); + fromUtf8 = from->toUtf8(transaction); if (global) { diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 3c07b01ac2..9c874557ae 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1130,7 +1130,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->getStatement()->setType( - legacy ? DsqlCompiledStatement::TYPE_SET_GENERATOR : DsqlCompiledStatement::TYPE_DDL); + legacy ? DsqlStatement::TYPE_SET_GENERATOR : DsqlStatement::TYPE_DDL); return this; } @@ -2418,7 +2418,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->getStatement()->setType( - create ? DsqlCompiledStatement::TYPE_CREATE_DB : DsqlCompiledStatement::TYPE_DDL); + create ? DsqlStatement::TYPE_CREATE_DB : DsqlStatement::TYPE_DDL); return this; } diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp index 81329807d6..0cdb773400 100644 --- a/src/dsql/DsqlBatch.cpp +++ b/src/dsql/DsqlBatch.cpp @@ -60,7 +60,7 @@ namespace { }; } -DsqlBatch::DsqlBatch(dsql_req* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) +DsqlBatch::DsqlBatch(DsqlDmlRequest* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) : m_request(req), m_batch(NULL), m_meta(inMeta), @@ -183,7 +183,7 @@ void DsqlBatch::setInterfacePtr(JBatch* interfacePtr) throw() m_batch = interfacePtr; } -DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inMetadata, +DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadata* inMetadata, unsigned parLength, const UCHAR* par) { SET_TDBB(tdbb); @@ -205,15 +205,15 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inM // Sanity checks before creating batch - if (!req->req_request) + if (!req->getJrdRequest()) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << Arg::Gds(isc_unprepared_stmt)); } - const DsqlCompiledStatement* statement = req->getStatement(); + const DsqlStatement* statement = req->getStatement(); - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -221,11 +221,11 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inM switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_INSERT: - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_INSERT: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_EXEC_BLOCK: break; default: @@ -652,7 +652,7 @@ private: // execute request m_request->req_transaction = transaction; - jrd_req* req = m_request->req_request; + jrd_req* req = m_request->getJrdRequest(); fb_assert(req); // prepare completion interface @@ -662,7 +662,7 @@ private: const dsql_msg* message = m_request->getStatement()->getSendMsg(); bool startRequest = true; - bool isExecBlock = m_request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK; + bool isExecBlock = m_request->getStatement()->getType() == DsqlStatement::TYPE_EXEC_BLOCK; const auto receiveMessage = isExecBlock ? m_request->getStatement()->getReceiveMsg() : nullptr; auto receiveMsgBuffer = isExecBlock ? m_request->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr; diff --git a/src/dsql/DsqlBatch.h b/src/dsql/DsqlBatch.h index 187fc34e4b..06e9182b64 100644 --- a/src/dsql/DsqlBatch.h +++ b/src/dsql/DsqlBatch.h @@ -41,7 +41,7 @@ class ClumpletReader; namespace Jrd { -class dsql_req; +class DsqlDmlRequest; class dsql_msg; class thread_db; class JBatch; @@ -50,7 +50,7 @@ class Attachment; class DsqlBatch { public: - DsqlBatch(dsql_req* req, const dsql_msg* message, Firebird::IMessageMetadata* inMetadata, + DsqlBatch(DsqlDmlRequest* req, const dsql_msg* message, Firebird::IMessageMetadata* inMetadata, Firebird::ClumpletReader& pb); ~DsqlBatch(); @@ -61,7 +61,7 @@ public: static const ULONG SIZEOF_BLOB_HEAD = sizeof(ISC_QUAD) + 2 * sizeof(ULONG); static const unsigned BLOB_STREAM_ALIGN = 4; - static DsqlBatch* open(thread_db* tdbb, dsql_req* req, Firebird::IMessageMetadata* inMetadata, + static DsqlBatch* open(thread_db* tdbb, DsqlDmlRequest* req, Firebird::IMessageMetadata* inMetadata, unsigned parLength, const UCHAR* par); Attachment* getAttachment() const; @@ -101,7 +101,7 @@ private: m_flags &= ~(1 << bit); } - dsql_req* const m_request; + DsqlDmlRequest* const m_request; JBatch* m_batch; Firebird::IMessageMetadata* m_meta; diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index beb0bb1eb4..a29731e380 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -24,6 +24,7 @@ #include "../jrd/jrd.h" #include "../dsql/dsql.h" +#include "../dsql/DsqlStatements.h" #include "../dsql/BlrDebugWriter.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" @@ -73,14 +74,13 @@ public: public: DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction, - DsqlCompiledStatement* aStatement, DsqlCompilerScratch* aMainScratch = NULL) + DsqlStatement* aStatement = nullptr, DsqlCompilerScratch* aMainScratch = nullptr) : BlrDebugWriter(p), dbb(aDbb), transaction(aTransaction), statement(aStatement), flags(0), nestingLevel(0), - ports(p), relation(NULL), mainContext(p), context(&mainContext), @@ -167,14 +167,14 @@ public: transaction = value; } - DsqlCompiledStatement* getStatement() + DsqlStatement* getStatement() const { return statement; } - DsqlCompiledStatement* getStatement() const + void setStatement(DsqlStatement* aStatement) { - return statement; + statement = aStatement; } void putBlrMarkers(ULONG marks); @@ -272,12 +272,11 @@ private: dsql_dbb* dbb; // DSQL attachment jrd_tra* transaction; // Transaction - DsqlCompiledStatement* statement; // Compiled statement + DsqlStatement* statement; // Compiled statement public: unsigned flags; // flags unsigned nestingLevel; // begin...end nesting level - Firebird::Array ports; // Port messages dsql_rel* relation; // relation created by this request (for DDL) DsqlContextStack mainContext; DsqlContextStack* context; diff --git a/src/dsql/DsqlCursor.cpp b/src/dsql/DsqlCursor.cpp index 564a476459..ba238e438b 100644 --- a/src/dsql/DsqlCursor.cpp +++ b/src/dsql/DsqlCursor.cpp @@ -33,7 +33,7 @@ using namespace Jrd; static const char* const SCRATCH = "fb_cursor_"; static const ULONG PREFETCH_SIZE = 65536; // 64 KB -DsqlCursor::DsqlCursor(dsql_req* req, ULONG flags) +DsqlCursor::DsqlCursor(DsqlDmlRequest* req, ULONG flags) : m_request(req), m_message(req->getStatement()->getReceiveMsg()), m_resultSet(NULL), m_flags(flags), m_space(req->getPool(), SCRATCH), @@ -69,10 +69,10 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) if (!cursor) return; - Jrd::Attachment* const attachment = cursor->getAttachment(); - dsql_req* const request = cursor->m_request; + const auto attachment = cursor->getAttachment(); + const auto request = cursor->m_request; - if (request->req_request) + if (request->getJrdRequest()) { ThreadStatusGuard status_vector(tdbb); try @@ -90,7 +90,7 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) TraceManager::event_dsql_free(attachment, &stmt, DSQL_close); } - JRD_unwind_request(tdbb, request->req_request); + JRD_unwind_request(tdbb, request->getJrdRequest()); } catch (Firebird::Exception&) {} // no-op diff --git a/src/dsql/DsqlCursor.h b/src/dsql/DsqlCursor.h index 864a8b3817..75392a47f9 100644 --- a/src/dsql/DsqlCursor.h +++ b/src/dsql/DsqlCursor.h @@ -27,7 +27,7 @@ namespace Jrd { -class dsql_req; +class DsqlDmlRequest; class JResultSet; class DsqlCursor @@ -35,7 +35,7 @@ class DsqlCursor enum State { BOS, POSITIONED, EOS }; public: - DsqlCursor(dsql_req* req, ULONG flags); + DsqlCursor(DsqlDmlRequest* req, ULONG flags); ~DsqlCursor(); jrd_tra* getTransaction() const; @@ -65,7 +65,7 @@ private: int fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position); bool cacheInput(thread_db* tdbb, FB_UINT64 position = MAX_UINT64); - dsql_req* const m_request; + DsqlDmlRequest* const m_request; const dsql_msg* const m_message; JResultSet* m_resultSet; const ULONG m_flags; diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp new file mode 100644 index 0000000000..dec63074ab --- /dev/null +++ b/src/dsql/DsqlRequests.cpp @@ -0,0 +1,1123 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.cpp + */ + +#include "firebird.h" +#include "../dsql/DsqlRequests.h" +#include "../dsql/dsql.h" +#include "../dsql/DsqlBatch.h" +#include "../dsql/Nodes.h" +#include "../jrd/JrdStatement.h" +#include "../jrd/req.h" +#include "../jrd/tra.h" +#include "../jrd/replication/Publisher.h" +#include "../jrd/trace/TraceDSQLHelpers.h" +#include "../jrd/trace/TraceObjects.h" +#include "../dsql/errd_proto.h" +#include "../dsql/movd_proto.h" +#include "../jrd/exe_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +static void checkD(IStatus* st); + + +// DsqlRequest + +DsqlRequest::DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement) + : PermanentStorage(pool), + req_dbb(dbb), + statement(aStatement) +{ +} + +DsqlRequest::~DsqlRequest() +{ +} + +void DsqlRequest::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); +} + +void DsqlRequest::setDelayedFormat(thread_db* /*tdbb*/, IMessageMetadata* /*metadata*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); +} + +bool DsqlRequest::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); + + return false; // avoid warning +} + +unsigned int DsqlRequest::getTimeout() +{ + return req_timeout; +} + +unsigned int DsqlRequest::getActualTimeout() +{ + if (req_timer) + return req_timer->getValue(); + + return 0; +} + +void DsqlRequest::setTimeout(unsigned int timeOut) +{ + req_timeout = timeOut; +} + +TimeoutTimer* DsqlRequest::setupTimer(thread_db* tdbb) +{ + auto jrdRequest = getJrdRequest(); + + if (jrdRequest) + { + if (jrdRequest->hasInternalStatement()) + return req_timer; + + jrdRequest->req_timeout = this->req_timeout; + + fb_assert(!jrdRequest->req_caller); + if (jrdRequest->req_caller) + { + if (req_timer) + req_timer->setup(0, 0); + return req_timer; + } + } + + Database* dbb = tdbb->getDatabase(); + Attachment* att = tdbb->getAttachment(); + + ISC_STATUS toutErr = isc_cfg_stmt_timeout; + unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000; + + if (req_timeout) + { + if (!timeOut || req_timeout < timeOut) + { + timeOut = req_timeout; + toutErr = isc_req_stmt_timeout; + } + } + else + { + const unsigned int attTout = att->getStatementTimeout(); + + if (!timeOut || attTout && attTout < timeOut) + { + timeOut = attTout; + toutErr = isc_att_stmt_timeout; + } + } + + if (!req_timer && timeOut) + { + req_timer = FB_NEW TimeoutTimer(); + fb_assert(jrdRequest); + jrdRequest->req_timer = this->req_timer; + } + + if (req_timer) + { + req_timer->setup(timeOut, toutErr); + req_timer->start(); + } + + return req_timer; +} + +// Release a dynamic request. +void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* request) +{ + SET_TDBB(tdbb); + + if (request->req_timer) + { + request->req_timer->stop(); + request->req_timer = nullptr; + } + + // If request is parent, orphan the children and release a portion of their requests + + for (auto childStatement : request->cursors) + { + childStatement->addFlags(DsqlStatement::FLAG_ORPHAN); + childStatement->setParentRequest(nullptr); + + // hvlad: lines below is commented out as + // - child is already unlinked from its parent request + // - we should not free child's sql text until its owner request is alive + // It seems to me we should destroy owner request here, not a child + // statement - as it always was before + + //Jrd::ContextPoolHolder context(tdbb, &childStatement->getPool()); + //releaseStatement(childStatement); + } + + // If the request had an open cursor, close it + + if (request->req_cursor) + DsqlCursor::close(tdbb, request->req_cursor); + + if (request->req_batch) + { + delete request->req_batch; + request->req_batch = nullptr; + } + + Jrd::Attachment* att = request->req_dbb->dbb_attachment; + const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); + if (need_trace_free) + { + TraceSQLStatementImpl stmt(request, NULL); + TraceManager::event_dsql_free(att, &stmt, DSQL_drop); + } + + if (request->req_cursor_name.hasData()) + request->req_dbb->dbb_cursors.remove(request->req_cursor_name); + + // If a request has been compiled, release it now + if (request->getJrdRequest()) + EXE_release(tdbb, request->getJrdRequest()); + + // Increase the statement refCount so its pool is not destroyed before the request is gone. + auto statement = request->getStatement(); + + // Release the entire request + delete request; + + statement = nullptr; +} + +// Parse the message of a request. +USHORT DsqlRequest::parseMetadata(IMessageMetadata* meta, const Array& parameters_list) +{ + HalfStaticArray parameters; + + for (FB_SIZE_T i = 0; i < parameters_list.getCount(); ++i) + { + dsql_par* param = parameters_list[i]; + + if (param->par_index) + { + if (param->par_index > parameters.getCount()) + parameters.grow(param->par_index); + fb_assert(!parameters[param->par_index - 1]); + parameters[param->par_index - 1] = param; + } + } + + // If there's no metadata, then the format of the current message buffer + // is identical to the format of the previous one. + + if (!meta) + return parameters.getCount(); + + FbLocalStatus st; + unsigned count = meta->getCount(&st); + checkD(&st); + + unsigned count2 = parameters.getCount(); + + if (count != count2) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_wrong_param_num) <getType(&st, index); + checkD(&st); + unsigned sqlLength = meta->getLength(&st, index); + checkD(&st); + + dsc desc; + desc.dsc_flags = 0; + + unsigned dataOffset, nullOffset, dtype, dlength; + offset = fb_utils::sqlTypeToDsc(offset, sqlType, sqlLength, + &dtype, &dlength, &dataOffset, &nullOffset); + desc.dsc_dtype = dtype; + desc.dsc_length = dlength; + + desc.dsc_scale = meta->getScale(&st, index); + checkD(&st); + desc.dsc_sub_type = meta->getSubType(&st, index); + checkD(&st); + unsigned textType = meta->getCharSet(&st, index); + checkD(&st); + desc.setTextType(textType); + desc.dsc_address = (UCHAR*)(IPTR) dataOffset; + + const dsql_par* const parameter = parameters[index]; + fb_assert(parameter); + + // ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been + // implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started + // failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using + // blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset + // here at the server as a way to make older (and not yet changed) client work + // correctly. + if (desc.isText() && desc.getTextType() == ttype_dynamic) + desc.setTextType(ttype_none); + + req_user_descs.put(parameter, desc); + + dsql_par* null = parameter->par_null; + if (null) + { + desc.clear(); + desc.dsc_dtype = dtype_short; + desc.dsc_scale = 0; + desc.dsc_length = sizeof(SSHORT); + desc.dsc_address = (UCHAR*)(IPTR) nullOffset; + + req_user_descs.put(null, desc); + } + } + + return count; +} + + +// DsqlDmlRequest + +DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement) + : DsqlRequest(pool, dbb, aStatement), + req_msg_buffers(pool) +{ + // Create the messages buffers + for (auto message : aStatement->getPorts()) + { + // Allocate buffer for message + const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1; + UCHAR* msgBuffer = FB_NEW_POOL(getPool()) UCHAR[newLen]; + msgBuffer = FB_ALIGN(msgBuffer, FB_DOUBLE_ALIGN); + fb_assert(message->msg_buffer_number == req_msg_buffers.getCount()); + req_msg_buffers.add(msgBuffer); + } + + jrdRequest = aStatement->getJrdStatement()->findRequest(tdbb); + tdbb->getAttachment()->att_requests.add(jrdRequest); +} + +JrdStatement* DsqlDmlRequest::getJrdStatement() const +{ + return jrdRequest ? jrdRequest->getStatement() : nullptr; +} + +// Provide backward-compatibility +void DsqlDmlRequest::setDelayedFormat(thread_db* tdbb, IMessageMetadata* metadata) +{ + if (!needDelayedFormat) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); + } + + needDelayedFormat = false; + delayedFormat = metadata; +} + +// Fetch next record from a dynamic SQL cursor. +bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + const auto statement = getStatement(); + + // if the cursor isn't open, we've got a problem + if (statement->isCursorBased()) + { + if (!req_cursor) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_dsql_cursor_err) << + Arg::Gds(isc_dsql_cursor_not_open)); + } + } + + if (!jrdRequest) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_unprepared_stmt)); + } + + dsql_msg* message = (dsql_msg*) statement->getReceiveMsg(); + + if (delayedFormat && message) + { + parseMetadata(delayedFormat, message->msg_parameters); + delayedFormat = NULL; + } + + // Set up things for tracing this call + Jrd::Attachment* att = req_dbb->dbb_attachment; + TraceDSQLFetch trace(att, this); + + thread_db::TimerGuard timerGuard(tdbb, req_timer, false); + if (req_timer && req_timer->expired()) + tdbb->checkCancelState(); + + UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; + if (!firstRowFetched && needRestarts()) + { + // Note: tra_handle can't be changed by executeReceiveWithRestarts below + // and outMetadata and outMsg in not used there, so passing NULL's is safe. + jrd_tra* tra = req_transaction; + + executeReceiveWithRestarts(tdbb, &tra, NULL, NULL, false, false, true); + fb_assert(tra == req_transaction); + } + else + JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, dsqlMsgBuffer); + + firstRowFetched = true; + + const dsql_par* const eof = statement->getEof(); + const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; + const bool eofReached = eof && !(*eofPtr); + + if (eofReached) + { + if (req_timer) + req_timer->stop(); + + trace.fetch(true, ITracePlugin::RESULT_SUCCESS); + return false; + } + + if (msgBuffer) + mapInOut(tdbb, true, message, NULL, msgBuffer); + + trace.fetch(false, ITracePlugin::RESULT_SUCCESS); + return true; +} + +// Set a cursor name for a dynamic request. +void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + const size_t MAX_CURSOR_LENGTH = 132 - 1; + string cursor = name; + + if (cursor.hasData() && cursor[0] == '\"') + { + // Quoted cursor names eh? Strip'em. + // Note that "" will be replaced with ". + // The code is very strange, because it doesn't check for "" really + // and thus deletes one isolated " in the middle of the cursor. + for (string::iterator i = cursor.begin(); i < cursor.end(); ++i) + { + if (*i == '\"') + cursor.erase(i); + } + } + else // not quoted name + { + const string::size_type i = cursor.find(' '); + if (i != string::npos) + cursor.resize(i); + + cursor.upper(); + } + + USHORT length = (USHORT) fb_utils::name_length(cursor.c_str()); + + if (!length) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_invalid)); + } + + if (length > MAX_CURSOR_LENGTH) + length = MAX_CURSOR_LENGTH; + + cursor.resize(length); + + // If there already is a different cursor by the same name, bitch + + auto* const* symbol = req_dbb->dbb_cursors.get(cursor); + if (symbol) + { + if (this == *symbol) + return; + + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_redefined) << cursor); + } + + // If there already is a cursor and its name isn't the same, ditto. + // We already know there is no cursor by this name in the hash table + + if (req_cursor && req_cursor_name.hasData()) + { + fb_assert(!symbol); + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_redefined) << req_cursor_name); + } + + if (req_cursor_name.hasData()) + req_dbb->dbb_cursors.remove(req_cursor_name); + req_cursor_name = cursor; + req_dbb->dbb_cursors.put(cursor, this); +} + +// Open a dynamic SQL cursor. +DsqlCursor* DsqlDmlRequest::openCursor(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMeta, const UCHAR* inMsg, IMessageMetadata* outMeta, ULONG flags) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_bad_req_handle)); + } + + // Validate transaction handle + + if (!*traHandle) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_bad_trans_handle)); + } + + // Validate statement type + + if (!statement->isCursorBased()) + Arg::Gds(isc_no_cursor).raise(); + + // Validate cursor or batch being not already open + + if (req_cursor) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)); + } + + if (req_batch) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_batch_open)); + } + + req_transaction = *traHandle; + execute(tdbb, traHandle, inMeta, inMsg, outMeta, NULL, false); + + req_cursor = FB_NEW_POOL(getPool()) DsqlCursor(this, flags); + + return req_cursor; +} + +bool DsqlDmlRequest::needRestarts() +{ + return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency)); +}; + +// Execute a dynamic SQL statement +void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + firstRowFetched = false; + const dsql_msg* message = statement->getSendMsg(); + + if (!message) + JRD_start(tdbb, jrdRequest, req_transaction); + else + { + UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; + JRD_start_and_send(tdbb, jrdRequest, req_transaction, message->msg_number, + message->msg_length, msgBuffer); + } + + // Selectable execute block should get the "proc fetch" flag assigned, + // which ensures that the savepoint stack is preserved while suspending + if (statement->getType() == DsqlStatement::TYPE_SELECT_BLOCK) + jrdRequest->req_flags |= req_proc_fetch; + + // TYPE_EXEC_BLOCK has no outputs so there are no out_msg + // supplied from client side, but TYPE_EXEC_BLOCK requires + // 2-byte message for EOS synchronization + const bool isBlock = (statement->getType() == DsqlStatement::TYPE_EXEC_BLOCK); + + message = statement->getReceiveMsg(); + + if (outMetadata == DELAYED_OUT_FORMAT) + { + needDelayedFormat = true; + outMetadata = NULL; + } + + if (outMetadata && message) + parseMetadata(outMetadata, message->msg_parameters); + + if ((outMsg && message) || isBlock) + { + UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2]; + dsql_msg temp_msg(*getDefaultMemoryPool()); + + // Insure that the metadata for the message is parsed, regardless of + // whether anything is found by the call to receive. + + UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; + + if (!outMetadata && isBlock) + { + message = &temp_msg; + temp_msg.msg_number = 1; + temp_msg.msg_length = 2; + msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); + } + + JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, msgBuffer); + + if (outMsg) + mapInOut(tdbb, true, message, NULL, outMsg); + + // if this is a singleton select, make sure there's in fact one record + + if (singleton) + { + USHORT counter; + + // Create a temp message buffer and try two more receives. + // If both succeed then the first is the next record and the + // second is either another record or the end of record message. + // In either case, there's more than one record. + + UCHAR* message_buffer = (UCHAR*) gds__alloc(message->msg_length); + + ISC_STATUS status = FB_SUCCESS; + FbLocalStatus localStatus; + + for (counter = 0; counter < 2 && !status; counter++) + { + localStatus->init(); + AutoSetRestore autoStatus(&tdbb->tdbb_status_vector, &localStatus); + + try + { + JRD_receive(tdbb, jrdRequest, message->msg_number, + message->msg_length, message_buffer); + status = FB_SUCCESS; + } + catch (Exception&) + { + status = tdbb->tdbb_status_vector->getErrors()[1]; + } + } + + gds__free(message_buffer); + + // two successful receives means more than one record + // a req_sync error on the first pass above means no records + // a non-req_sync error on any of the passes above is an error + + if (!status) + status_exception::raise(Arg::Gds(isc_sing_select_err)); + else if (status == isc_req_sync && counter == 1) + status_exception::raise(Arg::Gds(isc_stream_eof)); + else if (status != isc_req_sync) + status_exception::raise(&localStatus); + } + } + + switch (statement->getType()) + { + case DsqlStatement::TYPE_UPDATE_CURSOR: + if (!jrdRequest->req_records_updated) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << + Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict)); + } + break; + + case DsqlStatement::TYPE_DELETE_CURSOR: + if (!jrdRequest->req_records_deleted) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << + Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict)); + } + break; + } +} + +DsqlBatch* DsqlDmlRequest::openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) +{ + return DsqlBatch::open(tdbb, this, inMetadata, parLength, par); +} + +// Execute a dynamic SQL statement with tracing, restart and timeout handler +void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + if (!jrdRequest) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_unprepared_stmt)); + } + + // If there is no data required, just start the request + + const dsql_msg* message = statement->getSendMsg(); + if (message) + mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); + + // we need to mapInOut() before tracing of execution start to let trace + // manager know statement parameters values + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + + // Setup and start timeout timer + const bool have_cursor = statement->isCursorBased() && !singleton; + + setupTimer(tdbb); + thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); + + if (needRestarts()) + executeReceiveWithRestarts(tdbb, traHandle, outMetadata, outMsg, singleton, true, false); + else { + doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); + } + + trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS); +} + +void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton, bool exec, bool fetch) +{ + jrdRequest->req_flags &= ~req_update_conflict; + int numTries = 0; + const int MAX_RESTARTS = 10; + + while (true) + { + AutoSavePoint savePoint(tdbb, req_transaction); + + // Don't set req_restart_ready flag at last attempt to restart request. + // It allows to raise update conflict error (if any) as usual and + // handle error by PSQL handler. + const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; + AutoSetRestoreFlag restartReady(&jrdRequest->req_flags, flag, true); + try + { + if (exec) + doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); + + if (fetch) + { + fb_assert(statement->isCursorBased()); + + const dsql_msg* message = statement->getReceiveMsg(); + + UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; + JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, dsqlMsgBuffer); + } + } + catch (const status_exception&) + { + if (!(req_transaction->tra_flags & TRA_ex_restart)) + { + jrdRequest->req_flags &= ~req_update_conflict; + throw; + } + } + + if (!(jrdRequest->req_flags & req_update_conflict)) + { + fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); + req_transaction->tra_flags &= ~TRA_ex_restart; + +#ifdef DEV_BUILD + if (numTries > 0) + { + string s; + s.printf("restarts = %d", numTries); + + ERRD_post_warning(Arg::Warning(isc_random) << Arg::Str(s)); + } +#endif + savePoint.release(); // everything is ok + break; + } + + fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); + + jrdRequest->req_flags &= ~req_update_conflict; + req_transaction->tra_flags &= ~TRA_ex_restart; + fb_utils::init_status(tdbb->tdbb_status_vector); + + // Undo current savepoint but preserve already taken locks. + // Savepoint will be restarted at the next loop iteration. + savePoint.rollback(true); + + numTries++; + if (numTries >= MAX_RESTARTS) + { + gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" + "\tafter %d attempts of restart.\n" + "\tQuery:\n%s\n", numTries, jrdRequest->getStatement()->sqlText->c_str() ); + } + + // When restart we must execute query + exec = true; + } +} + +// Map data from external world into message or from message to external world. +void DsqlDmlRequest::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* message, + IMessageMetadata* meta, UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf) +{ + USHORT count = parseMetadata(meta, message->msg_parameters); + + // Sanity check + + if (count) + { + if (toExternal) + { + if (dsql_msg_buf == NULL) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_no_output_sqlda)); + } + } + else + { + if (in_dsql_msg_buf == NULL) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_no_input_sqlda)); + } + } + } + + USHORT count2 = 0; + + for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i) + { + dsql_par* parameter = message->msg_parameters[i]; + + if (parameter->par_index) + { + // Make sure the message given to us is long enough + + dsc desc; + if (!req_user_descs.get(parameter, desc)) + desc.clear(); + + /*** + ULONG length = (IPTR) desc.dsc_address + desc.dsc_length; + if (length > msg_length) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_random) << "Message buffer too short"); + } + ***/ + if (!desc.dsc_dtype) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_datatype_err) << + Arg::Gds(isc_dsql_sqlvar_index) << Arg::Num(parameter->par_index-1)); + } + + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + SSHORT* flag = NULL; + dsql_par* const null_ind = parameter->par_null; + if (null_ind != NULL) + { + dsc userNullDesc; + if (!req_user_descs.get(null_ind, userNullDesc)) + userNullDesc.clear(); + + const ULONG null_offset = (IPTR) userNullDesc.dsc_address; + + /*** + length = null_offset + sizeof(SSHORT); + if (length > msg_length) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) + << Arg::Gds(isc_random) << "Message buffer too short"); + } + ***/ + + dsc nullDesc = null_ind->par_desc; + nullDesc.dsc_address = msgBuffer + (IPTR) nullDesc.dsc_address; + + if (toExternal) + { + flag = reinterpret_cast(dsql_msg_buf + null_offset); + *flag = *reinterpret_cast(nullDesc.dsc_address); + } + else + { + flag = reinterpret_cast(nullDesc.dsc_address); + *flag = *reinterpret_cast(in_dsql_msg_buf + null_offset); + } + } + + const bool notNull = (!flag || *flag >= 0); + + dsc parDesc = parameter->par_desc; + parDesc.dsc_address = msgBuffer + (IPTR) parDesc.dsc_address; + + if (toExternal) + { + desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; + + if (notNull) + MOVD_move(tdbb, &parDesc, &desc); + else + memset(desc.dsc_address, 0, desc.dsc_length); + } + else if (notNull && !parDesc.isNull()) + { + // Safe cast because desc is used as source only. + desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; + MOVD_move(tdbb, &desc, &parDesc); + } + else + memset(parDesc.dsc_address, 0, parDesc.dsc_length); + + ++count2; + } + } + + if (count != count2) + { + ERRD_post( + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && + (parameter = statement->getDbKey())) + { + UCHAR* parentMsgBuffer = statement->getParentRequest() ? + statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + fb_assert(parentMsgBuffer); + + dsc parentDesc = dbkey->par_desc; + parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; + + dsc desc = parameter->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + MOVD_move(tdbb, &parentDesc, &desc); + + dsql_par* null_ind = parameter->par_null; + if (null_ind != NULL) + { + desc = null_ind->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + SSHORT* flag = (SSHORT*) desc.dsc_address; + *flag = 0; + } + } + + const dsql_par* rec_version; + if (!toExternal && (rec_version = statement->getParentRecVersion()) && + (parameter = statement->getRecVersion())) + { + UCHAR* parentMsgBuffer = statement->getParentRequest() ? + statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : + NULL; + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + fb_assert(parentMsgBuffer); + + dsc parentDesc = rec_version->par_desc; + parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; + + dsc desc = parameter->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + MOVD_move(tdbb, &parentDesc, &desc); + + dsql_par* null_ind = parameter->par_null; + if (null_ind != NULL) + { + desc = null_ind->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + SSHORT* flag = (SSHORT*) desc.dsc_address; + *flag = 0; + } + } +} + + +// DsqlDdlRequest + +DsqlDdlRequest::DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode) + : DsqlRequest(pool, dbb, aInternalScratch->getStatement()), + internalScratch(aInternalScratch), + node(aNode) +{ +} + +// Execute a dynamic SQL statement. +void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + + fb_utils::init_status(tdbb->tdbb_status_vector); + + // run all statements under savepoint control + { // scope + AutoSavePoint savePoint(tdbb, req_transaction); + + try + { + AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); + + node->executeDdl(tdbb, internalScratch, req_transaction); + + const bool isInternalRequest = + (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); + + if (!isInternalRequest && node->mustBeReplicated()) + REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); + } + catch (status_exception& ex) + { + DsqlStatement::rethrowDdlException(ex, true, node); + } + + savePoint.release(); // everything is ok + } + + JRD_autocommit_ddl(tdbb, req_transaction); + + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// DsqlTransactionRequest + +DsqlTransactionRequest::DsqlTransactionRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, TransactionNode* aNode) + : DsqlRequest(pool, dbb, aStatement), + node(aNode) +{ + // Don't trace anything except savepoint statements + req_traced = (aStatement->getType() == DsqlStatement::TYPE_SAVEPOINT); +} + +// Execute a dynamic SQL statement. +void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, + IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, + bool /*singleton*/) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + node->execute(tdbb, this, traHandle); + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// DsqlSessionManagementStatement + +DsqlSessionManagementStatement::~DsqlSessionManagementStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +void DsqlSessionManagementStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, + ntrace_result_t* /*traceResult*/) +{ + node = Node::doDsqlPass(scratch, node); + + this->scratch = scratch; +} + +DsqlSessionManagementRequest* DsqlSessionManagementStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlSessionManagementRequest(getPool(), dbb, this, node); +} + +// Execute a dynamic SQL statement. +void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + node->execute(tdbb, this, traHandle); + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// Utility functions + +// raise error if one present +static void checkD(IStatus* st) +{ + if (st->getState() & IStatus::STATE_ERRORS) + ERRD_post(Arg::StatusVector(st)); +} diff --git a/src/dsql/DsqlRequests.h b/src/dsql/DsqlRequests.h new file mode 100644 index 0000000000..e08764922c --- /dev/null +++ b/src/dsql/DsqlRequests.h @@ -0,0 +1,271 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.h + */ + +#ifndef DSQL_REQUESTS_H +#define DSQL_REQUESTS_H + +#include "firebird/Interface.h" +#include "../common/StatusArg.h" +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/NestConst.h" +#include "../common/classes/RefCounted.h" +#include "../jrd/jrd.h" + +namespace Jrd { + + +class DdlNode; +class dsql_dbb; +class DsqlStatement; +class DsqlCompilerScratch; +class DsqlCursor; +class DsqlDmlStatement; +class dsql_par; +class jrd_req; +class jrd_tra; +class JrdStatement; +class SessionManagementNode; +class TransactionNode; + + +class DsqlRequest : public Firebird::PermanentStorage +{ +public: + DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement); + virtual ~DsqlRequest(); + +public: + jrd_tra* getTransaction() + { + return req_transaction; + } + + Firebird::RefPtr getStatement() + { + return statement; + } + + virtual JrdStatement* getJrdStatement() const + { + return nullptr; + } + + virtual jrd_req* getJrdRequest() const + { + return nullptr; + } + + virtual bool isDml() const + { + return false; + } + + virtual DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMeta, ULONG flags) + { + Firebird::Arg::Gds(isc_no_cursor).raise(); + } + + virtual DsqlBatch* openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) + { + (Firebird::Arg::Gds(isc_sqlerr) << + Firebird::Arg::Num(-504) << + Firebird::Arg::Gds(isc_unprepared_stmt) + ).raise(); + } + + virtual void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) = 0; + + virtual void setCursor(thread_db* tdbb, const TEXT* name); + + virtual bool fetch(thread_db* tdbb, UCHAR* buffer); + + virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); + + // Get session-level timeout, milliseconds + unsigned int getTimeout(); + + // Set session-level timeout, milliseconds + void setTimeout(unsigned int timeOut); + + // Get actual timeout, milliseconds + unsigned int getActualTimeout(); + + // Evaluate actual timeout value, consider config- and session-level timeout values, + // setup and start timer + TimeoutTimer* setupTimer(thread_db* tdbb); + + USHORT parseMetadata(Firebird::IMessageMetadata* meta, const Firebird::Array& parameters_list); + + static void destroy(thread_db* tdbb, DsqlRequest* request); + +public: + dsql_dbb* req_dbb; // DSQL attachment + Firebird::RefPtr statement; + Firebird::Array cursors{getPool()}; // Cursor update statements + + jrd_tra* req_transaction = nullptr; // JRD transaction + + Firebird::string req_cursor_name{getPool()}; // Cursor name, if any + DsqlCursor* req_cursor = nullptr; // Open cursor, if any + DsqlBatch* req_batch = nullptr; // Active batch, if any + Firebird::NonPooledMap req_user_descs{getPool()}; // SQLDA data type + + Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time + SINT64 req_fetch_elapsed = 0; // Number of clock ticks spent while fetching rows for this request since we reported it last time + SINT64 req_fetch_rowcount = 0; // Total number of rows returned by this request + bool req_traced = false; // request is traced via TraceAPI + +protected: + unsigned int req_timeout = 0; // query timeout in milliseconds, set by the user + Firebird::RefPtr req_timer; // timeout timer +}; + + +class DsqlDmlRequest final : public DsqlRequest +{ +public: + DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement); + + // Reintroduce method to fake covariant return type with RefPtr. + auto getStatement() + { + return Firebird::RefPtr((DsqlDmlStatement*) statement.getPtr()); + } + + JrdStatement* getJrdStatement() const override; + + jrd_req* getJrdRequest() const override + { + return jrdRequest; + } + + bool isDml() const override + { + return true; + } + + DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMeta, ULONG flags) override; + + DsqlBatch* openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) override; + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + + void setCursor(thread_db* tdbb, const TEXT* name) override; + + bool fetch(thread_db* tdbb, UCHAR* buffer) override; + + void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata) override; + + void mapInOut(Jrd::thread_db* tdbb, bool toExternal, const dsql_msg* message, Firebird::IMessageMetadata* meta, + UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf = nullptr); + +private: + // True, if request could be restarted + bool needRestarts(); + + void doExecute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton); + + // [Re]start part of "request restarts" algorithm + void executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton, bool exec, bool fetch); + +public: + Firebird::Array req_msg_buffers; + +private: + Firebird::RefPtr delayedFormat; + jrd_req* jrdRequest = nullptr; + bool needDelayedFormat = false; + bool firstRowFetched = false; +}; + + +class DsqlDdlRequest final : public DsqlRequest +{ +public: + DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode); + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + DsqlCompilerScratch* internalScratch; + NestConst node; +}; + + +class DsqlTransactionRequest final : public DsqlRequest +{ +public: + DsqlTransactionRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, TransactionNode* aNode); + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + NestConst node; +}; + + +class DsqlSessionManagementRequest final : public DsqlRequest +{ +public: + DsqlSessionManagementRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, + SessionManagementNode* aNode) + : DsqlRequest(pool, dbb, aStatement), + node(aNode) + { + } + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + NestConst node; +}; + + +} // namespace Jrd + +#endif // DSQL_REQUESTS_H diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp new file mode 100644 index 0000000000..cb1e3fc139 --- /dev/null +++ b/src/dsql/DsqlStatements.cpp @@ -0,0 +1,298 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.cpp + */ + +#include "firebird.h" +#include "../dsql/DsqlStatements.h" +#include "../dsql/dsql.h" +#include "../dsql/Nodes.h" +#include "../dsql/DsqlCompilerScratch.h" +#include "../jrd/JrdStatement.h" +#include "../dsql/errd_proto.h" +#include "../dsql/gen_proto.h" +#include "../jrd/cmp_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +// Class DsqlStatement + +// Rethrow an exception with isc_no_meta_update and prefix codes. +void DsqlStatement::rethrowDdlException(status_exception& ex, bool metadataUpdate, DdlNode* node) +{ + Arg::StatusVector newVector; + + if (metadataUpdate) + newVector << Arg::Gds(isc_no_meta_update); + + node->putErrorPrefix(newVector); + + const ISC_STATUS* status = ex.value(); + + if (status[1] == isc_no_meta_update) + status += 2; + + newVector.append(Arg::StatusVector(status)); + + status_exception::raise(newVector); +} + +int DsqlStatement::release() +{ + fb_assert(refCounter.value() > 0); + const int refCnt = --refCounter; + + if (!refCnt) + { + doRelease(); + dsqlAttachment->deletePool(&getPool()); + } + + return refCnt; +} + +void DsqlStatement::doRelease() +{ + setSqlText(nullptr); + setOrgText(nullptr, 0); +} + +void DsqlStatement::setOrgText(const char* ptr, ULONG len) +{ + if (!ptr || !len) + { + orgText = NULL; + return; + } + + const string text(ptr, len); + + if (text == *sqlText) + orgText = sqlText; + else + orgText = FB_NEW_POOL(getPool()) RefString(getPool(), text); +} + + +// DsqlDmlStatement + +void DsqlDmlStatement::doRelease() +{ + if (auto parent = getParentRequest()) + { + FB_SIZE_T pos; + if (parent->cursors.find(this, pos)) + parent->cursors.remove(pos); + } + + if (jrdStatement) + { + thread_db* tdbb = JRD_get_thread_data(); + ThreadStatusGuard status_vector(tdbb); + + try + { + jrdStatement->release(tdbb); + } + catch (Exception&) + {} // no-op + } + + DsqlStatement::doRelease(); +} + +void DsqlDmlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) +{ + { // scope + ContextPoolHolder scratchContext(tdbb, &scratch->getPool()); + node = Node::doDsqlPass(scratch, node); + } + + if (scratch->clientDialect > SQL_DIALECT_V5) + scratch->getStatement()->setBlrVersion(5); + else + scratch->getStatement()->setBlrVersion(4); + + GEN_statement(scratch, node); + + unsigned messageNumber = 0; + + for (auto message : ports) + message->msg_buffer_number = messageNumber++; + + // have the access method compile the statement + +#ifdef DSQL_DEBUG + if (DSQL_debug & 64) + { + dsql_trace("Resulting BLR code for DSQL:"); + gds__trace_raw("Statement:\n"); + gds__trace_raw(getSqlText()->c_str(), getSqlText()->length()); + gds__trace_raw("\nBLR:\n"); + fb_print_blr(scratch->getBlrData().begin(), + (ULONG) scratch->getBlrData().getCount(), + gds__trace_printer, 0, 0); + } +#endif + + FbLocalStatus localStatus; + + // check for warnings + if (tdbb->tdbb_status_vector->getState() & IStatus::STATE_WARNINGS) + { + // save a status vector + fb_utils::copyStatus(&localStatus, tdbb->tdbb_status_vector); + fb_utils::init_status(tdbb->tdbb_status_vector); + } + + ISC_STATUS status = FB_SUCCESS; + + try + { + const auto attachment = scratch->getAttachment()->dbb_attachment; + const auto& blr = scratch->getBlrData(); + const auto& debugData = scratch->getDebugData(); + + jrdStatement = CMP_compile(tdbb, blr.begin(), blr.getCount(), + (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST), + debugData.getCount(), debugData.begin()); + + if (getSqlText()) + jrdStatement->sqlText = getSqlText(); + + fb_assert(jrdStatement->blr.isEmpty()); + + if (attachment->getDebugOptions().getDsqlKeepBlr()) + jrdStatement->blr.insert(0, blr.begin(), blr.getCount()); + } + catch (const Exception&) + { + status = tdbb->tdbb_status_vector->getErrors()[1]; + *traceResult = status == isc_no_priv ? + ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; + } + + // restore warnings (if there are any) + if (localStatus->getState() & IStatus::STATE_WARNINGS) + { + Arg::StatusVector cur(tdbb->tdbb_status_vector->getWarnings()); + Arg::StatusVector saved(localStatus->getWarnings()); + saved << cur; + + tdbb->tdbb_status_vector->setWarnings2(saved.length(), saved.value()); + } + + // free blr memory + scratch->getBlrData().free(); + + if (status) + status_exception::raise(tdbb->tdbb_status_vector); + + node = NULL; +} + +DsqlDmlRequest* DsqlDmlStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlDmlRequest(tdbb, getPool(), dbb, this); +} + + +// DsqlDdlStatement + +DsqlDdlStatement::~DsqlDdlStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +bool DsqlDdlStatement::mustBeReplicated() const +{ + return node->mustBeReplicated(); +} + +void DsqlDdlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) +{ + Database* const dbb = tdbb->getDatabase(); + + scratch->flags |= DsqlCompilerScratch::FLAG_DDL; + + try + { + node = Node::doDsqlPass(scratch, node); + } + catch (status_exception& ex) + { + rethrowDdlException(ex, false, node); + } + + if (dbb->readOnly()) + ERRD_post(Arg::Gds(isc_read_only_database)); + + // In read-only replica, only replicator is allowed to execute DDL. + // As an exception, not replicated DDL statements are also allowed. + if (dbb->isReplica(REPLICA_READ_ONLY) && + !(tdbb->tdbb_flags & TDBB_replicator) && + node->mustBeReplicated()) + { + ERRD_post(Arg::Gds(isc_read_only_trans)); + } + + const auto dbDialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; + + if ((scratch->flags & DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT) && + dbDialect != scratch->clientDialect) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) << + Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(dbDialect)); + } + + if (scratch->clientDialect > SQL_DIALECT_V5) + scratch->getStatement()->setBlrVersion(5); + else + scratch->getStatement()->setBlrVersion(4); + + this->scratch = scratch; +} + +DsqlDdlRequest* DsqlDdlStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlDdlRequest(getPool(), dbb, scratch, node); +} + + +// DsqlTransactionStatement + +DsqlTransactionStatement::~DsqlTransactionStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +void DsqlTransactionStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, + ntrace_result_t* /*traceResult*/) +{ + node = Node::doDsqlPass(scratch, node); + + this->scratch = scratch; +} + +DsqlTransactionRequest* DsqlTransactionStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlTransactionRequest(getPool(), dbb, this, node); +} diff --git a/src/dsql/DsqlStatements.h b/src/dsql/DsqlStatements.h new file mode 100644 index 0000000000..1918e3f82b --- /dev/null +++ b/src/dsql/DsqlStatements.h @@ -0,0 +1,292 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.h + */ + +#ifndef DSQL_STATEMENTS_H +#define DSQL_STATEMENTS_H + +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/NestConst.h" +#include "../jrd/jrd.h" +#include "../jrd/ntrace.h" +#include "../dsql/DsqlRequests.h" + +namespace Jrd { + + +class DdlNode; +class dsql_dbb; +class dsql_msg; +class dsql_par; +class DsqlRequest; +class DsqlCompilerScratch; +class JrdStatement; +class SessionManagementNode; +class TransactionNode; + + +// Compiled statement - shared by multiple requests. +class DsqlStatement : public Firebird::PermanentStorage +{ +public: + enum Type // statement type + { + TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, + TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, + TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, + TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT, + TYPE_RETURNING_CURSOR + }; + + // Statement flags. + static const unsigned FLAG_ORPHAN = 0x01; + static const unsigned FLAG_NO_BATCH = 0x02; + //static const unsigned FLAG_BLR_VERSION4 = 0x04; + //static const unsigned FLAG_BLR_VERSION5 = 0x08; + static const unsigned FLAG_SELECTABLE = 0x10; + + static void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate, DdlNode* node); + +public: + DsqlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment) + : PermanentStorage(p), + dsqlAttachment(aDsqlAttachment), + type(TYPE_SELECT), + flags(0), + blrVersion(5), + ports(p) + { + } + +protected: + virtual ~DsqlStatement() = default; + +public: + int addRef() + { + return ++refCounter; + } + + int release(); + + bool isCursorBased() const + { + switch (type) + { + case TYPE_SELECT: + case TYPE_SELECT_BLOCK: + case TYPE_SELECT_UPD: + case TYPE_RETURNING_CURSOR: + return true; + } + + return false; + } + + Type getType() const { return type; } + void setType(Type value) { type = value; } + + ULONG getFlags() const { return flags; } + void setFlags(ULONG value) { flags = value; } + void addFlags(ULONG value) { flags |= value; } + + unsigned getBlrVersion() const { return blrVersion; } + void setBlrVersion(unsigned value) { blrVersion = value; } + + Firebird::RefStrPtr& getSqlText() { return sqlText; } + const Firebird::RefStrPtr& getSqlText() const { return sqlText; } + void setSqlText(Firebird::RefString* value) { sqlText = value; } + + void setOrgText(const char* ptr, ULONG len); + const Firebird::string& getOrgText() const { return *orgText; } + + Firebird::Array& getPorts() { return ports; } + + dsql_msg* getSendMsg() { return sendMsg; } + const dsql_msg* getSendMsg() const { return sendMsg; } + void setSendMsg(dsql_msg* value) { sendMsg = value; } + + dsql_msg* getReceiveMsg() { return receiveMsg; } + const dsql_msg* getReceiveMsg() const { return receiveMsg; } + void setReceiveMsg(dsql_msg* value) { receiveMsg = value; } + + dsql_par* getEof() { return eof; } + const dsql_par* getEof() const { return eof; } + void setEof(dsql_par* value) { eof = value; } + +public: + virtual JrdStatement* getJrdStatement() const + { + return nullptr; + } + + virtual bool mustBeReplicated() const + { + return false; + } + + virtual bool shouldPreserveScratch() const + { + return true; + } + + virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) = 0; + virtual DsqlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) = 0; + +protected: + virtual void doRelease(); + +protected: + dsql_dbb* dsqlAttachment; + Type type; // Type of statement + ULONG flags; // generic flag + unsigned blrVersion; + Firebird::RefStrPtr sqlText; + Firebird::RefStrPtr orgText; + Firebird::Array ports; // Port messages + dsql_msg* sendMsg = nullptr; // Message to be sent to start request + dsql_msg* receiveMsg = nullptr; // Per record message to be received + dsql_par* eof = nullptr; // End of file parameter + +private: + Firebird::AtomicCounter refCounter; +}; + + +class DsqlDmlStatement final : public DsqlStatement +{ +public: + DsqlDmlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, StmtNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + +public: + JrdStatement* getJrdStatement() const override + { + return jrdStatement; + } + + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlDmlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + + dsql_par* getDbKey() { return dbKey; } + const dsql_par* getDbKey() const { return dbKey; } + void setDbKey(dsql_par* value) { dbKey = value; } + + dsql_par* getRecVersion() { return recVersion; } + const dsql_par* getRecVersion() const { return recVersion; } + void setRecVersion(dsql_par* value) { recVersion = value; } + + dsql_par* getParentRecVersion() { return parentRecVersion; } + const dsql_par* getParentRecVersion() const { return parentRecVersion; } + void setParentRecVersion(dsql_par* value) { parentRecVersion = value; } + + dsql_par* getParentDbKey() { return parentDbKey; } + const dsql_par* getParentDbKey() const { return parentDbKey; } + void setParentDbKey(dsql_par* value) { parentDbKey = value; } + + DsqlDmlRequest* getParentRequest() const { return parentRequest; } + void setParentRequest(DsqlDmlRequest* value) { parentRequest = value; } + +protected: + void doRelease() override; + +private: + NestConst node; + JrdStatement* jrdStatement = nullptr; + dsql_par* dbKey = nullptr; // Database key for current of + dsql_par* recVersion = nullptr; // Record Version for current of + dsql_par* parentRecVersion = nullptr; // parent record version + dsql_par* parentDbKey = nullptr; // Parent database key for current of + DsqlDmlRequest* parentRequest = nullptr; // Source request, if cursor update +}; + + +class DsqlDdlStatement final : public DsqlStatement +{ +public: + DsqlDdlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, DdlNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlDdlStatement(); + +public: + bool mustBeReplicated() const override; + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlDdlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +class DsqlTransactionStatement final : public DsqlStatement +{ +public: + DsqlTransactionStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, TransactionNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlTransactionStatement(); + +public: + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlTransactionRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +class DsqlSessionManagementStatement final : public DsqlStatement +{ +public: + DsqlSessionManagementStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, SessionManagementNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlSessionManagementStatement(); + +public: + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlSessionManagementRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +} // namespace Jrd + +#endif // DSQL_STATEMENTS_H diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index fbc0277ef8..e2192a7724 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -213,7 +213,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); return this; } @@ -283,7 +283,7 @@ public: return this; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const = 0; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const = 0; }; @@ -300,12 +300,12 @@ public: { Node::dsqlPass(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SESSION_MANAGEMENT); return this; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const = 0; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const = 0; }; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 71d40ac857..be7eed2a61 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -221,21 +221,14 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // items for (unsigned i = 0; i < items->getCount(); ++i) { - DsqlCompiledStatement* itemStatement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); - - DsqlCompilerScratch* itemScratch = (*items)[i].dsqlScratch = - FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), - dsqlScratch->getTransaction(), itemStatement); - - itemScratch->clientDialect = dsqlScratch->clientDialect; - itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; - itemScratch->package = name; + DdlNode* ddlNode; switch ((*items)[i].type) { case CreateAlterPackageNode::Item::FUNCTION: { CreateAlterFunctionNode* const fun = (*items)[i].function; + ddlNode = fun; if (functionNames.exist(fun->name)) { @@ -249,13 +242,13 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) fun->alter = true; fun->package = name; - fun->dsqlPass(itemScratch); break; } case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* const proc = (*items)[i].procedure; + ddlNode = proc; if (procedureNames.exist(proc->name)) { @@ -269,10 +262,24 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) proc->alter = true; proc->package = name; - proc->dsqlPass(itemScratch); break; } + + default: + fb_assert(false); } + + auto itemStatement = FB_NEW_POOL(pool) DsqlDdlStatement(pool, dsqlScratch->getAttachment(), ddlNode); + + auto itemScratch = (*items)[i].dsqlScratch = + FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), + dsqlScratch->getTransaction(), itemStatement); + + itemScratch->clientDialect = dsqlScratch->clientDialect; + itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; + itemScratch->package = name; + + ddlNode->dsqlPass(itemScratch); } return DdlNode::dsqlPass(dsqlScratch); @@ -616,21 +623,14 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) for (unsigned j = 0; j < arrays[i]->getCount(); ++j) { - DsqlCompiledStatement* itemStatement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); - - DsqlCompilerScratch* itemScratch = (*arrays[i])[j].dsqlScratch = - FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), - dsqlScratch->getTransaction(), itemStatement); - - itemScratch->clientDialect = dsqlScratch->clientDialect; - itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; - itemScratch->package = name; + DdlNode* ddlNode; switch ((*arrays[i])[j].type) { case CreateAlterPackageNode::Item::FUNCTION: { CreateAlterFunctionNode* const fun = (*arrays[i])[j].function; + ddlNode = fun; if (functionNames[i].exist(fun->name)) { @@ -648,14 +648,13 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (arrays[i] == items) fun->alter = true; - fun->dsqlPass(itemScratch); - break; } case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* const proc = (*arrays[i])[j].procedure; + ddlNode = proc; if (procedureNames[i].exist(proc->name)) { @@ -673,11 +672,24 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (arrays[i] == items) proc->alter = true; - proc->dsqlPass(itemScratch); - break; } + + default: + fb_assert(false); } + + auto itemStatement = FB_NEW_POOL(pool) DsqlDdlStatement(pool, dsqlScratch->getAttachment(), ddlNode); + + auto itemScratch = (*arrays[i])[j].dsqlScratch = + FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), + dsqlScratch->getTransaction(), itemStatement); + + itemScratch->clientDialect = dsqlScratch->clientDialect; + itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; + itemScratch->package = name; + + ddlNode->dsqlPass(itemScratch); } } diff --git a/src/dsql/Parser.cpp b/src/dsql/Parser.cpp index bacacc6378..af0f9ab9f4 100644 --- a/src/dsql/Parser.cpp +++ b/src/dsql/Parser.cpp @@ -98,10 +98,10 @@ namespace } -Parser::Parser(thread_db* tdbb, MemoryPool& pool, DsqlCompilerScratch* aScratch, - USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, - SSHORT characterSet) +Parser::Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, + USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT characterSet) : PermanentStorage(pool), + statementPool(aStatementPool), scratch(aScratch), client_dialect(aClientDialect), db_dialect(aDbDialect), @@ -172,7 +172,7 @@ Parser::~Parser() } -dsql_req* Parser::parse() +DsqlStatement* Parser::parse() { if (parseAux() != 0) { @@ -182,7 +182,7 @@ dsql_req* Parser::parse() transformString(lex.start, lex.end - lex.start, transformedString); - return DSQL_parse; + return parsedStatement; } diff --git a/src/dsql/Parser.h b/src/dsql/Parser.h index da561e1dc1..28e22323f5 100644 --- a/src/dsql/Parser.h +++ b/src/dsql/Parser.h @@ -130,13 +130,12 @@ public: static const int MAX_TOKEN_LEN = 256; public: - Parser(thread_db* tdbb, MemoryPool& pool, DsqlCompilerScratch* aScratch, USHORT aClientDialect, - USHORT aDbDialect, const TEXT* string, size_t length, - SSHORT characterSet); + Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, + USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT characterSet); ~Parser(); public: - dsql_req* parse(); + DsqlStatement* parse(); const Firebird::string& getTransformedString() const { @@ -233,11 +232,6 @@ private: return cmpNode; } - MemoryPool& getStatementPool() - { - return scratch->getStatement()->getPool(); - } - void yyReducePosn(YYPOSN& ret, YYPOSN* termPosns, YYSTYPE* termVals, int termNo, int stkPos, int yychar, YYPOSN& yyposn, void*); @@ -376,6 +370,7 @@ private: // end - defined in btyacc_fb.ske private: + MemoryPool* statementPool; DsqlCompilerScratch* scratch; USHORT client_dialect; USHORT db_dialect; @@ -385,7 +380,7 @@ private: Firebird::string transformedString; Firebird::GenericMap > strMarks; bool stmt_ambiguous; - dsql_req* DSQL_parse; + DsqlStatement* parsedStatement; // These value/posn are taken from the lexer YYSTYPE yylval; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 1919a984e7..5b1d4c3594 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -72,8 +72,8 @@ namespace Jrd { template static void dsqlExplodeFields(dsql_rel* relation, Array >& fields, bool includeComputed); -static dsql_par* dsqlFindDbKey(const dsql_req*, const RelationSourceNode*); -static dsql_par* dsqlFindRecordVersion(const dsql_req*, const RelationSourceNode*); +static dsql_par* dsqlFindDbKey(const DsqlDmlStatement*, const RelationSourceNode*); +static dsql_par* dsqlFindRecordVersion(const DsqlDmlStatement*, const RelationSourceNode*); static void dsqlGenEofAssignment(DsqlCompilerScratch* dsqlScratch, SSHORT value); static void dsqlGenReturning(DsqlCompilerScratch* dsqlScratch, ReturningClause* returning, Nullable localTableNumber); @@ -1697,7 +1697,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (prevDecl) dsqlScratch->putSubFunction(this, true); - DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); + auto statement = FB_NEW_POOL(pool) DsqlDmlStatement(pool, dsqlScratch->getAttachment(), dsqlBlock); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) statement->setBlrVersion(5); @@ -1709,7 +1709,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); @@ -1729,7 +1729,7 @@ void DeclareSubFuncNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlBlock) // forward decl return; - GEN_request(blockScratch, dsqlBlock); + GEN_statement(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subfunc_decl); dsqlScratch->appendNullString(name.c_str()); @@ -2036,7 +2036,7 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (prevDecl) dsqlScratch->putSubProcedure(this, true); - DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); + auto statement = FB_NEW_POOL(pool) DsqlDmlStatement(pool, dsqlScratch->getAttachment(), dsqlBlock); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) statement->setBlrVersion(5); @@ -2048,7 +2048,7 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); @@ -2068,7 +2068,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlBlock) // forward decl return; - GEN_request(blockScratch, dsqlBlock); + GEN_statement(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subproc_decl); dsqlScratch->appendNullString(name.c_str()); @@ -2076,7 +2076,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(SUB_ROUTINE_TYPE_PSQL); dsqlScratch->appendUChar( - blockScratch->getStatement()->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ? 1 : 0); + blockScratch->getStatement()->getFlags() & DsqlStatement::FLAG_SELECTABLE ? 1 : 0); genParameters(dsqlScratch, dsqlBlock->parameters); genParameters(dsqlScratch, dsqlBlock->returns); @@ -2291,7 +2291,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? - DsqlCompiledStatement::TYPE_DELETE_CURSOR : DsqlCompiledStatement::TYPE_DELETE); + DsqlStatement::TYPE_DELETE_CURSOR : DsqlStatement::TYPE_DELETE); // Generate record selection expression. @@ -2990,7 +2990,7 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } if (!dsqlScratch->isPsql()) - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE); ExecProcedureNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName); node->dsqlProcedure = procedure; @@ -3112,7 +3112,7 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) { const dsql_msg* message = NULL; - if (dsqlScratch->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_PROCEDURE) + if (dsqlScratch->getStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE) { if ((message = dsqlScratch->getStatement()->getReceiveMsg())) { @@ -4267,12 +4267,12 @@ const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, Exe ExecBlockNode* ExecBlockNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); if (returns.hasData()) - statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK); + statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); else - statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK); + statement->setType(DsqlStatement::TYPE_EXEC_BLOCK); dsqlScratch->flags |= DsqlCompilerScratch::FLAG_BLOCK; @@ -4404,7 +4404,7 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); dsqlScratch->appendUChar(blr_begin); @@ -4503,9 +4503,9 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) stmtNode->genBlr(dsqlScratch); if (returns.hasData()) - statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK); + statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); else - statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK); + statement->setType(DsqlStatement::TYPE_EXEC_BLOCK); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); @@ -5969,8 +5969,8 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { // Describe it as TYPE_RETURNING_CURSOR if RETURNING is present or as INSERT otherwise. dsqlScratch->getStatement()->setType(returning ? - DsqlCompiledStatement::TYPE_RETURNING_CURSOR : - DsqlCompiledStatement::TYPE_INSERT); + DsqlStatement::TYPE_RETURNING_CURSOR : + DsqlStatement::TYPE_INSERT); } // Setup the main node. @@ -6530,7 +6530,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up } dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? - DsqlCompiledStatement::TYPE_UPDATE_CURSOR : DsqlCompiledStatement::TYPE_UPDATE); + DsqlStatement::TYPE_UPDATE_CURSOR : DsqlStatement::TYPE_UPDATE); doDsqlPass(dsqlScratch, node->dsqlRelation, relation, false); dsql_ctx* mod_context = dsqlGetContext(node->dsqlRelation); @@ -7461,7 +7461,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, { DsqlContextStack::AutoRestore autoContext(*dsqlScratch->context); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_INSERT); const auto node = FB_NEW_POOL(dsqlScratch->getPool()) StoreNode(dsqlScratch->getPool()); node->overrideClause = overrideClause; @@ -8164,8 +8164,8 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (dsqlForUpdate) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SELECT_UPD); - dsqlScratch->getStatement()->addFlags(DsqlCompiledStatement::FLAG_NO_BATCH); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SELECT_UPD); + dsqlScratch->getStatement()->addFlags(DsqlStatement::FLAG_NO_BATCH); } else { @@ -8178,7 +8178,7 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (rseNode->dsqlOrder || rseNode->dsqlDistinct) { dsqlScratch->getStatement()->setFlags( - dsqlScratch->getStatement()->getFlags() & ~DsqlCompiledStatement::FLAG_NO_BATCH); + dsqlScratch->getStatement()->getFlags() & ~DsqlStatement::FLAG_NO_BATCH); } } @@ -8204,7 +8204,7 @@ void SelectNode::genBlr(DsqlCompilerScratch* dsqlScratch) RseNode* const rse = nodeAs(dsqlRse); fb_assert(rse); - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); // Set up parameter for things in the select list. ValueListNode* list = rse->dsqlSelectList; @@ -8532,7 +8532,7 @@ DmlNode* SuspendNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getStatement(); if (dsqlScratch->flags & (DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)) { @@ -8554,7 +8554,7 @@ SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) Arg::Gds(isc_dsql_unsupported_in_auto_trans) << Arg::Str("SUSPEND")); } - statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE); + statement->addFlags(DsqlStatement::FLAG_SELECTABLE); return this; } @@ -8790,7 +8790,7 @@ const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_START_TRANS); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_START_TRANS); // Generate tpb for set transaction. Use blr string of dsqlScratch. // If a value is not specified, default is not stuffed, let the engine handle it. @@ -8869,7 +8869,7 @@ SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratc return this; } -void SetTransactionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void SetTransactionNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const { JRD_start_transaction(tdbb, &request->req_transaction, request->req_dbb->dbb_attachment, tpb.getCount(), tpb.begin()); @@ -8906,7 +8906,7 @@ void SetTransactionNode::genTableLock(DsqlCompilerScratch* dsqlScratch, //-------------------- -void SessionResetNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const +void SessionResetNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8917,7 +8917,7 @@ void SessionResetNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** tra //-------------------- -void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetRoleNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8948,7 +8948,7 @@ SetDebugOptionNode::SetDebugOptionNode(MemoryPool& pool, MetaName* aName, ExprNo { } -void SetDebugOptionNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDebugOptionNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); auto& debugOptions = tdbb->getAttachment()->getDebugOptions(); @@ -8984,7 +8984,7 @@ SetDecFloatRoundNode::SetDecFloatRoundNode(MemoryPool& pool, MetaName* name) rndMode = mode->val; } -void SetDecFloatRoundNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDecFloatRoundNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -9004,7 +9004,7 @@ void SetDecFloatTrapsNode::trap(MetaName* name) traps |= trap->val; } -void SetDecFloatTrapsNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDecFloatTrapsNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -9027,7 +9027,7 @@ SessionManagementNode* SetBindNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } -void SetBindNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetBindNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); @@ -9091,7 +9091,7 @@ string SetSessionNode::internalPrint(NodePrinter& printer) const return "SetSessionNode"; } -void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetSessionNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { Attachment* att = tdbb->getAttachment(); @@ -9111,7 +9111,7 @@ void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*tra //-------------------- -void SetTimeZoneNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetTimeZoneNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { Attachment* const attachment = tdbb->getAttachment(); @@ -9363,9 +9363,9 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) node->modifyNode = nodeAs(node->modifyNode->internalDsqlPass(dsqlScratch, true)); fb_assert(node->modifyNode); - // If RETURNING is present, type is already DsqlCompiledStatement::TYPE_EXEC_PROCEDURE. + // If RETURNING is present, type is already DsqlStatement::TYPE_EXEC_PROCEDURE. if (!returning) - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_INSERT); return SavepointEncloseNode::make(dsqlScratch->getPool(), dsqlScratch, node); } @@ -9439,19 +9439,19 @@ CommitRollbackNode* CommitRollbackNode::dsqlPass(DsqlCompilerScratch* dsqlScratc { case CMD_COMMIT: dsqlScratch->getStatement()->setType(retain ? - DsqlCompiledStatement::TYPE_COMMIT_RETAIN : DsqlCompiledStatement::TYPE_COMMIT); + DsqlStatement::TYPE_COMMIT_RETAIN : DsqlStatement::TYPE_COMMIT); break; case CMD_ROLLBACK: dsqlScratch->getStatement()->setType(retain ? - DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN : DsqlCompiledStatement::TYPE_ROLLBACK); + DsqlStatement::TYPE_ROLLBACK_RETAIN : DsqlStatement::TYPE_ROLLBACK); break; } return this; } -void CommitRollbackNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void CommitRollbackNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const { if (retain) { @@ -9499,11 +9499,11 @@ Firebird::string UserSavepointNode::internalPrint(NodePrinter& printer) const UserSavepointNode* UserSavepointNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SAVEPOINT); + dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SAVEPOINT); return this; } -void UserSavepointNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*transaction*/) const +void UserSavepointNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*transaction*/) const { jrd_tra* const transaction = request->req_transaction; fb_assert(!(transaction->tra_flags & TRA_system)); @@ -9610,12 +9610,11 @@ static void dsqlExplodeFields(dsql_rel* relation, Array >& fields, } // Find dbkey for named relation in statement's saved dbkeys. -static dsql_par* dsqlFindDbKey(const dsql_req* request, const RelationSourceNode* relation_name) +static dsql_par* dsqlFindDbKey(const DsqlDmlStatement* statement, const RelationSourceNode* relation_name) { - DEV_BLKCHK(request, dsql_type_req); DEV_BLKCHK(relation_name, dsql_type_nod); - const dsql_msg* message = request->getStatement()->getReceiveMsg(); + const dsql_msg* message = statement->getReceiveMsg(); dsql_par* candidate = NULL; const MetaName& relName = relation_name->dsqlName; @@ -9636,11 +9635,9 @@ static dsql_par* dsqlFindDbKey(const dsql_req* request, const RelationSourceNode } // Find record version for relation in statement's saved record version. -static dsql_par* dsqlFindRecordVersion(const dsql_req* request, const RelationSourceNode* relation_name) +static dsql_par* dsqlFindRecordVersion(const DsqlDmlStatement* statement, const RelationSourceNode* relation_name) { - DEV_BLKCHK(request, dsql_type_req); - - const dsql_msg* message = request->getStatement()->getReceiveMsg(); + const dsql_msg* message = statement->getReceiveMsg(); dsql_par* candidate = NULL; const MetaName& relName = relation_name->dsqlName; @@ -9993,7 +9990,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const // Lookup parent dsqlScratch - dsql_req* const* const symbol = dsqlScratch->getAttachment()->dbb_cursors.get(cursor.c_str()); + const auto* const symbol = dsqlScratch->getAttachment()->dbb_cursors.get(cursor.c_str()); if (!symbol) { @@ -10003,12 +10000,12 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_not_found) << cursor); } - dsql_req* parent = *symbol; + auto parent = *symbol; // Verify that the cursor is appropriate and updatable - dsql_par* source = dsqlFindDbKey(parent, relation_name); - dsql_par* rv_source = dsqlFindRecordVersion(parent, relation_name); + dsql_par* source = dsqlFindDbKey(parent->getStatement(), relation_name); + dsql_par* rv_source = dsqlFindRecordVersion(parent->getStatement(), relation_name); if (!source || !rv_source) { @@ -10017,7 +10014,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_update_err) << cursor); } - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + const auto statement = static_cast(dsqlScratch->getStatement()); statement->setParentRequest(parent); statement->setParentDbKey(source); @@ -10293,7 +10290,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d if (!dsqlScratch->isPsql()) { dsqlScratch->getStatement()->setType(singleton ? - DsqlCompiledStatement::TYPE_EXEC_PROCEDURE : DsqlCompiledStatement::TYPE_RETURNING_CURSOR); + DsqlStatement::TYPE_EXEC_PROCEDURE : DsqlStatement::TYPE_RETURNING_CURSOR); } return node; diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index d8fd591ca8..e8b3e8250f 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -1473,7 +1473,7 @@ public: // Save and reset the statement type, as SessionManagementNode sets it to TYPE_SESSION_MANAGEMENT but // we are a DML statement. - DsqlCompiledStatement::Type statementType = dsqlScratch->getStatement()->getType(); + DsqlStatement::Type statementType = dsqlScratch->getStatement()->getType(); wrapped->dsqlPass(dsqlScratch); dsqlScratch->getStatement()->setType(statementType); @@ -1566,7 +1566,7 @@ public: } virtual SetTransactionNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; private: void genTableLock(DsqlCompilerScratch* dsqlScratch, const RestrictionOption& tblLock, @@ -1607,7 +1607,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual CommitRollbackNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; private: const Command command; @@ -1637,7 +1637,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual UserSavepointNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; public: const Command command; @@ -1661,7 +1661,7 @@ public: return "SessionResetNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; }; @@ -1693,7 +1693,7 @@ public: return "SetRoleNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: bool trusted; @@ -1714,7 +1714,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; private: Type m_type; @@ -1738,7 +1738,7 @@ public: return "SetDebugOptionNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; private: MetaName name; @@ -1761,7 +1761,7 @@ public: return "SetDecFloatRoundNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: USHORT rndMode; @@ -1787,7 +1787,7 @@ public: return "SetDecFloatTrapsNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; void trap(MetaName* name); @@ -1818,7 +1818,7 @@ public: } virtual SessionManagementNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: dsql_fld* from; @@ -1854,7 +1854,7 @@ public: return "SetTimeZoneNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: Firebird::string str; diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 43661f79ee..f5c67ffe3d 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -57,6 +57,7 @@ #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../yvalve/gds_proto.h" +#include "../jrd/exe_proto.h" #include "../jrd/inf_proto.h" #include "../jrd/ini_proto.h" #include "../jrd/intl_proto.h" @@ -80,30 +81,15 @@ using namespace Jrd; using namespace Firebird; -static ULONG get_request_info(thread_db*, dsql_req*, ULONG, UCHAR*); +static ULONG get_request_info(thread_db*, DsqlRequest*, ULONG, UCHAR*); static dsql_dbb* init(Jrd::thread_db*, Jrd::Attachment*); -static dsql_req* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); -static dsql_req* prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); +static DsqlRequest* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); +static RefPtr prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, + bool, ntrace_result_t* traceResult); static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const); -static void release_statement(DsqlCompiledStatement* statement); -static void sql_info(thread_db*, dsql_req*, ULONG, const UCHAR*, ULONG, UCHAR*); +static void sql_info(thread_db*, DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*); static UCHAR* var_info(const dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*, const UCHAR* const, USHORT, bool); -static void checkD(IStatus*); - -static inline bool reqTypeWithCursor(DsqlCompiledStatement::Type type) -{ - switch (type) - { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - return true; - } - - return false; -} #ifdef DSQL_DEBUG unsigned DSQL_debug = 0; @@ -131,7 +117,7 @@ dsql_dbb::~dsql_dbb() // Execute a dynamic SQL statement. void DSQL_execute(thread_db* tdbb, jrd_tra** tra_handle, - dsql_req* request, + DsqlRequest* request, IMessageMetadata* in_meta, const UCHAR* in_msg, IMessageMetadata* out_meta, UCHAR* out_msg) { @@ -139,9 +125,9 @@ void DSQL_execute(thread_db* tdbb, Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -150,20 +136,20 @@ void DSQL_execute(thread_db* tdbb, // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) + statement->getType() != DsqlStatement::TYPE_START_TRANS && + statement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); } // A select with a non zero output length is a singleton select - const bool singleton = reqTypeWithCursor(statement->getType()) && out_msg; + const bool singleton = statement->isCursorBased() && out_msg; // If the request is a SELECT or blob statement then this is an open. // Make sure the cursor is not already open. - if (reqTypeWithCursor(statement->getType())) + if (statement->isCursorBased()) { if (request->req_cursor) { @@ -180,154 +166,6 @@ void DSQL_execute(thread_db* tdbb, } -// Open a dynamic SQL cursor. -DsqlCursor* DSQL_open(thread_db* tdbb, - jrd_tra** tra_handle, - dsql_req* request, - IMessageMetadata* in_meta, const UCHAR* in_msg, - IMessageMetadata* out_meta, ULONG flags) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - - const DsqlCompiledStatement* statement = request->getStatement(); - - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_bad_req_handle)); - } - - // Validate transaction handle - - if (!*tra_handle) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_bad_trans_handle)); - } - - // Validate statement type - - if (!reqTypeWithCursor(statement->getType())) - Arg::Gds(isc_no_cursor).raise(); - - // Validate cursor or batch being not already open - - if (request->req_cursor) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_cursor_open_err)); - } - - if (request->req_batch) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_batch_open)); - } - - request->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, NULL, false); - - request->req_cursor = FB_NEW_POOL(request->getPool()) DsqlCursor(request, flags); - return request->req_cursor; -} - - -// Provide backward-compatibility -void DsqlDmlRequest::setDelayedFormat(thread_db* tdbb, IMessageMetadata* metadata) -{ - if (!needDelayedFormat) - { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); - } - - needDelayedFormat = false; - delayedFormat = metadata; -} - - -// Fetch next record from a dynamic SQL cursor. -bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &getPool()); - - const DsqlCompiledStatement* statement = getStatement(); - - // if the cursor isn't open, we've got a problem - if (reqTypeWithCursor(statement->getType())) - { - if (!req_cursor) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_dsql_cursor_err) << - Arg::Gds(isc_dsql_cursor_not_open)); - } - } - - if (!req_request) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_unprepared_stmt)); - } - - dsql_msg* message = (dsql_msg*) statement->getReceiveMsg(); - - if (delayedFormat && message) - { - parseMetadata(delayedFormat, message->msg_parameters); - delayedFormat = NULL; - } - - // Set up things for tracing this call - Jrd::Attachment* att = req_dbb->dbb_attachment; - TraceDSQLFetch trace(att, this); - - thread_db::TimerGuard timerGuard(tdbb, req_timer, false); - if (req_timer && req_timer->expired()) - tdbb->checkCancelState(); - - UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - if (!firstRowFetched && needRestarts()) - { - // Note: tra_handle can't be changed by executeReceiveWithRestarts below - // and outMetadata and outMsg in not used there, so passing NULL's is safe. - jrd_tra* tra = req_transaction; - - executeReceiveWithRestarts(tdbb, &tra, NULL, NULL, false, false, true); - fb_assert(tra == req_transaction); - } - else - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer); - - firstRowFetched = true; - - const dsql_par* const eof = statement->getEof(); - const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; - const bool eofReached = eof && !(*eofPtr); - - if (eofReached) - { - if (req_timer) - req_timer->stop(); - - trace.fetch(true, ITracePlugin::RESULT_SUCCESS); - return false; - } - - if (msgBuffer) - mapInOut(tdbb, true, message, NULL, msgBuffer); - - trace.fetch(false, ITracePlugin::RESULT_SUCCESS); - return true; -} - - /** DSQL_free_statement @@ -340,30 +178,25 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) @param option **/ -void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option) +void DSQL_free_statement(thread_db* tdbb, DsqlRequest* request, USHORT option) { SET_TDBB(tdbb); Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); + + fb_assert(!(option & DSQL_unprepare)); // handled in y-valve if (option & DSQL_drop) { // Release everything associated with the request - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } - /* - else if (option & DSQL_unprepare) - { - // Release everything but the request itself - dsql_req::destroy(tdbb, request, false); - } - */ else if (option & DSQL_close) { // Just close the cursor associated with the request - if (reqTypeWithCursor(statement->getType())) + if (statement->isCursorBased()) { if (!request->req_cursor) { @@ -396,7 +229,7 @@ void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option) @param buffer **/ -dsql_req* DSQL_prepare(thread_db* tdbb, +DsqlRequest* DSQL_prepare(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction, ULONG length, const TEXT* string, USHORT dialect, unsigned prepareFlags, Array* items, Array* buffer, @@ -405,7 +238,7 @@ dsql_req* DSQL_prepare(thread_db* tdbb, SET_TDBB(tdbb); dsql_dbb* database = init(tdbb, attachment); - dsql_req* request = NULL; + DsqlRequest* request = NULL; try { @@ -416,8 +249,8 @@ dsql_req* DSQL_prepare(thread_db* tdbb, // Can not prepare a CREATE DATABASE/SCHEMA statement - const DsqlCompiledStatement* statement = request->getStatement(); - if (statement->getType() == DsqlCompiledStatement::TYPE_CREATE_DB) + const DsqlStatement* statement = request->getStatement(); + if (statement->getType() == DsqlStatement::TYPE_CREATE_DB) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-530) << Arg::Gds(isc_dsql_crdb_prepare_err)); @@ -437,89 +270,13 @@ dsql_req* DSQL_prepare(thread_db* tdbb, if (request) { Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } throw; } } -// Set a cursor name for a dynamic request. -void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &getPool()); - - const size_t MAX_CURSOR_LENGTH = 132 - 1; - string cursor = name; - - if (cursor.hasData() && cursor[0] == '\"') - { - // Quoted cursor names eh? Strip'em. - // Note that "" will be replaced with ". - // The code is very strange, because it doesn't check for "" really - // and thus deletes one isolated " in the middle of the cursor. - for (string::iterator i = cursor.begin(); i < cursor.end(); ++i) - { - if (*i == '\"') - cursor.erase(i); - } - } - else // not quoted name - { - const string::size_type i = cursor.find(' '); - if (i != string::npos) - cursor.resize(i); - - cursor.upper(); - } - - USHORT length = (USHORT) fb_utils::name_length(cursor.c_str()); - - if (!length) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_invalid)); - } - - if (length > MAX_CURSOR_LENGTH) - length = MAX_CURSOR_LENGTH; - - cursor.resize(length); - - // If there already is a different cursor by the same name, bitch - - dsql_req* const* symbol = req_dbb->dbb_cursors.get(cursor); - if (symbol) - { - if (this == *symbol) - return; - - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_redefined) << cursor); - } - - // If there already is a cursor and its name isn't the same, ditto. - // We already know there is no cursor by this name in the hash table - - if (req_cursor && req_cursor_name.hasData()) - { - fb_assert(!symbol); - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_redefined) << req_cursor_name); - } - - if (req_cursor_name.hasData()) - req_dbb->dbb_cursors.remove(req_cursor_name); - req_cursor_name = cursor; - req_dbb->dbb_cursors.put(cursor, this); -} - - /** DSQL_sql_info @@ -536,7 +293,7 @@ void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) **/ void DSQL_sql_info(thread_db* tdbb, - dsql_req* request, + DsqlRequest* request, ULONG item_length, const UCHAR* items, ULONG info_length, UCHAR* info) { @@ -558,20 +315,20 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr SET_TDBB(tdbb); dsql_dbb* const database = init(tdbb, attachment); - dsql_req* request = NULL; + DsqlRequest* request = NULL; try { request = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, isInternalRequest); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) + statement->getType() != DsqlStatement::TYPE_START_TRANS && + statement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); @@ -580,7 +337,7 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr Jrd::ContextPoolHolder context(tdbb, &request->getPool()); // A select having cursor is a singleton select when executed immediate - const bool singleton = reqTypeWithCursor(statement->getType()); + const bool singleton = statement->isCursorBased(); if (singleton && !(out_msg && out_meta)) { ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << @@ -591,515 +348,20 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } catch (const Exception&) { if (request) { Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, request); } throw; } } -void DsqlDmlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) -{ - { // scope - Jrd::ContextPoolHolder scratchContext(tdbb, &scratch->getPool()); - node = Node::doDsqlPass(scratch, node); - } - - if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); - else - scratch->getStatement()->setBlrVersion(4); - - GEN_request(scratch, node); - - // Create the messages buffers - for (FB_SIZE_T i = 0; i < scratch->ports.getCount(); ++i) - { - dsql_msg* message = scratch->ports[i]; - - // Allocate buffer for message - const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1; - UCHAR* msgBuffer = FB_NEW_POOL(scratch->getStatement()->getPool()) UCHAR[newLen]; - msgBuffer = FB_ALIGN(msgBuffer, FB_DOUBLE_ALIGN); - message->msg_buffer_number = req_msg_buffers.add(msgBuffer); - } - - // have the access method compile the statement - -#ifdef DSQL_DEBUG - if (DSQL_debug & 64) - { - dsql_trace("Resulting BLR code for DSQL:"); - gds__trace_raw("Statement:\n"); - gds__trace_raw(statement->getSqlText()->c_str(), statement->getSqlText()->length()); - gds__trace_raw("\nBLR:\n"); - fb_print_blr(scratch->getBlrData().begin(), - (ULONG) scratch->getBlrData().getCount(), - gds__trace_printer, 0, 0); - } -#endif - - FbLocalStatus localStatus; - - // check for warnings - if (tdbb->tdbb_status_vector->getState() & IStatus::STATE_WARNINGS) - { - // save a status vector - fb_utils::copyStatus(&localStatus, tdbb->tdbb_status_vector); - fb_utils::init_status(tdbb->tdbb_status_vector); - } - - ISC_STATUS status = FB_SUCCESS; - - try - { - JRD_compile(tdbb, scratch->getAttachment()->dbb_attachment, &req_request, - scratch->getBlrData().getCount(), scratch->getBlrData().begin(), - statement->getSqlText(), - scratch->getDebugData().getCount(), scratch->getDebugData().begin(), - (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST)); - } - catch (const Exception&) - { - status = tdbb->tdbb_status_vector->getErrors()[1]; - *traceResult = status == isc_no_priv ? - ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; - } - - // restore warnings (if there are any) - if (localStatus->getState() & IStatus::STATE_WARNINGS) - { - Arg::StatusVector cur(tdbb->tdbb_status_vector->getWarnings()); - Arg::StatusVector saved(localStatus->getWarnings()); - saved << cur; - - tdbb->tdbb_status_vector->setWarnings2(saved.length(), saved.value()); - } - - // free blr memory - scratch->getBlrData().free(); - - if (status) - status_exception::raise(tdbb->tdbb_status_vector); - - // We don't need the scratch pool anymore. Tell our caller to delete it. - node = NULL; - *destroyScratchPool = true; -} - -bool DsqlDmlRequest::needRestarts() -{ - return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency)); -}; - -// Execute a dynamic SQL statement -void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - firstRowFetched = false; - const dsql_msg* message = statement->getSendMsg(); - - if (!message) - JRD_start(tdbb, req_request, req_transaction); - else - { - UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_start_and_send(tdbb, req_request, req_transaction, message->msg_number, - message->msg_length, msgBuffer); - } - - // Selectable execute block should get the "proc fetch" flag assigned, - // which ensures that the savepoint stack is preserved while suspending - if (statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK) - req_request->req_flags |= req_proc_fetch; - - // TYPE_EXEC_BLOCK has no outputs so there are no out_msg - // supplied from client side, but TYPE_EXEC_BLOCK requires - // 2-byte message for EOS synchronization - const bool isBlock = (statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK); - - message = statement->getReceiveMsg(); - - if (outMetadata == DELAYED_OUT_FORMAT) - { - needDelayedFormat = true; - outMetadata = NULL; - } - - if (outMetadata && message) - parseMetadata(outMetadata, message->msg_parameters); - - if ((outMsg && message) || isBlock) - { - UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2]; - dsql_msg temp_msg(*getDefaultMemoryPool()); - - // Insure that the metadata for the message is parsed, regardless of - // whether anything is found by the call to receive. - - UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - - if (!outMetadata && isBlock) - { - message = &temp_msg; - temp_msg.msg_number = 1; - temp_msg.msg_length = 2; - msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); - } - - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, msgBuffer); - - if (outMsg) - mapInOut(tdbb, true, message, NULL, outMsg); - - // if this is a singleton select, make sure there's in fact one record - - if (singleton) - { - USHORT counter; - - // Create a temp message buffer and try two more receives. - // If both succeed then the first is the next record and the - // second is either another record or the end of record message. - // In either case, there's more than one record. - - UCHAR* message_buffer = (UCHAR*) gds__alloc(message->msg_length); - - ISC_STATUS status = FB_SUCCESS; - FbLocalStatus localStatus; - - for (counter = 0; counter < 2 && !status; counter++) - { - localStatus->init(); - AutoSetRestore autoStatus(&tdbb->tdbb_status_vector, &localStatus); - - try - { - JRD_receive(tdbb, req_request, message->msg_number, - message->msg_length, message_buffer); - status = FB_SUCCESS; - } - catch (Exception&) - { - status = tdbb->tdbb_status_vector->getErrors()[1]; - } - } - - gds__free(message_buffer); - - // two successful receives means more than one record - // a req_sync error on the first pass above means no records - // a non-req_sync error on any of the passes above is an error - - if (!status) - status_exception::raise(Arg::Gds(isc_sing_select_err)); - else if (status == isc_req_sync && counter == 1) - status_exception::raise(Arg::Gds(isc_stream_eof)); - else if (status != isc_req_sync) - status_exception::raise(&localStatus); - } - } - - switch (statement->getType()) - { - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: - if (!req_request->req_records_updated) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << - Arg::Gds(isc_deadlock) << - Arg::Gds(isc_update_conflict)); - } - break; - - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: - if (!req_request->req_records_deleted) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << - Arg::Gds(isc_deadlock) << - Arg::Gds(isc_update_conflict)); - } - break; - } -} - -// Execute a dynamic SQL statement with tracing, restart and timeout handler -void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - if (!req_request) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_unprepared_stmt)); - } - - // If there is no data required, just start the request - - const dsql_msg* message = statement->getSendMsg(); - if (message) - mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); - - // we need to mapInOut() before tracing of execution start to let trace - // manager know statement parameters values - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - - // Setup and start timeout timer - const bool have_cursor = reqTypeWithCursor(statement->getType()) && !singleton; - - setupTimer(tdbb); - thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); - - if (needRestarts()) - executeReceiveWithRestarts(tdbb, traHandle, outMetadata, outMsg, singleton, true, false); - else { - doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); - } - - trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS); -} - -void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton, bool exec, bool fetch) -{ - req_request->req_flags &= ~req_update_conflict; - int numTries = 0; - const int MAX_RESTARTS = 10; - - while (true) - { - AutoSavePoint savePoint(tdbb, req_transaction); - - // Don't set req_restart_ready flag at last attempt to restart request. - // It allows to raise update conflict error (if any) as usual and - // handle error by PSQL handler. - const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; - AutoSetRestoreFlag restartReady(&req_request->req_flags, flag, true); - try - { - if (exec) - doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); - - if (fetch) - { - fb_assert(reqTypeWithCursor(statement->getType())); - - const dsql_msg* message = statement->getReceiveMsg(); - - UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer); - } - } - catch (const status_exception&) - { - if (!(req_transaction->tra_flags & TRA_ex_restart)) - { - req_request->req_flags &= ~req_update_conflict; - throw; - } - } - - if (!(req_request->req_flags & req_update_conflict)) - { - fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); - req_transaction->tra_flags &= ~TRA_ex_restart; - -#ifdef DEV_BUILD - if (numTries > 0) - { - string s; - s.printf("restarts = %d", numTries); - - ERRD_post_warning(Arg::Warning(isc_random) << Arg::Str(s)); - } -#endif - savePoint.release(); // everything is ok - break; - } - - fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); - - req_request->req_flags &= ~req_update_conflict; - req_transaction->tra_flags &= ~TRA_ex_restart; - fb_utils::init_status(tdbb->tdbb_status_vector); - - // Undo current savepoint but preserve already taken locks. - // Savepoint will be restarted at the next loop iteration. - savePoint.rollback(true); - - numTries++; - if (numTries >= MAX_RESTARTS) - { - gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" - "\tafter %d attempts of restart.\n" - "\tQuery:\n%s\n", numTries, req_request->getStatement()->sqlText->c_str() ); - } - - // When restart we must execute query - exec = true; - } -} - -void DsqlDdlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) -{ - Database* const dbb = tdbb->getDatabase(); - - internalScratch = scratch; - - scratch->flags |= DsqlCompilerScratch::FLAG_DDL; - - try - { - node = Node::doDsqlPass(scratch, node); - } - catch (status_exception& ex) - { - rethrowDdlException(ex, false); - } - - if (dbb->readOnly()) - ERRD_post(Arg::Gds(isc_read_only_database)); - - // In read-only replica, only replicator is allowed to execute DDL. - // As an exception, not replicated DDL statements are also allowed. - if (dbb->isReplica(REPLICA_READ_ONLY) && - !(tdbb->tdbb_flags & TDBB_replicator) && - node->mustBeReplicated()) - { - ERRD_post(Arg::Gds(isc_read_only_trans)); - } - - const auto dbDialect = - (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; - - if ((scratch->flags & DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT) && - dbDialect != scratch->clientDialect) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) << - Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(dbDialect)); - } - - if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); - else - scratch->getStatement()->setBlrVersion(4); -} - -// Execute a dynamic SQL statement. -void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - - fb_utils::init_status(tdbb->tdbb_status_vector); - - // run all statements under savepoint control - { // scope - AutoSavePoint savePoint(tdbb, req_transaction); - - try - { - AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); - - node->executeDdl(tdbb, internalScratch, req_transaction); - - const bool isInternalRequest = - (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); - - if (!isInternalRequest && node->mustBeReplicated()) - REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); - } - catch (status_exception& ex) - { - rethrowDdlException(ex, true); - } - - savePoint.release(); // everything is ok - } - - JRD_autocommit_ddl(tdbb, req_transaction); - - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - -bool DsqlDdlRequest::mustBeReplicated() const -{ - return node->mustBeReplicated(); -} - -// Rethrow an exception with isc_no_meta_update and prefix codes. -void DsqlDdlRequest::rethrowDdlException(status_exception& ex, bool metadataUpdate) -{ - Arg::StatusVector newVector; - - if (metadataUpdate) - newVector << Arg::Gds(isc_no_meta_update); - - node->putErrorPrefix(newVector); - - const ISC_STATUS* status = ex.value(); - - if (status[1] == isc_no_meta_update) - status += 2; - - newVector.append(Arg::StatusVector(status)); - - status_exception::raise(newVector); -} - - -void DsqlTransactionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* /*traceResult*/) -{ - node = Node::doDsqlPass(scratch, node); - - // Don't trace anything except savepoint statements - req_traced = (scratch->getStatement()->getType() == DsqlCompiledStatement::TYPE_SAVEPOINT); -} - -// Execute a dynamic SQL statement. -void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, - IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, - bool /*singleton*/) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - node->execute(tdbb, this, traHandle); - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - - -void DsqlSessionManagementRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* /*traceResult*/) -{ - node = Node::doDsqlPass(scratch, node); -} - -// Execute a dynamic SQL statement. -void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - node->execute(tdbb, this, traHandle); - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - - /** get_request_info @@ -1112,16 +374,16 @@ void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, @param buffer **/ -static ULONG get_request_info(thread_db* tdbb, dsql_req* request, ULONG buffer_length, UCHAR* buffer) +static ULONG get_request_info(thread_db* tdbb, DsqlRequest* request, ULONG buffer_length, UCHAR* buffer) { - if (!request->req_request) // DDL + if (!request->getJrdRequest()) // DDL return 0; // get the info for the request from the engine try { - return INF_request_info(request->req_request, sizeof(record_info), record_info, + return INF_request_info(request->getJrdRequest(), sizeof(record_info), record_info, buffer_length, buffer); } catch (Exception&) @@ -1163,348 +425,45 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment) } -/** - - mapInOut - - @brief Map data from external world into message or - from message to external world. - - - @param request - @param toExternal - @param message - @param meta - @param dsql_msg_buf - @param in_dsql_msg_buf - - **/ -void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* message, - IMessageMetadata* meta, UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf) -{ - USHORT count = parseMetadata(meta, message->msg_parameters); - - // Sanity check - - if (count) - { - if (toExternal) - { - if (dsql_msg_buf == NULL) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_no_output_sqlda)); - } - } - else - { - if (in_dsql_msg_buf == NULL) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_no_input_sqlda)); - } - } - } - - USHORT count2 = 0; - - for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i) - { - dsql_par* parameter = message->msg_parameters[i]; - - if (parameter->par_index) - { - // Make sure the message given to us is long enough - - dsc desc; - if (!req_user_descs.get(parameter, desc)) - desc.clear(); - - /*** - ULONG length = (IPTR) desc.dsc_address + desc.dsc_length; - if (length > msg_length) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_random) << "Message buffer too short"); - } - ***/ - if (!desc.dsc_dtype) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_datatype_err) << - Arg::Gds(isc_dsql_sqlvar_index) << Arg::Num(parameter->par_index-1)); - } - - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - SSHORT* flag = NULL; - dsql_par* const null_ind = parameter->par_null; - if (null_ind != NULL) - { - dsc userNullDesc; - if (!req_user_descs.get(null_ind, userNullDesc)) - userNullDesc.clear(); - - const ULONG null_offset = (IPTR) userNullDesc.dsc_address; - - /*** - length = null_offset + sizeof(SSHORT); - if (length > msg_length) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) - << Arg::Gds(isc_random) << "Message buffer too short"); - } - ***/ - - dsc nullDesc = null_ind->par_desc; - nullDesc.dsc_address = msgBuffer + (IPTR) nullDesc.dsc_address; - - if (toExternal) - { - flag = reinterpret_cast(dsql_msg_buf + null_offset); - *flag = *reinterpret_cast(nullDesc.dsc_address); - } - else - { - flag = reinterpret_cast(nullDesc.dsc_address); - *flag = *reinterpret_cast(in_dsql_msg_buf + null_offset); - } - } - - const bool notNull = (!flag || *flag >= 0); - - dsc parDesc = parameter->par_desc; - parDesc.dsc_address = msgBuffer + (IPTR) parDesc.dsc_address; - - if (toExternal) - { - desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; - - if (notNull) - MOVD_move(tdbb, &parDesc, &desc); - else - memset(desc.dsc_address, 0, desc.dsc_length); - } - else if (notNull && !parDesc.isNull()) - { - // Safe cast because desc is used as source only. - desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; - MOVD_move(tdbb, &desc, &parDesc); - } - else - memset(parDesc.dsc_address, 0, parDesc.dsc_length); - - ++count2; - } - } - - if (count != count2) - { - ERRD_post( - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && - (parameter = statement->getDbKey())) - { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - fb_assert(parentMsgBuffer); - - dsc parentDesc = dbkey->par_desc; - parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; - - dsc desc = parameter->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - MOVD_move(tdbb, &parentDesc, &desc); - - dsql_par* null_ind = parameter->par_null; - if (null_ind != NULL) - { - desc = null_ind->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - SSHORT* flag = (SSHORT*) desc.dsc_address; - *flag = 0; - } - } - - const dsql_par* rec_version; - if (!toExternal && (rec_version = statement->getParentRecVersion()) && - (parameter = statement->getRecVersion())) - { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : - NULL; - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - fb_assert(parentMsgBuffer); - - dsc parentDesc = rec_version->par_desc; - parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; - - dsc desc = parameter->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - MOVD_move(tdbb, &parentDesc, &desc); - - dsql_par* null_ind = parameter->par_null; - if (null_ind != NULL) - { - desc = null_ind->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - SSHORT* flag = (SSHORT*) desc.dsc_address; - *flag = 0; - } - } -} - - -/** - - parseMetadata - - @brief Parse the message of a request. - - - @param request - @param meta - @param parameters_list - - **/ -USHORT dsql_req::parseMetadata(IMessageMetadata* meta, const Array& parameters_list) -{ - HalfStaticArray parameters; - - for (FB_SIZE_T i = 0; i < parameters_list.getCount(); ++i) - { - dsql_par* param = parameters_list[i]; - - if (param->par_index) - { - if (param->par_index > parameters.getCount()) - parameters.grow(param->par_index); - fb_assert(!parameters[param->par_index - 1]); - parameters[param->par_index - 1] = param; - } - } - - // If there's no metadata, then the format of the current message buffer - // is identical to the format of the previous one. - - if (!meta) - return parameters.getCount(); - - FbLocalStatus st; - unsigned count = meta->getCount(&st); - checkD(&st); - - unsigned count2 = parameters.getCount(); - - if (count != count2) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_wrong_param_num) <getType(&st, index); - checkD(&st); - unsigned sqlLength = meta->getLength(&st, index); - checkD(&st); - - dsc desc; - desc.dsc_flags = 0; - - unsigned dataOffset, nullOffset, dtype, dlength; - offset = fb_utils::sqlTypeToDsc(offset, sqlType, sqlLength, - &dtype, &dlength, &dataOffset, &nullOffset); - desc.dsc_dtype = dtype; - desc.dsc_length = dlength; - - desc.dsc_scale = meta->getScale(&st, index); - checkD(&st); - desc.dsc_sub_type = meta->getSubType(&st, index); - checkD(&st); - unsigned textType = meta->getCharSet(&st, index); - checkD(&st); - desc.setTextType(textType); - desc.dsc_address = (UCHAR*)(IPTR) dataOffset; - - const dsql_par* const parameter = parameters[index]; - fb_assert(parameter); - - // ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been - // implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started - // failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using - // blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset - // here at the server as a way to make older (and not yet changed) client work - // correctly. - if (desc.isText() && desc.getTextType() == ttype_dynamic) - desc.setTextType(ttype_none); - - req_user_descs.put(parameter, desc); - - dsql_par* null = parameter->par_null; - if (null) - { - desc.clear(); - desc.dsc_dtype = dtype_short; - desc.dsc_scale = 0; - desc.dsc_length = sizeof(SSHORT); - desc.dsc_address = (UCHAR*)(IPTR) nullOffset; - - req_user_descs.put(null, desc); - } - } - - return count; -} - - -// raise error if one present -static void checkD(IStatus* st) -{ - if (st->getState() & IStatus::STATE_ERRORS) - { - ERRD_post(Arg::StatusVector(st)); - } -} - - -// Prepare a request for execution. Return SQL status code. +// Prepare a request for execution. // Note: caller is responsible for pool handling. -static dsql_req* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, +static DsqlRequest* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest) { - return prepareStatement(tdbb, database, transaction, textLength, text, clientDialect, isInternalRequest); + TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text); + + ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS; + try + { + auto statement = prepareStatement(tdbb, database, transaction, textLength, text, + clientDialect, isInternalRequest, &traceResult); + + auto request = statement->createRequest(tdbb, database); + + request->req_traced = true; + trace.setStatement(request); + trace.prepare(traceResult); + + return request; + } + catch (const Exception&) + { + trace.prepare(ITracePlugin::RESULT_FAILED); + throw; + } } -// Prepare a statement for execution. Return SQL status code. +// Prepare a statement for execution. // Note: caller is responsible for pool handling. -static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, - ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest) +static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, + ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest, ntrace_result_t* traceResult) { Database* const dbb = tdbb->getDatabase(); if (text && textLength == 0) textLength = static_cast(strlen(text)); - TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text); - if (clientDialect > SQL_DIALECT_CURRENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << @@ -1540,27 +499,19 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* // allocate the statement block, then prepare the statement + MemoryPool* scratchPool = nullptr; + DsqlCompilerScratch* scratch = nullptr; MemoryPool* statementPool = database->createPool(); - MemoryPool* scratchPool = NULL; - dsql_req* request = NULL; + RefPtr statement; Jrd::ContextPoolHolder statementContext(tdbb, statementPool); try { - DsqlCompiledStatement* statement = FB_NEW_POOL(*statementPool) DsqlCompiledStatement(*statementPool); - scratchPool = database->createPool(); if (!transaction) // Useful for session management statements transaction = database->dbb_attachment->getSysTransaction(); - DsqlCompilerScratch* scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database, - transaction, statement); - scratch->clientDialect = clientDialect; - - if (isInternalRequest) - scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST; - const auto dbDialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; @@ -1571,12 +522,19 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* { // scope to delete parser before the scratch pool is gone Jrd::ContextPoolHolder scratchContext(tdbb, scratchPool); - Parser parser(tdbb, *scratchPool, scratch, clientDialect, + scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database, transaction); + scratch->clientDialect = clientDialect; + + if (isInternalRequest) + scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST; + + Parser parser(tdbb, *scratchPool, statementPool, scratch, clientDialect, dbDialect, text, textLength, charSetId); // Parse the SQL statement. If it croaks, return - request = parser.parse(); - request->liveScratchPool = scratchPool; + statement = parser.parse(); + + scratch->setStatement(statement); if (parser.isStmtAmbiguous()) scratch->flags |= DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT; @@ -1584,10 +542,6 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transformedText = parser.getTransformedString(); } - request->req_dbb = scratch->getAttachment(); - request->req_transaction = scratch->getTransaction(); - request->statement = scratch->getStatement(); - // If the attachment charset is NONE, replace non-ASCII characters by question marks, so // that engine internals doesn't receive non-mappeable data to UTF8. If an attachment // charset is used, validate the string. @@ -1628,50 +582,26 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); + statement->dsqlPass(tdbb, scratch, traceResult); - request->req_traced = true; - trace.setStatement(request); + if (!statement->shouldPreserveScratch()) + database->deletePool(scratchPool); - ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS; - try - { - bool destroyScratchPool = false; - request->dsqlPass(tdbb, scratch, &destroyScratchPool, &traceResult); + scratchPool = nullptr; - if (destroyScratchPool) - { - database->deletePool(scratchPool); - request->liveScratchPool = scratchPool = NULL; - } - } - catch (const Exception&) - { - trace.prepare(traceResult); - throw; - } - - if (!isInternalRequest && request->mustBeReplicated()) + if (!isInternalRequest && statement->mustBeReplicated()) statement->setOrgText(text, textLength); - trace.prepare(traceResult); - - return request; + return statement; } catch (const Exception&) { - trace.prepare(ITracePlugin::RESULT_FAILED); - - if (request) - { - request->req_traced = false; - dsql_req::destroy(tdbb, request, true); - } - else - { + if (scratchPool) database->deletePool(scratchPool); + + if (!statement) database->deletePool(statementPool); - } throw; } @@ -1718,261 +648,14 @@ static UCHAR* put_item( UCHAR item, } -// Release a compiled statement. -static void release_statement(DsqlCompiledStatement* statement) -{ - if (statement->getParentRequest()) - { - dsql_req* parent = statement->getParentRequest(); - - FB_SIZE_T pos; - if (parent->cursors.find(statement, pos)) - parent->cursors.remove(pos); - - statement->setParentRequest(NULL); - } - - statement->setSqlText(NULL); - statement->setOrgText(NULL, 0); -} - - -// Class DsqlCompiledStatement - -void DsqlCompiledStatement::setOrgText(const char* ptr, ULONG len) -{ - if (!ptr || !len) - { - orgText = NULL; - return; - } - - const string text(ptr, len); - - if (text == *sqlText) - orgText = sqlText; - else - orgText = FB_NEW_POOL(getPool()) RefString(getPool(), text); -} - - -// Class dsql_req - -dsql_req::dsql_req(MemoryPool& pool) - : req_pool(pool), - statement(NULL), - liveScratchPool(NULL), - cursors(req_pool), - req_dbb(NULL), - req_transaction(NULL), - req_msg_buffers(req_pool), - req_cursor_name(req_pool), - req_cursor(NULL), - req_batch(NULL), - req_user_descs(req_pool), - req_traced(false), - req_timeout(0) -{ -} - -dsql_req::~dsql_req() -{ -} - -void dsql_req::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); -} - -void dsql_req::setDelayedFormat(thread_db* /*tdbb*/, IMessageMetadata* /*metadata*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); -} - -bool dsql_req::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); - - return false; // avoid warning -} - -unsigned int dsql_req::getTimeout() -{ - return req_timeout; -} - -unsigned int dsql_req::getActualTimeout() -{ - if (req_timer) - return req_timer->getValue(); - - return 0; -} - -void dsql_req::setTimeout(unsigned int timeOut) -{ - req_timeout = timeOut; -} - -TimeoutTimer* dsql_req::setupTimer(thread_db* tdbb) -{ - if (req_request) - { - if (req_request->hasInternalStatement()) - return req_timer; - - req_request->req_timeout = this->req_timeout; - - fb_assert(!req_request->req_caller); - if (req_request->req_caller) - { - if (req_timer) - req_timer->setup(0, 0); - return req_timer; - } - } - - Database* dbb = tdbb->getDatabase(); - Attachment* att = tdbb->getAttachment(); - - ISC_STATUS toutErr = isc_cfg_stmt_timeout; - unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000; - - if (req_timeout) - { - if (!timeOut || req_timeout < timeOut) - { - timeOut = req_timeout; - toutErr = isc_req_stmt_timeout; - } - } - else - { - const unsigned int attTout = att->getStatementTimeout(); - - if (!timeOut || attTout && attTout < timeOut) - { - timeOut = attTout; - toutErr = isc_att_stmt_timeout; - } - } - - if (!req_timer && timeOut) - { - req_timer = FB_NEW TimeoutTimer(); - req_request->req_timer = this->req_timer; - } - - if (req_timer) - { - req_timer->setup(timeOut, toutErr); - req_timer->start(); - } - - return req_timer; -} - -// Release a dynamic request. -void dsql_req::destroy(thread_db* tdbb, dsql_req* request, bool drop) -{ - SET_TDBB(tdbb); - - if (request->req_timer) - { - request->req_timer->stop(); - request->req_timer = NULL; - } - - // If request is parent, orphan the children and release a portion of their requests - - for (FB_SIZE_T i = 0; i < request->cursors.getCount(); ++i) - { - DsqlCompiledStatement* child = request->cursors[i]; - child->addFlags(DsqlCompiledStatement::FLAG_ORPHAN); - child->setParentRequest(NULL); - - // hvlad: lines below is commented out as - // - child is already unlinked from its parent request - // - we should not free child's sql text until its owner request is alive - // It seems to me we should destroy owner request here, not a child - // statement - as it always was before - - //Jrd::ContextPoolHolder context(tdbb, &child->getPool()); - //release_statement(child); - } - - // If the request had an open cursor, close it - - if (request->req_cursor) - DsqlCursor::close(tdbb, request->req_cursor); - - if (request->req_batch) - { - delete request->req_batch; - request->req_batch = nullptr; - } - - Jrd::Attachment* att = request->req_dbb->dbb_attachment; - const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); - if (need_trace_free) - { - TraceSQLStatementImpl stmt(request, NULL); - TraceManager::event_dsql_free(att, &stmt, DSQL_drop); - } - request->req_traced = false; - - if (request->req_cursor_name.hasData()) - { - request->req_dbb->dbb_cursors.remove(request->req_cursor_name); - request->req_cursor_name = ""; - } - - // If a request has been compiled, release it now - - if (request->req_request) - { - ThreadStatusGuard status_vector(tdbb); - - try - { - CMP_release(tdbb, request->req_request); - request->req_request = NULL; - } - catch (Exception&) - {} // no-op - } - - const DsqlCompiledStatement* statement = request->getStatement(); - release_statement(const_cast(statement)); - - // Release the entire request if explicitly asked for - - if (drop) - { - request->req_dbb->deletePool(request->liveScratchPool); - request->req_dbb->deletePool(&request->getPool()); - } -} - - // Return as UTF8 -string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const +string IntlString::toUtf8(jrd_tra* transaction) const { CHARSET_ID id = CS_dynamic; if (charset.hasData()) { - const dsql_intlsym* resolved = METD_get_charset(dsqlScratch->getTransaction(), - charset.length(), charset.c_str()); + const dsql_intlsym* resolved = METD_get_charset(transaction, charset.length(), charset.c_str()); if (!resolved) { @@ -2004,7 +687,7 @@ string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const **/ static void sql_info(thread_db* tdbb, - dsql_req* request, + DsqlRequest* request, ULONG item_length, const UCHAR* items, ULONG info_length, @@ -2037,7 +720,7 @@ static void sql_info(thread_db* tdbb, bool messageFound = false; USHORT first_index = 0; - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); while (items < end_items && *items != isc_info_end && info < end_info) { @@ -2065,14 +748,14 @@ static void sql_info(thread_db* tdbb, value = IStatement::FLAG_REPEAT_EXECUTE; switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_CREATE_DB: - case DsqlCompiledStatement::TYPE_DDL: + case DsqlStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_DDL: value &= ~IStatement::FLAG_REPEAT_EXECUTE; break; - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_RETURNING_CURSOR: value |= IStatement::FLAG_HAS_CURSOR; break; } @@ -2085,55 +768,55 @@ static void sql_info(thread_db* tdbb, case isc_info_sql_stmt_type: switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_RETURNING_CURSOR: number = isc_info_sql_stmt_select; break; - case DsqlCompiledStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_UPD: number = isc_info_sql_stmt_select_for_upd; break; - case DsqlCompiledStatement::TYPE_CREATE_DB: - case DsqlCompiledStatement::TYPE_DDL: + case DsqlStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_DDL: number = isc_info_sql_stmt_ddl; break; - case DsqlCompiledStatement::TYPE_COMMIT: - case DsqlCompiledStatement::TYPE_COMMIT_RETAIN: + case DsqlStatement::TYPE_COMMIT: + case DsqlStatement::TYPE_COMMIT_RETAIN: number = isc_info_sql_stmt_commit; break; - case DsqlCompiledStatement::TYPE_ROLLBACK: - case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN: + case DsqlStatement::TYPE_ROLLBACK: + case DsqlStatement::TYPE_ROLLBACK_RETAIN: number = isc_info_sql_stmt_rollback; break; - case DsqlCompiledStatement::TYPE_START_TRANS: + case DsqlStatement::TYPE_START_TRANS: number = isc_info_sql_stmt_start_trans; break; - case DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT: + case DsqlStatement::TYPE_SESSION_MANAGEMENT: number = isc_info_sql_stmt_ddl; // ????????????????? break; - case DsqlCompiledStatement::TYPE_INSERT: + case DsqlStatement::TYPE_INSERT: number = isc_info_sql_stmt_insert; break; - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_UPDATE_CURSOR: number = isc_info_sql_stmt_update; break; - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_DELETE_CURSOR: number = isc_info_sql_stmt_delete; break; - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_EXEC_PROCEDURE: number = isc_info_sql_stmt_exec_procedure; break; - case DsqlCompiledStatement::TYPE_SET_GENERATOR: + case DsqlStatement::TYPE_SET_GENERATOR: number = isc_info_sql_stmt_set_generator; break; - case DsqlCompiledStatement::TYPE_SAVEPOINT: + case DsqlStatement::TYPE_SAVEPOINT: number = isc_info_sql_stmt_savepoint; break; - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_EXEC_BLOCK: number = isc_info_sql_stmt_exec_procedure; break; - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT_BLOCK: number = isc_info_sql_stmt_select; break; default: @@ -2169,7 +852,7 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_batch_fetch: - if (statement->getFlags() & DsqlCompiledStatement::FLAG_NO_BATCH) + if (statement->getFlags() & DsqlStatement::FLAG_NO_BATCH) number = 0; else number = 1; @@ -2206,9 +889,7 @@ static void sql_info(thread_db* tdbb, { const bool detailed = (item == isc_info_sql_explain_plan); string plan = tdbb->getAttachment()->stringToUserCharSet(tdbb, - OPT_get_plan(tdbb, - (request->req_request ? request->req_request->getStatement() : nullptr), - detailed)); + OPT_get_plan(tdbb, request->getJrdStatement(), detailed)); if (plan.hasData()) { @@ -2258,9 +939,9 @@ static void sql_info(thread_db* tdbb, { HalfStaticArray path; - if (request->req_request && request->req_request->getStatement()) + if (request->getJrdStatement()) { - const auto& blr = request->req_request->getStatement()->blr; + const auto& blr = request->getJrdStatement()->blr; if (blr.hasData()) { diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index b3e1e7d6ff..6a3266aba7 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -35,6 +35,7 @@ #define DSQL_DSQL_H #include "../common/classes/array.h" +#include "../common/classes/fb_atomic.h" #include "../common/classes/GenericMap.h" #include "../jrd/MetaName.h" #include "../common/classes/stack.h" @@ -76,6 +77,7 @@ namespace Jrd class Attachment; class Database; class DsqlCompilerScratch; + class DsqlDmlStatement; class DdlNode; class RseNode; class StmtNode; @@ -134,7 +136,7 @@ public: Firebird::GenericMap > > dbb_charsets_by_id; // charsets sorted by charset_id Firebird::GenericMap > > dbb_cursors; // known cursors in database + Firebird::string, DsqlDmlRequest*>>> dbb_cursors; // known cursors in database MemoryPool& dbb_pool; // The current pool for the dbb Attachment* dbb_attachment; @@ -451,321 +453,6 @@ enum intlsym_flags_vals { INTLSYM_dropped = 1 // intlsym has been dropped }; - -// Compiled statement - shared by multiple requests. -class DsqlCompiledStatement : public Firebird::PermanentStorage -{ -public: - enum Type // statement type - { - TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, - TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, - TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, - TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT, - TYPE_RETURNING_CURSOR - }; - - // Statement flags. - static const unsigned FLAG_ORPHAN = 0x01; - static const unsigned FLAG_NO_BATCH = 0x02; - //static const unsigned FLAG_BLR_VERSION4 = 0x04; - //static const unsigned FLAG_BLR_VERSION5 = 0x08; - static const unsigned FLAG_SELECTABLE = 0x10; - -public: - explicit DsqlCompiledStatement(MemoryPool& p) - : PermanentStorage(p), - type(TYPE_SELECT), - flags(0), - blrVersion(5), - sendMsg(NULL), - receiveMsg(NULL), - eof(NULL), - dbKey(NULL), - recVersion(NULL), - parentRecVersion(NULL), - parentDbKey(NULL), - parentRequest(NULL) - { - } - -public: - Type getType() const { return type; } - void setType(Type value) { type = value; } - - ULONG getFlags() const { return flags; } - void setFlags(ULONG value) { flags = value; } - void addFlags(ULONG value) { flags |= value; } - - unsigned getBlrVersion() const { return blrVersion; } - void setBlrVersion(unsigned value) { blrVersion = value; } - - Firebird::RefStrPtr& getSqlText() { return sqlText; } - const Firebird::RefStrPtr& getSqlText() const { return sqlText; } - void setSqlText(Firebird::RefString* value) { sqlText = value; } - - void setOrgText(const char* ptr, ULONG len); - const Firebird::string& getOrgText() const { return *orgText; } - - dsql_msg* getSendMsg() { return sendMsg; } - const dsql_msg* getSendMsg() const { return sendMsg; } - void setSendMsg(dsql_msg* value) { sendMsg = value; } - - dsql_msg* getReceiveMsg() { return receiveMsg; } - const dsql_msg* getReceiveMsg() const { return receiveMsg; } - void setReceiveMsg(dsql_msg* value) { receiveMsg = value; } - - dsql_par* getEof() { return eof; } - const dsql_par* getEof() const { return eof; } - void setEof(dsql_par* value) { eof = value; } - - dsql_par* getDbKey() { return dbKey; } - const dsql_par* getDbKey() const { return dbKey; } - void setDbKey(dsql_par* value) { dbKey = value; } - - dsql_par* getRecVersion() { return recVersion; } - const dsql_par* getRecVersion() const { return recVersion; } - void setRecVersion(dsql_par* value) { recVersion = value; } - - dsql_par* getParentRecVersion() { return parentRecVersion; } - const dsql_par* getParentRecVersion() const { return parentRecVersion; } - void setParentRecVersion(dsql_par* value) { parentRecVersion = value; } - - dsql_par* getParentDbKey() { return parentDbKey; } - const dsql_par* getParentDbKey() const { return parentDbKey; } - void setParentDbKey(dsql_par* value) { parentDbKey = value; } - - dsql_req* getParentRequest() const { return parentRequest; } - void setParentRequest(dsql_req* value) { parentRequest = value; } - -private: - Type type; // Type of statement - ULONG flags; // generic flag - unsigned blrVersion; - Firebird::RefStrPtr sqlText; - Firebird::RefStrPtr orgText; - dsql_msg* sendMsg; // Message to be sent to start request - dsql_msg* receiveMsg; // Per record message to be received - dsql_par* eof; // End of file parameter - dsql_par* dbKey; // Database key for current of - dsql_par* recVersion; // Record Version for current of - dsql_par* parentRecVersion; // parent record version - dsql_par* parentDbKey; // Parent database key for current of - dsql_req* parentRequest; // Source request, if cursor update -}; - -class dsql_req : public pool_alloc -{ -public: - explicit dsql_req(MemoryPool& pool); - -public: - MemoryPool& getPool() - { - return req_pool; - } - - jrd_tra* getTransaction() - { - return req_transaction; - } - - const DsqlCompiledStatement* getStatement() const - { - return statement; - } - - virtual bool mustBeReplicated() const - { - return false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) = 0; - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) = 0; - - virtual void setCursor(thread_db* tdbb, const TEXT* name); - - virtual bool fetch(thread_db* tdbb, UCHAR* buffer); - - virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); - - // Get session-level timeout, milliseconds - unsigned int getTimeout(); - - // Set session-level timeout, milliseconds - void setTimeout(unsigned int timeOut); - - // Get actual timeout, milliseconds - unsigned int getActualTimeout(); - - // Evaluate actual timeout value, consider config- and session-level timeout values, - // setup and start timer - TimeoutTimer* setupTimer(thread_db* tdbb); - - USHORT parseMetadata(Firebird::IMessageMetadata* meta, const Firebird::Array& parameters_list); - void mapInOut(Jrd::thread_db* tdbb, bool toExternal, const dsql_msg* message, Firebird::IMessageMetadata* meta, - UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf = NULL); - - static void destroy(thread_db* tdbb, dsql_req* request, bool drop); - -private: - MemoryPool& req_pool; - -public: - const DsqlCompiledStatement* statement; - MemoryPool* liveScratchPool; - Firebird::Array cursors; // Cursor update statements - - dsql_dbb* req_dbb; // DSQL attachment - jrd_tra* req_transaction; // JRD transaction - jrd_req* req_request; // JRD request - - Firebird::Array req_msg_buffers; - Firebird::string req_cursor_name; // Cursor name, if any - DsqlCursor* req_cursor; // Open cursor, if any - DsqlBatch* req_batch; // Active batch, if any - Firebird::GenericMap > req_user_descs; // SQLDA data type - - Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time - SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time - SINT64 req_fetch_rowcount; // Total number of rows returned by this request - bool req_traced; // request is traced via TraceAPI - -protected: - unsigned int req_timeout; // query timeout in milliseconds, set by the user - Firebird::RefPtr req_timer; // timeout timer - - // Request should never be destroyed using delete. - // It dies together with it's pool in release_request(). - ~dsql_req(); - - // To avoid posix warning about missing public destructor declare - // MemoryPool as friend class. In fact IT releases request memory! - friend class Firebird::MemoryPool; -}; - -class DsqlDmlRequest : public dsql_req -{ -public: - explicit DsqlDmlRequest(MemoryPool& pool, StmtNode* aNode) - : dsql_req(pool), - node(aNode), - needDelayedFormat(false), - firstRowFetched(false) - { - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - virtual void setCursor(thread_db* tdbb, const TEXT* name); - - virtual bool fetch(thread_db* tdbb, UCHAR* buffer); - - virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); - -private: - // True, if request could be restarted - bool needRestarts(); - - void doExecute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - // [Re]start part of "request restarts" algorithm - void executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton, bool exec, bool fetch); - - NestConst node; - Firebird::RefPtr delayedFormat; - bool needDelayedFormat; - bool firstRowFetched; -}; - -class DsqlDdlRequest : public dsql_req -{ -public: - explicit DsqlDdlRequest(MemoryPool& pool, DdlNode* aNode) - : dsql_req(pool), - node(aNode), - internalScratch(NULL) - { - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - virtual bool mustBeReplicated() const; - -private: - // Rethrow an exception with isc_no_meta_update and prefix codes. - void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate); - -private: - NestConst node; - DsqlCompilerScratch* internalScratch; -}; - -class DsqlTransactionRequest : public dsql_req -{ -public: - explicit DsqlTransactionRequest(MemoryPool& pool, TransactionNode* aNode) - : dsql_req(pool), - node(aNode) - { - req_traced = false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - -private: - NestConst node; -}; - -class DsqlSessionManagementRequest : public dsql_req -{ -public: - explicit DsqlSessionManagementRequest(MemoryPool& pool, SessionManagementNode* aNode) - : dsql_req(pool), - node(aNode) - { - req_traced = false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - -private: - NestConst node; -}; - //! Implicit (NATURAL and USING) joins class ImplicitJoin : public pool_alloc { @@ -985,7 +672,7 @@ public: s(p) { } - Firebird::string toUtf8(DsqlCompilerScratch*) const; + Firebird::string toUtf8(jrd_tra* transaction) const; const MetaName& getCharSet() const { diff --git a/src/dsql/dsql_proto.h b/src/dsql/dsql_proto.h index dfd44e6d4d..89a6e2b4c3 100644 --- a/src/dsql/dsql_proto.h +++ b/src/dsql/dsql_proto.h @@ -31,21 +31,19 @@ namespace Jrd { class Attachment; class jrd_tra; - class dsql_req; + class DsqlDmlRequest; + class DsqlRequest; } -void DSQL_execute(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*, +void DSQL_execute(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::DsqlRequest*, Firebird::IMessageMetadata*, const UCHAR*, Firebird::IMessageMetadata*, UCHAR*); void DSQL_execute_immediate(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra**, ULONG, const TEXT*, USHORT, Firebird::IMessageMetadata*, const UCHAR*, Firebird::IMessageMetadata*, UCHAR*, bool); -void DSQL_free_statement(Jrd::thread_db*, Jrd::dsql_req*, USHORT); -Jrd::DsqlCursor* DSQL_open(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*, - Firebird::IMessageMetadata*, const UCHAR*, - Firebird::IMessageMetadata*, ULONG); -Jrd::dsql_req* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*, +void DSQL_free_statement(Jrd::thread_db*, Jrd::DsqlRequest*, USHORT); +Jrd::DsqlRequest* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*, USHORT, unsigned, Firebird::Array*, Firebird::Array*, bool); -void DSQL_sql_info(Jrd::thread_db*, Jrd::dsql_req*, +void DSQL_sql_info(Jrd::thread_db*, Jrd::DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*); #endif // DSQL_DSQL_PROTO_H diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index d8bbb41689..89aa4c554b 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -220,22 +220,22 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) message->msg_length = offset; - dsqlScratch->ports.add(message); + dsqlScratch->getStatement()->getPorts().add(message); } // Generate complete blr for a dsqlScratch. -void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) +void GEN_statement(DsqlCompilerScratch* scratch, DmlNode* node) { - DsqlCompiledStatement* statement = scratch->getStatement(); + DsqlStatement* statement = scratch->getStatement(); if (statement->getBlrVersion() == 4) scratch->appendUChar(blr_version4); else scratch->appendUChar(blr_version5); - const bool block = statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK || - statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK; + const bool block = statement->getType() == DsqlStatement::TYPE_EXEC_BLOCK || + statement->getType() == DsqlStatement::TYPE_SELECT_BLOCK; // To parse sub-routines messages, they must not have that begin...end pair. // And since it appears to be unnecessary for execute block too, do not generate them. @@ -247,14 +247,14 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_SELECT_BLOCK: node->genBlr(scratch); break; - ///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + ///case DsqlStatement::TYPE_RETURNING_CURSOR: default: { dsql_msg* message = statement->getSendMsg(); diff --git a/src/dsql/gen_proto.h b/src/dsql/gen_proto.h index 55cf5fb4fe..6301fda05d 100644 --- a/src/dsql/gen_proto.h +++ b/src/dsql/gen_proto.h @@ -35,7 +35,7 @@ void GEN_expr(Jrd::DsqlCompilerScratch*, Jrd::ExprNode*); void GEN_hidden_variables(Jrd::DsqlCompilerScratch* dsqlScratch); 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_statement(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*); void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*); void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*); void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*); diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index 5eddf02f5c..d68df999fb 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -34,7 +34,7 @@ namespace Jrd { class TypeClause; class dsql_msg; class dsql_par; - class dsql_req; + class DsqlRequest; class DsqlCompilerScratch; class IntlString; class ExprNode; diff --git a/src/dsql/metd_proto.h b/src/dsql/metd_proto.h index b622c18400..fe9faa2c36 100644 --- a/src/dsql/metd_proto.h +++ b/src/dsql/metd_proto.h @@ -34,7 +34,7 @@ namespace Jrd { typedef Firebird::GenericMap MetaNamePairMap; - class dsql_req; + class DsqlRequest; class DsqlCompilerScratch; class jrd_tra; class dsql_intlsym; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 852889bfd5..e9840aa749 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -828,7 +828,7 @@ using namespace Firebird; Jrd::SetTransactionNode::RestrictionOption* setTransactionRestrictionClause; Jrd::DeclareSubProcNode* declareSubProcNode; Jrd::DeclareSubFuncNode* declareSubFuncNode; - Jrd::dsql_req* dsqlReq; + Jrd::DsqlStatement* dsqlStatement; Jrd::CreateAlterUserNode* createAlterUserNode; Jrd::MappingNode* mappingNode; Jrd::MappingNode::OP mappingOp; @@ -848,16 +848,23 @@ using namespace Firebird; // list of possible statements top - : statement { DSQL_parse = $1; } - | statement ';' { DSQL_parse = $1; } + : statement { parsedStatement = $1; } + | statement ';' { parsedStatement = $1; } ; -%type statement +%type statement statement - : dml_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlDmlRequest(getStatementPool(), $1); } - | ddl_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlDdlRequest(getStatementPool(), $1); } - | tra_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlTransactionRequest(getStatementPool(), $1); } - | mng_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlSessionManagementRequest(getStatementPool(), $1); } + : dml_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlDmlStatement(*statementPool, scratch->getAttachment(), $1); } + | ddl_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlDdlStatement(*statementPool, scratch->getAttachment(), $1); } + | tra_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlTransactionStatement(*statementPool, scratch->getAttachment(), $1); } + | mng_statement + { + $$ = FB_NEW_POOL(*statementPool) DsqlSessionManagementStatement( + *statementPool, scratch->getAttachment(), $1); + } ; %type dml_statement @@ -7743,7 +7750,7 @@ sql_string %type utf_string utf_string : sql_string - { $$ = newString($1->toUtf8(scratch)); } + { $$ = newString($1->toUtf8(scratch->getTransaction())); } ; %type signed_short_integer diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index edc19703f5..4bfd0c0308 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -35,7 +35,7 @@ class blb; class jrd_tra; class DsqlCursor; class DsqlBatch; -class dsql_req; +class DsqlRequest; class JrdStatement; class StableAttachmentPart; class Attachment; @@ -305,20 +305,20 @@ public: unsigned parLength, const unsigned char* par) override; public: - JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array& meta); + JStatement(DsqlRequest* handle, StableAttachmentPart* sa, Firebird::Array& meta); StableAttachmentPart* getAttachment() { return sAtt; } - dsql_req* getHandle() throw() + DsqlRequest* getHandle() throw() { return statement; } private: - dsql_req* statement; + DsqlRequest* statement; Firebird::RefPtr sAtt; Firebird::StatementMetadata metadata; diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index 1ce4864d13..6e9ece57ad 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -410,6 +410,9 @@ jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) // Create the request. jrd_req* const request = FB_NEW_POOL(*pool) jrd_req(attachment, this, parentStats); + if (level == 0) + pool->setStatsGroup(request->req_memory_stats); + requests[level] = request; return request; diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index e58a309d3d..9f39d76ff0 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -325,7 +325,7 @@ void PreparedStatement::init(thread_db* tdbb, Attachment* attachment, jrd_tra* t request = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, 0, NULL, NULL, isInternalRequest); - const DsqlCompiledStatement* statement = request->getStatement(); + const DsqlStatement* statement = request->getStatement(); if (statement->getSendMsg()) parseDsqlMessage(statement->getSendMsg(), inValues, inMetadata, inMessage); @@ -348,7 +348,7 @@ void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& valu { fb_assert(param > 0); - jrd_req* jrdRequest = getRequest()->req_request; + jrd_req* jrdRequest = getRequest()->getJrdRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( @@ -382,7 +382,7 @@ void PreparedStatement::open(thread_db* tdbb, jrd_tra* transaction) if (builder) builder->moveToStatement(tdbb, this); - DSQL_open(tdbb, &transaction, request, inMetadata, inMessage.begin(), outMetadata, 0); + request->openCursor(tdbb, &transaction, inMetadata, inMessage.begin(), outMetadata, 0); } @@ -400,7 +400,7 @@ ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction unsigned PreparedStatement::executeUpdate(thread_db* tdbb, jrd_tra* transaction) { execute(tdbb, transaction); - return getRequest()->req_request->req_records_updated; + return getRequest()->getJrdRequest()->req_records_updated; } diff --git a/src/jrd/PreparedStatement.h b/src/jrd/PreparedStatement.h index 9c37ae611f..4206b41370 100644 --- a/src/jrd/PreparedStatement.h +++ b/src/jrd/PreparedStatement.h @@ -40,7 +40,7 @@ namespace Jrd { class thread_db; class jrd_tra; class Attachment; -class dsql_req; +class DsqlRequest; class dsql_msg; class ResultSet; @@ -359,7 +359,7 @@ public: int getResultCount() const; - dsql_req* getRequest() + DsqlRequest* getRequest() { return request; } @@ -369,7 +369,7 @@ public: private: const Builder* builder; - dsql_req* request; + DsqlRequest* request; Firebird::Array inValues, outValues; Firebird::RefPtr inMetadata, outMetadata; Firebird::UCharBuffer inMessage, outMessage; diff --git a/src/jrd/ResultSet.cpp b/src/jrd/ResultSet.cpp index 0d22863ce6..bd112fa6bb 100644 --- a/src/jrd/ResultSet.cpp +++ b/src/jrd/ResultSet.cpp @@ -54,14 +54,14 @@ ResultSet::~ResultSet() stmt->resultSet = NULL; - if (stmt->request->getStatement()->getType() != DsqlCompiledStatement::TYPE_EXEC_PROCEDURE) + if (stmt->request->getStatement()->getType() != DsqlStatement::TYPE_EXEC_PROCEDURE) DSQL_free_statement(tdbb, stmt->request, DSQL_close); } bool ResultSet::fetch(thread_db* tdbb) { - if (stmt->request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_PROCEDURE && + if (stmt->request->getStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE && firstFetchDone) { return false; @@ -103,7 +103,7 @@ Firebird::string ResultSet::getString(thread_db* tdbb, unsigned param) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->req_request; + jrd_req* jrdRequest = stmt->getRequest()->getJrdRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( @@ -131,7 +131,7 @@ void ResultSet::moveDesc(thread_db* tdbb, unsigned param, dsc& desc) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->req_request; + jrd_req* jrdRequest = stmt->getRequest()->getJrdRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index c891e8a54f..ba6c8c4ba3 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -143,38 +143,26 @@ BoolExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, BoolExpr return clone; } - -jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool internal_flag, - ULONG dbginfo_length, const UCHAR* dbginfo) +// Compile a statement. +JrdStatement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, + ULONG dbginfoLength, const UCHAR* dbginfo) { -/************************************** - * - * C M P _ c o m p i l e 2 - * - ************************************** - * - * Functional description - * Compile a BLR request. - * - **************************************/ - jrd_req* request = NULL; + JrdStatement* statement = nullptr; SET_TDBB(tdbb); - Jrd::Attachment* const att = tdbb->getAttachment(); + const auto att = tdbb->getAttachment(); // 26.09.2002 Nickolay Samofatov: default memory pool will become statement pool // and will be freed by CMP_release - MemoryPool* const new_pool = att->createPool(); + const auto newPool = att->createPool(); try { - Jrd::ContextPoolHolder context(tdbb, new_pool); + Jrd::ContextPoolHolder context(tdbb, newPool); - CompilerScratch* csb = - PAR_parse(tdbb, blr, blr_length, internal_flag, dbginfo_length, dbginfo); + const auto csb = PAR_parse(tdbb, blr, blrLength, internalFlag, dbginfoLength, dbginfo); - request = JrdStatement::makeRequest(tdbb, csb, internal_flag); - new_pool->setStatsGroup(request->req_memory_stats); + statement = JrdStatement::makeStatement(tdbb, csb, internalFlag); #ifdef CMP_DEBUG if (csb->csb_dump.hasData()) @@ -196,20 +184,40 @@ jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool } #endif - request->getStatement()->verifyAccess(tdbb); + statement->verifyAccess(tdbb); delete csb; } catch (const Firebird::Exception& ex) { ex.stuffException(tdbb->tdbb_status_vector); - if (request) - CMP_release(tdbb, request); + if (statement) + statement->release(tdbb); else - att->deletePool(new_pool); + att->deletePool(newPool); ERR_punt(); } + return statement; +} + +jrd_req* CMP_compile_request(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag) +{ +/************************************** + * + * C M P _ c o m p i l e _ r e q u e s t + * + ************************************** + * + * Functional description + * Compile a BLR request. + * + **************************************/ + SET_TDBB(tdbb); + + auto statement = CMP_compile(tdbb, blr, blrLength, internalFlag, 0, nullptr); + auto request = statement->getRequest(tdbb, 0); + return request; } diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index c95f41c881..a5f3a9b630 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -37,8 +37,9 @@ StreamType* CMP_alloc_map(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType str Jrd::ValueExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); Jrd::BoolExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::BoolExprNode*); Jrd::ValueExprNode* CMP_clone_node(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); -Jrd::jrd_req* CMP_compile2(Jrd::thread_db*, const UCHAR* blr, ULONG blr_length, bool internal_flag, - ULONG = 0, const UCHAR* = NULL); +Jrd::JrdStatement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, + ULONG dbginfoLength, const UCHAR* dbginfo); +Jrd::jrd_req* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element); const Jrd::Format* CMP_format(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType); Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); diff --git a/src/jrd/exe_proto.h b/src/jrd/exe_proto.h index ea3dedcc2e..ee60d69e6f 100644 --- a/src/jrd/exe_proto.h +++ b/src/jrd/exe_proto.h @@ -93,7 +93,7 @@ namespace Jrd if (request) return; - request = CMP_compile2(tdbb, blr, blrLength, true); + request = CMP_compile_request(tdbb, blr, blrLength, true); cacheRequest(); } @@ -166,7 +166,7 @@ namespace Jrd if (request) return; - request = CMP_compile2(tdbb, blr, blrLength, true); + request = CMP_compile_request(tdbb, blr, blrLength, true); } jrd_req* operator ->() diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 0ab41a1969..590e2fa931 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -392,7 +392,7 @@ void InternalTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bo else m_transaction->rollback(status); - if (status->getState() & IStatus::STATE_ERRORS) + if (status->getState() & IStatus::STATE_ERRORS) err = status->getErrors()[1]; if (err == isc_cancelled) @@ -504,7 +504,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (status->getState() & IStatus::STATE_ERRORS) raise(&status, tdbb, "JAttachment::prepare", &sql); - const DsqlCompiledStatement* statement = m_request->getHandle()->getStatement(); + const DsqlStatement* statement = m_request->getHandle()->getStatement(); if (statement->getSendMsg()) { @@ -542,33 +542,33 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_BLOCK: m_stmt_selectable = true; break; - case DsqlCompiledStatement::TYPE_START_TRANS: - case DsqlCompiledStatement::TYPE_COMMIT: - case DsqlCompiledStatement::TYPE_ROLLBACK: - case DsqlCompiledStatement::TYPE_COMMIT_RETAIN: - case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN: - case DsqlCompiledStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_START_TRANS: + case DsqlStatement::TYPE_COMMIT: + case DsqlStatement::TYPE_ROLLBACK: + case DsqlStatement::TYPE_COMMIT_RETAIN: + case DsqlStatement::TYPE_ROLLBACK_RETAIN: + case DsqlStatement::TYPE_CREATE_DB: Arg::Gds(isc_eds_expl_tran_ctrl).copyTo(&status); raise(&status, tdbb, "JAttachment::prepare", &sql); break; - case DsqlCompiledStatement::TYPE_INSERT: - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: - case DsqlCompiledStatement::TYPE_DDL: - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: - case DsqlCompiledStatement::TYPE_SET_GENERATOR: - case DsqlCompiledStatement::TYPE_SAVEPOINT: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_INSERT: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_UPDATE_CURSOR: + case DsqlStatement::TYPE_DELETE_CURSOR: + case DsqlStatement::TYPE_DDL: + case DsqlStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_SET_GENERATOR: + case DsqlStatement::TYPE_SAVEPOINT: + case DsqlStatement::TYPE_EXEC_BLOCK: break; } } diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index a65090931b..883b2b14d2 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -345,7 +345,7 @@ JEvents::JEvents(int aId, StableAttachmentPart* sa, Firebird::IEventCallback* aC { } -JStatement::JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array& meta) +JStatement::JStatement(DsqlRequest* handle, StableAttachmentPart* sa, Firebird::Array& meta) : statement(handle), sAtt(sa), metadata(getPool(), this, sAtt) { metadata.parse(meta.getCount(), meta.begin()); @@ -657,7 +657,7 @@ namespace validateHandle(tdbb, statement->requests[0]->req_attachment); } - inline void validateHandle(thread_db* tdbb, dsql_req* const statement) + inline void validateHandle(thread_db* tdbb, DsqlRequest* const statement) { if (!statement) status_exception::raise(Arg::Gds(isc_bad_req_handle)); @@ -2693,11 +2693,14 @@ JRequest* JAttachment::compileRequest(CheckStatusWrapper* user_status, TraceBlrCompile trace(tdbb, blr_length, blr); try { - jrd_req* request = NULL; - JRD_compile(tdbb, getHandle(), &request, blr_length, blr, RefStrPtr(), 0, NULL, false); - stmt = request->getStatement(); + stmt = CMP_compile(tdbb, blr, blr_length, false, 0, nullptr); - trace.finish(request, ITracePlugin::RESULT_SUCCESS); + const auto attachment = tdbb->getAttachment(); + const auto rootRequest = stmt->getRequest(tdbb, 0); + rootRequest->setAttachment(attachment); + attachment->att_requests.add(rootRequest); + + trace.finish(stmt, ITracePlugin::RESULT_SUCCESS); } catch (const Exception& ex) { @@ -4717,7 +4720,6 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* for (FB_SIZE_T i = 0; i < csb->csb_rpt.getCount(); i++) { - const MessageNode* node = csb->csb_rpt[i].csb_message; if (node) { @@ -5066,7 +5068,7 @@ JResultSet* JStatement::openCursor(CheckStatusWrapper* user_status, ITransaction } } - DsqlCursor* const cursor = DSQL_open(tdbb, &tra, getHandle(), + const auto cursor = getHandle()->openCursor(tdbb, &tra, inMetadata, static_cast(inBuffer), outMetadata, flags); rs = FB_NEW JResultSet(cursor, this); @@ -5542,7 +5544,7 @@ JStatement* JAttachment::prepare(CheckStatusWrapper* user_status, ITransaction* validateHandle(tdbb, tra); check_database(tdbb); - dsql_req* statement = NULL; + DsqlRequest* statement = NULL; try { @@ -5814,7 +5816,7 @@ void JResultSet::setDelayedOutputFormat(CheckStatusWrapper* user_status, Firebir try { - dsql_req* req = statement->getHandle(); + DsqlRequest* req = statement->getHandle(); fb_assert(req); req->setDelayedFormat(tdbb, outMetadata); } @@ -5874,7 +5876,7 @@ unsigned int JStatement::getTimeout(CheckStatusWrapper* user_status) try { - Jrd::dsql_req* req = getHandle(); + Jrd::DsqlRequest* req = getHandle(); return req->getTimeout(); } catch (const Exception& ex) @@ -5904,7 +5906,7 @@ void JStatement::setTimeout(CheckStatusWrapper* user_status, unsigned int timeOu try { - Jrd::dsql_req* req = getHandle(); + Jrd::DsqlRequest* req = getHandle(); req->setTimeout(timeOut); } catch (const Exception& ex) @@ -5946,11 +5948,11 @@ JBatch* JStatement::createBatch(Firebird::CheckStatusWrapper* status, Firebird:: } } - DsqlBatch* const b = DsqlBatch::open(tdbb, getHandle(), inMetadata, parLength, par); + const auto dsqlBatch = getHandle()->openBatch(tdbb, inMetadata, parLength, par); - batch = FB_NEW JBatch(b, this, inMetadata); + batch = FB_NEW JBatch(dsqlBatch, this, inMetadata); batch->addRef(); - b->setInterfacePtr(batch); + dsqlBatch->setInterfacePtr(batch); tdbb->getAttachment()->registerBatch(batch); } catch (const Exception& ex) @@ -9367,47 +9369,6 @@ void JRD_unwind_request(thread_db* tdbb, jrd_req* request) } -void JRD_compile(thread_db* tdbb, - Jrd::Attachment* attachment, - jrd_req** req_handle, - ULONG blr_length, - const UCHAR* blr, - RefStrPtr ref_str, - ULONG dbginfo_length, - const UCHAR* dbginfo, - bool isInternalRequest) -{ -/************************************** - * - * J R D _ c o m p i l e - * - ************************************** - * - * Functional description - * Compile a request passing the SQL text and debug information. - * - **************************************/ - if (*req_handle) - status_exception::raise(Arg::Gds(isc_bad_req_handle)); - - jrd_req* request = CMP_compile2(tdbb, blr, blr_length, isInternalRequest, dbginfo_length, dbginfo); - request->req_attachment = attachment; - attachment->att_requests.add(request); - - JrdStatement* statement = request->getStatement(); - - if (ref_str) - statement->sqlText = ref_str; - - fb_assert(statement->blr.isEmpty()); - - if (attachment->getDebugOptions().getDsqlKeepBlr()) - statement->blr.insert(0, blr, blr_length); - - *req_handle = request; -} - - namespace { class DatabaseDirList : public DirectoryList diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index b1569e0fba..7f39152a17 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -40,7 +40,7 @@ namespace Jrd { class Service; class thread_db; struct teb; - class dsql_req; + class DsqlRequest; class MetaName; } @@ -71,9 +71,6 @@ void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tr void JRD_start_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction, Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb); void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::jrd_req* request); -void JRD_compile(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, Jrd::jrd_req** req_handle, - ULONG blr_length, const UCHAR* blr, Firebird::RefStrPtr, - ULONG dbginfo_length, const UCHAR* dbginfo, bool isInternalRequest); bool JRD_verify_database_access(const Firebird::PathName&); void JRD_shutdown_attachment(Jrd::Attachment* attachment); void JRD_shutdown_attachments(Jrd::Database* dbb); diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index 546a12464e..09e5b5ecaa 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -455,12 +455,10 @@ string OPT_get_plan(thread_db* tdbb, const JrdStatement* statement, bool detaile if (statement) { - const Array& fors = statement->fors; - - for (FB_SIZE_T i = 0; i < fors.getCount(); i++) + for (const auto& recordSource : statement->fors) { plan += detailed ? "\nSelect Expression" : "\nPLAN "; - fors[i]->print(tdbb, plan, detailed, 0); + recordSource->print(tdbb, plan, detailed, 0); } } diff --git a/src/jrd/opt_proto.h b/src/jrd/opt_proto.h index 233d8ee3ee..3ff42d7dac 100644 --- a/src/jrd/opt_proto.h +++ b/src/jrd/opt_proto.h @@ -30,7 +30,7 @@ #include "../jrd/lls.h" namespace Jrd { - class jrd_req; + class JrdStatement; class jrd_rel; class RecordSource; struct index_desc; diff --git a/src/jrd/req.h b/src/jrd/req.h index aa0736a606..08e5375543 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -382,8 +382,8 @@ public: ULONG req_flags; // misc request flags Savepoint* req_savepoints; // Looper savepoint list Savepoint* req_proc_sav_point; // procedure savepoint list - unsigned int req_timeout; // query timeout in milliseconds, set by the dsql_req::setupTimer - Firebird::RefPtr req_timer; // timeout timer, shared with dsql_req + unsigned int req_timeout; // query timeout in milliseconds, set by the DsqlRequest::setupTimer + Firebird::RefPtr req_timer; // timeout timer, shared with DsqlRequest Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time diff --git a/src/jrd/trace/TraceDSQLHelpers.h b/src/jrd/trace/TraceDSQLHelpers.h index cb4a176975..7d7570dac2 100644 --- a/src/jrd/trace/TraceDSQLHelpers.h +++ b/src/jrd/trace/TraceDSQLHelpers.h @@ -28,6 +28,7 @@ #ifndef JRD_TRACE_DSQL_HELPERS_H #define JRD_TRACE_DSQL_HELPERS_H +#include "../../jrd/trace/TraceManager.h" #include "../../jrd/trace/TraceObjects.h" namespace Jrd { @@ -65,7 +66,7 @@ public: prepare(ITracePlugin::RESULT_FAILED); } - void setStatement(dsql_req* request) + void setStatement(DsqlRequest* request) { m_request = request; } @@ -100,7 +101,7 @@ private: bool m_need_trace; Attachment* m_attachment; jrd_tra* const m_transaction; - dsql_req* m_request; + DsqlRequest* m_request; SINT64 m_start_clock; FB_SIZE_T m_string_len; const TEXT* m_string; @@ -110,7 +111,7 @@ private: class TraceDSQLExecute { public: - TraceDSQLExecute(Attachment* attachment, dsql_req* request) : + TraceDSQLExecute(Attachment* attachment, DsqlRequest* request) : m_attachment(attachment), m_request(request) { @@ -131,11 +132,10 @@ public: fb_assert(!m_request->req_fetch_baseline); m_request->req_fetch_baseline = NULL; - jrd_req* jrd_request = m_request->req_request; - if (jrd_request) + if (auto jrdRequest = m_request->getJrdRequest()) { MemoryPool* pool = MemoryPool::getContextPool(); - m_request->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, jrd_request->req_stats); + m_request->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, jrdRequest->req_stats); } } @@ -152,7 +152,7 @@ public: } TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - m_request->req_request ? &m_request->req_request->req_stats : NULL, + m_request->getJrdRequest() ? &m_request->getJrdRequest()->req_stats : NULL, fb_utils::query_performance_counter() - m_start_clock, m_request->req_fetch_rowcount); @@ -170,19 +170,19 @@ public: private: bool m_need_trace; Attachment* const m_attachment; - dsql_req* const m_request; + DsqlRequest* const m_request; SINT64 m_start_clock; }; class TraceDSQLFetch { public: - TraceDSQLFetch(Attachment* attachment, dsql_req* request) : + TraceDSQLFetch(Attachment* attachment, DsqlRequest* request) : m_attachment(attachment), m_request(request) { m_need_trace = m_request->req_traced && TraceManager::need_dsql_execute(m_attachment) && - m_request->req_request && (m_request->req_request->req_flags & req_active); + m_request->getJrdRequest() && (m_request->getJrdRequest()->req_flags & req_active); if (!m_need_trace) { @@ -212,7 +212,7 @@ public: } TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - &m_request->req_request->req_stats, m_request->req_fetch_elapsed, + &m_request->getJrdRequest()->req_stats, m_request->req_fetch_elapsed, m_request->req_fetch_rowcount); TraceSQLStatementImpl stmt(m_request, stats.getPerf()); @@ -227,7 +227,7 @@ public: private: bool m_need_trace; Attachment* const m_attachment; - dsql_req* const m_request; + DsqlRequest* const m_request; SINT64 m_start_clock; }; diff --git a/src/jrd/trace/TraceJrdHelpers.h b/src/jrd/trace/TraceJrdHelpers.h index eef6e7b16c..5643a48e1d 100644 --- a/src/jrd/trace/TraceJrdHelpers.h +++ b/src/jrd/trace/TraceJrdHelpers.h @@ -392,7 +392,7 @@ public: m_start_clock = fb_utils::query_performance_counter(); } - void finish(jrd_req* request, ntrace_result_t result) + void finish(JrdStatement* statement, ntrace_result_t result) { if (!m_need_trace) return; @@ -406,9 +406,9 @@ public: TraceConnectionImpl conn(m_tdbb->getAttachment()); TraceTransactionImpl tran(m_tdbb->getTransaction()); - if (request) + if (statement) { - TraceBLRStatementImpl stmt(request, NULL); + TraceBLRStatementImpl stmt(statement, NULL); trace_mgr->event_blr_compile(&conn, m_tdbb->getTransaction() ? &tran : NULL, &stmt, m_start_clock, result); } @@ -474,7 +474,7 @@ public: TraceConnectionImpl conn(m_tdbb->getAttachment()); TraceTransactionImpl tran(m_tdbb->getTransaction()); - TraceBLRStatementImpl stmt(m_request, stats.getPerf()); + TraceBLRStatementImpl stmt(m_request->getStatement(), stats.getPerf()); TraceManager* trace_mgr = m_tdbb->getAttachment()->att_trace_manager; trace_mgr->event_blr_execute(&conn, &tran, &stmt, result); diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 9cc507aa1d..56c5099477 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -169,8 +169,8 @@ ISC_INT64 TraceTransactionImpl::getInitialID() ISC_INT64 TraceSQLStatementImpl::getStmtID() { - if (m_stmt->req_request) - return m_stmt->req_request->getRequestId(); + if (m_stmt->getJrdRequest()) + return m_stmt->getJrdRequest()->getRequestId(); return 0; } @@ -211,8 +211,8 @@ void TraceSQLStatementImpl::fillPlan(bool explained) if (m_plan.isEmpty() || m_planExplained != explained) { m_planExplained = explained; - if (m_stmt->req_request) - m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->req_request->getStatement(), m_planExplained); + if (m_stmt->getJrdStatement()) + m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->getJrdStatement(), m_planExplained); } } @@ -234,6 +234,14 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() if (m_descs.getCount() || !m_params) return; + if (!m_stmt->isDml()) + { + fb_assert(false); + return; + } + + const auto dmlRequest = (DsqlDmlRequest*) m_stmt; + USHORT first_index = 0; for (FB_SIZE_T i = 0 ; i < m_params->getCount(); ++i) { @@ -246,7 +254,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() if (parameter->par_null) { const UCHAR* msgBuffer = - m_stmt->req_msg_buffers[parameter->par_null->par_message->msg_buffer_number]; + dmlRequest->req_msg_buffers[parameter->par_null->par_message->msg_buffer_number]; if (*(SSHORT*) (msgBuffer + (IPTR) parameter->par_null->par_desc.dsc_address)) null_flag = DSC_null; @@ -263,7 +271,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() *desc = parameter->par_desc; desc->dsc_flags |= null_flag; - UCHAR* msgBuffer = m_stmt->req_msg_buffers[parameter->par_message->msg_buffer_number]; + UCHAR* msgBuffer = dmlRequest->req_msg_buffers[parameter->par_message->msg_buffer_number]; desc->dsc_address = msgBuffer + (IPTR) desc->dsc_address; } } diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index 8b13bca77d..2536d74613 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -146,17 +146,17 @@ private: class TraceBLRStatementImpl : public BLRPrinter { public: - TraceBLRStatementImpl(const jrd_req* stmt, Firebird::PerformanceInfo* perf) : - BLRPrinter(stmt->getStatement()->blr.begin(), stmt->getStatement()->blr.getCount()), + TraceBLRStatementImpl(const JrdStatement* stmt, Firebird::PerformanceInfo* perf) : + BLRPrinter(stmt->blr.begin(), stmt->blr.getCount()), m_stmt(stmt), m_perf(perf) {} - ISC_INT64 getStmtID() { return m_stmt->getRequestId(); } + ISC_INT64 getStmtID() { return m_stmt->getStatementId(); } Firebird::PerformanceInfo* getPerf() { return m_perf; } private: - const jrd_req* const m_stmt; + const JrdStatement* const m_stmt; Firebird::PerformanceInfo* const m_perf; }; @@ -177,7 +177,7 @@ class TraceSQLStatementImpl : public Firebird::AutoIface > { public: - TraceSQLStatementImpl(const dsql_req* stmt, Firebird::PerformanceInfo* perf) : + TraceSQLStatementImpl(DsqlRequest* stmt, Firebird::PerformanceInfo* perf) : m_stmt(stmt), m_perf(perf), m_planExplained(false), @@ -198,7 +198,7 @@ private: public Firebird::AutoIface > { public: - DSQLParamsImpl(Firebird::MemoryPool& pool, const dsql_req* const stmt) : + DSQLParamsImpl(Firebird::MemoryPool& pool, DsqlRequest* const stmt) : m_stmt(stmt), m_params(NULL), m_descs(pool) @@ -215,7 +215,7 @@ private: private: void fillParams(); - const dsql_req* const m_stmt; + DsqlRequest* const m_stmt; const Firebird::Array* m_params; Firebird::HalfStaticArray m_descs; Firebird::string temp_utf8_text; @@ -223,7 +223,7 @@ private: void fillPlan(bool explained); - const dsql_req* const m_stmt; + DsqlRequest* const m_stmt; Firebird::PerformanceInfo* const m_perf; Firebird::string m_plan; bool m_planExplained; From b1033181175bd47904e27d716c0ca53e349662d2 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 8 Feb 2022 00:05:48 +0000 Subject: [PATCH 060/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index d01ca1b2c1..d844a92389 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:389 + FORMAL BUILD NUMBER:391 */ -#define PRODUCT_VER_STRING "5.0.0.389" -#define FILE_VER_STRING "WI-T5.0.0.389" -#define LICENSE_VER_STRING "WI-T5.0.0.389" -#define FILE_VER_NUMBER 5, 0, 0, 389 +#define PRODUCT_VER_STRING "5.0.0.391" +#define FILE_VER_STRING "WI-T5.0.0.391" +#define LICENSE_VER_STRING "WI-T5.0.0.391" +#define FILE_VER_NUMBER 5, 0, 0, 391 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "389" +#define FB_BUILD_NO "391" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 3667291cc5..0075b9a75e 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=389 +BuildNum=391 NowAt=`pwd` cd `dirname $0` From de6ea9aa6fb50ca7f1a7c71cd99ebbbb6b198bbf Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 8 Feb 2022 08:36:48 -0300 Subject: [PATCH 061/187] Misc. --- src/dsql/dsql.h | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 6a3266aba7..be7141d6d3 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -123,20 +123,13 @@ namespace Jrd { class dsql_dbb : public pool_alloc { public: - Firebird::GenericMap > > dbb_relations; // known relations in database - Firebird::GenericMap > > dbb_procedures; // known procedures in database - Firebird::GenericMap > > dbb_functions; // known functions in database - Firebird::GenericMap > > dbb_charsets; // known charsets in database - Firebird::GenericMap > > dbb_collations; // known collations in database - Firebird::GenericMap > > dbb_charsets_by_id; // charsets sorted by charset_id - Firebird::GenericMap>> dbb_cursors; // known cursors in database + Firebird::LeftPooledMap dbb_relations; // known relations in database + Firebird::LeftPooledMap dbb_procedures; // known procedures in database + Firebird::LeftPooledMap dbb_functions; // known functions in database + Firebird::LeftPooledMap dbb_charsets; // known charsets in database + Firebird::LeftPooledMap dbb_collations; // known collations in database + Firebird::NonPooledMap dbb_charsets_by_id; // charsets sorted by charset_id + Firebird::LeftPooledMap dbb_cursors; // known cursors in database MemoryPool& dbb_pool; // The current pool for the dbb Attachment* dbb_attachment; @@ -178,13 +171,13 @@ public: { } - class dsql_fld* rel_fields; // Field block - //dsql_rel* rel_base_relation; // base relation for an updatable view - MetaName rel_name; // Name of relation - MetaName rel_owner; // Owner of relation - USHORT rel_id; // Relation id - USHORT rel_dbkey_length; - USHORT rel_flags; + class dsql_fld* rel_fields; // Field block + //dsql_rel* rel_base_relation; // base relation for an updatable view + MetaName rel_name; // Name of relation + MetaName rel_owner; // Owner of relation + USHORT rel_id; // Relation id + USHORT rel_dbkey_length; + USHORT rel_flags; }; // rel_flags bits @@ -507,8 +500,7 @@ public: Firebird::string ctx_internal_alias; // Alias as specified in query DsqlContextStack ctx_main_derived_contexts; // contexts used for blr_derived_expr DsqlContextStack ctx_childs_derived_table; // Childs derived table context - Firebird::GenericMap > > ctx_imp_join; // Map of USING fieldname to ImplicitJoin + Firebird::LeftPooledMap ctx_imp_join; // Map of USING fieldname to ImplicitJoin Firebird::Array ctx_win_maps; // Maps for window functions Firebird::GenericMap ctx_named_windows; From 95e3e71622ab0b26cafa8b184ce08500f2eb613f Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 8 Feb 2022 15:40:37 +0300 Subject: [PATCH 062/187] Postfixes for #7122: Invalid state of mapping cache after replacement of database --- src/jrd/Mapping.cpp | 4 ++-- src/jrd/Mapping.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index 1d874c0adb..76ac4cac24 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -1235,10 +1235,10 @@ InitInstance spCache; void resetMap(const char* db, ULONG index) { - if(index & Mapping::MAPPING_CACHE) + if (index & Mapping::MAPPING_CACHE) resetMap(db); - if(index & Mapping::SYSTEM_PRIVILEGES_CACHE) + if (index & Mapping::SYSTEM_PRIVILEGES_CACHE) spCache().invalidate(db); } diff --git a/src/jrd/Mapping.h b/src/jrd/Mapping.h index b7ac46ea8d..5ff436676e 100644 --- a/src/jrd/Mapping.h +++ b/src/jrd/Mapping.h @@ -79,7 +79,7 @@ public: // possible clearCache() flags static const USHORT MAPPING_CACHE = 0x01; static const USHORT SYSTEM_PRIVILEGES_CACHE = 0x02; - static const USHORT ALL_CACHE = ~0u; + static const USHORT ALL_CACHE = MAX_USHORT; // Helper statuc functions to perform cleanup & shutdown. static void clearCache(const char* dbName, USHORT id); static void shutdownIpc(); From 7ba3b9a49297c88f8011906b67ea1ee02cd95457 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 9 Feb 2022 00:05:48 +0000 Subject: [PATCH 063/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index d844a92389..a7b2a7084d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:391 + FORMAL BUILD NUMBER:393 */ -#define PRODUCT_VER_STRING "5.0.0.391" -#define FILE_VER_STRING "WI-T5.0.0.391" -#define LICENSE_VER_STRING "WI-T5.0.0.391" -#define FILE_VER_NUMBER 5, 0, 0, 391 +#define PRODUCT_VER_STRING "5.0.0.393" +#define FILE_VER_STRING "WI-T5.0.0.393" +#define LICENSE_VER_STRING "WI-T5.0.0.393" +#define FILE_VER_NUMBER 5, 0, 0, 393 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "391" +#define FB_BUILD_NO "393" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 0075b9a75e..a6addf4fa1 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=391 +BuildNum=393 NowAt=`pwd` cd `dirname $0` From e9e4ad5b3ae246a69b23771b259ee2618de8c216 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 9 Feb 2022 09:17:57 +0300 Subject: [PATCH 064/187] Rename/move the files before refactoring (to preserve history) --- src/jrd/{opt.cpp => optimizer/Optimizer.cpp} | 0 src/jrd/{ => optimizer}/Optimizer.h | 0 src/jrd/{Optimizer.cpp => optimizer/Retrieval.cpp} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/jrd/{opt.cpp => optimizer/Optimizer.cpp} (100%) rename src/jrd/{ => optimizer}/Optimizer.h (100%) rename src/jrd/{Optimizer.cpp => optimizer/Retrieval.cpp} (100%) diff --git a/src/jrd/opt.cpp b/src/jrd/optimizer/Optimizer.cpp similarity index 100% rename from src/jrd/opt.cpp rename to src/jrd/optimizer/Optimizer.cpp diff --git a/src/jrd/Optimizer.h b/src/jrd/optimizer/Optimizer.h similarity index 100% rename from src/jrd/Optimizer.h rename to src/jrd/optimizer/Optimizer.h diff --git a/src/jrd/Optimizer.cpp b/src/jrd/optimizer/Retrieval.cpp similarity index 100% rename from src/jrd/Optimizer.cpp rename to src/jrd/optimizer/Retrieval.cpp From 19f2bfdd01461f8a298d519835a9a2b96bd361d8 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 9 Feb 2022 09:35:31 +0300 Subject: [PATCH 065/187] Refactored the optimizer. Better debug logging. --- builds/cmake/SourceGroups.cmake | 2 + builds/mac_os_x/CS/CS.pbproj/project.pbxproj | 15 - builds/posix/make.shared.variables | 2 +- builds/win32/msvc15/engine.vcxproj | 9 +- builds/win32/msvc15/engine.vcxproj.filters | 23 +- src/CMakeLists.txt | 1 + src/dsql/BoolNodes.cpp | 2 +- src/dsql/ExprNodes.cpp | 45 +- src/dsql/ExprNodes.h | 16 +- src/dsql/Nodes.h | 12 +- src/dsql/StmtNodes.cpp | 2 +- src/dsql/dsql.cpp | 4 +- src/intl/lc_ascii.cpp | 2 +- src/jrd/JrdStatement.cpp | 14 + src/jrd/JrdStatement.h | 2 + src/jrd/Monitoring.cpp | 6 +- src/jrd/RecordSourceNodes.cpp | 344 +- src/jrd/RecordSourceNodes.h | 53 +- src/jrd/Relation.cpp | 18 +- src/jrd/VirtualTable.cpp | 1 - src/jrd/btr.cpp | 15 +- src/jrd/btr.h | 6 +- src/jrd/btr_proto.h | 2 +- src/jrd/cmp.cpp | 29 +- src/jrd/constants.h | 4 +- src/jrd/dpm.epp | 1 - src/jrd/err.cpp | 1 - src/jrd/evl.cpp | 1 - src/jrd/exe.cpp | 2 - src/jrd/exe.h | 16 +- src/jrd/ext.cpp | 1 - src/jrd/ext_proto.h | 1 - src/jrd/idx.cpp | 1 - src/jrd/inf.cpp | 1 - src/jrd/jrd.cpp | 2 - src/jrd/opt_proto.h | 54 - src/jrd/optimizer/InnerJoin.cpp | 530 +++ src/jrd/optimizer/Optimizer.cpp | 4360 ++++++++---------- src/jrd/optimizer/Optimizer.h | 881 +++- src/jrd/optimizer/Retrieval.cpp | 1831 ++------ src/jrd/recsrc/ExternalTableScan.cpp | 1 - src/jrd/recsrc/IndexTableScan.cpp | 1 - src/jrd/recsrc/MergeJoin.cpp | 1 - src/jrd/recsrc/RecordSource.cpp | 1 - src/jrd/recsrc/RecordSource.h | 5 +- src/jrd/recsrc/VirtualTableScan.cpp | 1 - src/jrd/recsrc/WindowedStream.cpp | 19 +- src/jrd/rse.h | 116 - src/jrd/sort.cpp | 1 - src/jrd/tra.cpp | 1 - src/jrd/trace/TraceObjects.cpp | 4 +- src/jrd/validation.cpp | 1 - src/jrd/vio.cpp | 1 - 53 files changed, 3705 insertions(+), 4760 deletions(-) delete mode 100644 src/jrd/opt_proto.h create mode 100644 src/jrd/optimizer/InnerJoin.cpp delete mode 100644 src/jrd/rse.h diff --git a/builds/cmake/SourceGroups.cmake b/builds/cmake/SourceGroups.cmake index 58529882e6..b3efa9ec3f 100644 --- a/builds/cmake/SourceGroups.cmake +++ b/builds/cmake/SourceGroups.cmake @@ -42,6 +42,8 @@ source_group("ISQL files\\${EPP_TXT}" "${SSRC}/isql/${EPP}") source_group("ISQL files\\${GEN_TXT}" "${BSRC}/isql/${GEN}") source_group("JRD files" "${SSRC}/jrd/${CPP}") source_group("JRD files\\Data Access" "${SSRC}/jrd/recsrc/${CPP}") +source_group("JRD files\\Optimizer" "${SSRC}/jrd/optimizer/${CPP}") +source_group("JRD files\\Replication" "${SSRC}/jrd/replication/${CPP}") source_group("JRD files\\EXTDS" "${SSRC}/jrd/extds/${CPP}") source_group("JRD files\\${EPP_TXT}" "${SSRC}/jrd/${EPP}") source_group("JRD files\\${GEN_TXT}" "${BSRC}/jrd/${GEN}") diff --git a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj index 628a47746d..416a03d2c3 100644 --- a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj +++ b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj @@ -4881,16 +4881,6 @@ path = old_proto.h; refType = 4; }; - F616C66F0200B0CF01EF0ADE = { - isa = PBXFileReference; - path = opt.cpp; - refType = 4; - }; - F616C6700200B0CF01EF0ADE = { - isa = PBXFileReference; - path = opt_proto.h; - refType = 4; - }; F616C6710200B0CF01EF0ADE = { children = ( F616C6720200B0CF01EF0ADE, @@ -5133,11 +5123,6 @@ path = rse.cpp; refType = 4; }; - F616C6A70200B0D001EF0ADE = { - isa = PBXFileReference; - path = rse.h; - refType = 4; - }; F616C6A80200B0D001EF0ADE = { isa = PBXFileReference; path = rse_proto.h; diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables index fe286cda7e..c5d555b1ad 100644 --- a/builds/posix/make.shared.variables +++ b/builds/posix/make.shared.variables @@ -77,7 +77,7 @@ AllObjects += $(Chacha_Objects) # Engine Engine_Objects:= $(call dirObjects,jrd) $(call dirObjects,dsql) $(call dirObjects,jrd/extds) \ - $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) $(call dirObjects,jrd/trace) \ + $(call dirObjects,jrd/optimizer) $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) $(call dirObjects,jrd/trace) \ $(call makeObjects,lock,lock.cpp) AllObjects += $(Engine_Objects) diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj index 03e6ee6fd6..dd0c9ca3d4 100644 --- a/builds/win32/msvc15/engine.vcxproj +++ b/builds/win32/msvc15/engine.vcxproj @@ -102,8 +102,9 @@ - - + + + @@ -300,8 +301,7 @@ - - + @@ -321,7 +321,6 @@ - diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index 43ca7bbb6b..20a3ffafd3 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -318,12 +318,6 @@ JRD files - - JRD files - - - JRD files - JRD files @@ -486,6 +480,15 @@ JRD files + + Optimizer + + + Optimizer + + + Optimizer + Replication @@ -890,10 +893,7 @@ Header files - - Header files - - + Header files @@ -944,9 +944,6 @@ Header files - - Header files - Header files diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ca816e3395..fffc3d0d7b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -448,6 +448,7 @@ file(GLOB engine_src "dsql/*.cpp" "jrd/*.cpp" "jrd/extds/*.cpp" + "jrd/optimizer/*.cpp" "jrd/recsrc/*.cpp" "jrd/replication/*.cpp" "jrd/trace/*.cpp" diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 7c77793d8b..369583a179 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -28,7 +28,7 @@ #include "../jrd/tra.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/recsrc/Cursor.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index c26204b47a..66d3ba7d5f 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -35,7 +35,7 @@ #include "../jrd/SysFunction.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/recsrc/Cursor.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/recsrc/Cursor.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" @@ -344,15 +344,16 @@ bool ExprNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void ExprNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) +void ExprNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - NodeRefsHolder holder(optRet->getPool()); + NodeRefsHolder holder(csb->csb_pool); getChildren(holder, false); for (auto i : holder.refs) { if (*i) - (*i)->findDependentFromStreams(optRet, streamList); + (*i)->findDependentFromStreams(csb, currentStream, streamList); } } @@ -5035,15 +5036,15 @@ bool DerivedExprNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void DerivedExprNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void DerivedExprNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - arg->findDependentFromStreams(optRet, streamList); + arg->findDependentFromStreams(csb, currentStream, streamList); for (const auto derivedStream : internalStreamList) { - if (derivedStream != optRet->stream && - (optRet->csb->csb_rpt[derivedStream].csb_flags & csb_active)) + if (derivedStream != currentStream && + (csb->csb_rpt[derivedStream].csb_flags & csb_active)) { if (!streamList->exist(derivedStream)) streamList->add(derivedStream); @@ -6479,13 +6480,14 @@ bool FieldNode::computable(CompilerScratch* csb, StreamType stream, return csb->csb_rpt[fieldStream].csb_flags & csb_active; } -void FieldNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) +void FieldNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - // dimitr: OLD/NEW contexts shouldn't create any stream dependencies. + // dimitr: OLD/NEW contexts shouldn't create any stream dependencies - if (fieldStream != optRet->stream && - (optRet->csb->csb_rpt[fieldStream].csb_flags & csb_active) && - !(optRet->csb->csb_rpt[fieldStream].csb_flags & csb_trigger)) + if (fieldStream != currentStream && + (csb->csb_rpt[fieldStream].csb_flags & csb_active) && + !(csb->csb_rpt[fieldStream].csb_flags & csb_trigger)) { if (!streamList->exist(fieldStream)) streamList->add(fieldStream); @@ -10066,9 +10068,10 @@ bool RecordKeyNode::computable(CompilerScratch* csb, StreamType stream, return csb->csb_rpt[recStream].csb_flags & csb_active; } -void RecordKeyNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) +void RecordKeyNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - if (recStream != optRet->stream && (optRet->csb->csb_rpt[recStream].csb_flags & csb_active)) + if (recStream != currentStream && (csb->csb_rpt[recStream].csb_flags & csb_active)) { if (!streamList->exist(recStream)) streamList->add(recStream); @@ -11153,17 +11156,17 @@ bool SubQueryNode::computable(CompilerScratch* csb, StreamType stream, return rse->computable(csb, stream, allowOnlyCurrentStream, value1); } -void SubQueryNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void SubQueryNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { if (value2) - value2->findDependentFromStreams(optRet, streamList); + value2->findDependentFromStreams(csb, currentStream, streamList); - rse->findDependentFromStreams(optRet, streamList); + rse->findDependentFromStreams(csb, currentStream, streamList); // Check value expression, if any. if (value1) - value1->findDependentFromStreams(optRet, streamList); + value1->findDependentFromStreams(csb, currentStream, streamList); } void SubQueryNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 2e9fe25a66..88e2f0f319 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -666,8 +666,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; @@ -795,8 +795,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; @@ -1656,8 +1656,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; @@ -1915,8 +1915,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index e2192a7724..129b39cde6 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -42,7 +42,7 @@ class Node; class NodePrinter; class ExprNode; class NodeRefsHolder; -class OptimizerBlk; +class Optimizer; class OptimizerRetrieval; class RecordSource; class RseNode; @@ -52,7 +52,7 @@ class ValueExprNode; // Must be less then MAX_SSHORT. Not used for static arrays. -const int MAX_CONJUNCTS = 32000; +const unsigned MAX_CONJUNCTS = 32000; // New: MAX_STREAMS should be a multiple of BITS_PER_LONG (32 and hard to believe it will change) @@ -64,7 +64,7 @@ const StreamType STREAM_MAP_LENGTH = MAX_STREAMS + 2; // New formula is simply MAX_STREAMS / BITS_PER_LONG const int OPT_STREAM_BITS = MAX_STREAMS / BITS_PER_LONG; // 128 with 4096 streams -typedef Firebird::HalfStaticArray StreamList; +typedef Firebird::HalfStaticArray StreamList; typedef Firebird::SortedArray SortedStreamList; typedef Firebird::Array > NestValueArray; @@ -692,8 +692,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value = NULL); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual ExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); virtual ExprNode* copy(thread_db* tdbb, NodeCopier& copier) const = 0; @@ -1189,7 +1189,7 @@ public: streamList.add(getStream()); } - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) = 0; + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream) = 0; public: dsql_ctx* dsqlContext; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 5b1d4c3594..49a97caa22 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -34,7 +34,7 @@ #include "../jrd/tra.h" #include "../jrd/Coercion.h" #include "../jrd/Function.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/VirtualTable.h" #include "../jrd/extds/ExtDS.h" diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index f5c67ffe3d..f91f271c66 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -62,8 +62,8 @@ #include "../jrd/ini_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/tra_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/replication/Publisher.h" #include "../jrd/trace/TraceManager.h" @@ -889,7 +889,7 @@ static void sql_info(thread_db* tdbb, { const bool detailed = (item == isc_info_sql_explain_plan); string plan = tdbb->getAttachment()->stringToUserCharSet(tdbb, - OPT_get_plan(tdbb, request->getJrdStatement(), detailed)); + Optimizer::getPlan(tdbb, request->getJrdStatement(), detailed)); if (plan.hasData()) { diff --git a/src/intl/lc_ascii.cpp b/src/intl/lc_ascii.cpp index 22cc9b9d9d..137e6a8ef7 100644 --- a/src/intl/lc_ascii.cpp +++ b/src/intl/lc_ascii.cpp @@ -524,7 +524,7 @@ USHORT famasc_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInChar, U fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); fb_assert(iInLen <= LANGASCII_MAX_KEY); - fb_assert(iOutLen <= LANGASCII_MAX_KEY); +// fb_assert(iOutLen <= LANGASCII_MAX_KEY); fb_assert(iOutLen >= famasc_key_length(obj, iInLen)); // point inbuff at last character diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index 6e9ece57ad..8988213000 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -664,6 +664,20 @@ void JrdStatement::release(thread_db* tdbb) attachment->deletePool(pool); } +// Returns a formatted textual plan for all RseNode's in the specified request +string JrdStatement::getPlan(thread_db* tdbb, bool detailed) const +{ + string plan; + + for (const auto rsb : fors) + { + plan += detailed ? "\nSelect Expression" : "\nPLAN "; + rsb->print(tdbb, plan, detailed, 0); + } + + return plan; +} + // Check that we have enough rights to access all resources this list of triggers touches. void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers, MetaName userName) diff --git a/src/jrd/JrdStatement.h b/src/jrd/JrdStatement.h index 1234b11add..643c784a70 100644 --- a/src/jrd/JrdStatement.h +++ b/src/jrd/JrdStatement.h @@ -63,6 +63,8 @@ public: void verifyAccess(thread_db* tdbb); void release(thread_db* tdbb); + Firebird::string getPlan(thread_db* tdbb, bool detailed) const; + private: static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers, MetaName userName); diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index be19a879fe..bfec859023 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -39,7 +39,6 @@ #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/cvt_proto.h" #include "../jrd/CryptoManager.h" @@ -47,6 +46,7 @@ #include "../jrd/RecordBuffer.h" #include "../jrd/Monitoring.h" #include "../jrd/Function.h" +#include "../jrd/optimizer/Optimizer.h" #ifdef WIN_NT #include @@ -1493,7 +1493,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) { if (!(statement->flags & (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) { - const string plan = OPT_get_plan(tdbb, statement, true); + const string plan = Optimizer::getPlan(tdbb, statement, true); putStatement(record, statement, plan); } } @@ -1507,7 +1507,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) if (!(statement->flags & (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) { - const string plan = OPT_get_plan(tdbb, statement, true); + const string plan = Optimizer::getPlan(tdbb, statement, true); putRequest(record, request, plan); } } diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 7e46554621..1f5239e75e 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -22,7 +22,6 @@ #include "../jrd/align.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/DataTypeUtil.h" -#include "../jrd/Optimizer.h" #include "../jrd/recsrc/RecordSource.h" #include "../dsql/BoolNodes.h" #include "../dsql/ExprNodes.h" @@ -32,12 +31,12 @@ #include "../jrd/cmp_proto.h" #include "../common/dsc_proto.h" #include "../jrd/met_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/gen_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/pass1_proto.h" +#include "../jrd/optimizer/Optimizer.h" using namespace Firebird; using namespace Jrd; @@ -54,45 +53,6 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS static ValueExprNode* resolveUsingField(DsqlCompilerScratch* dsqlScratch, const MetaName& name, ValueListNode* list, const FieldNode* flawedNode, const TEXT* side, dsql_ctx*& ctx); -namespace -{ - class AutoActivateResetStreams : public AutoStorage - { - public: - AutoActivateResetStreams(CompilerScratch* csb, const RseNode* rse) - : m_csb(csb), m_streams(getPool()), m_flags(getPool()) - { - rse->computeRseStreams(m_streams); - - if (m_streams.getCount() >= MAX_STREAMS) - ERR_post(Arg::Gds(isc_too_many_contexts)); - - m_flags.resize(m_streams.getCount()); - - for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) - { - const StreamType stream = m_streams[i]; - m_flags[i] = m_csb->csb_rpt[stream].csb_flags; - m_csb->csb_rpt[stream].csb_flags |= (csb_active | csb_sub_stream); - } - } - - ~AutoActivateResetStreams() - { - for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) - { - const StreamType stream = m_streams[i]; - m_csb->csb_rpt[stream].csb_flags = m_flags[i]; - } - } - - private: - CompilerScratch* m_csb; - StreamList m_streams; - HalfStaticArray m_flags; - }; -} - //-------------------- @@ -153,11 +113,11 @@ bool SortNode::computable(CompilerScratch* csb, StreamType stream, bool allowOnl return true; } -void SortNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void SortNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) - (*i)->findDependentFromStreams(optRet, streamList); + (*i)->findDependentFromStreams(csb, currentStream, streamList); } @@ -570,14 +530,11 @@ void LocalTableSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) pass2(tdbb, csb); } -RecordSource* LocalTableSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* LocalTableSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - opt->localStreams.add(stream); + const auto csb = opt->getCompilerScratch(); - const auto csb = opt->opt_csb; - - if (tableNumber >= opt->opt_csb->csb_localTables.getCount() || !opt->opt_csb->csb_localTables[tableNumber]) + if (tableNumber >= csb->csb_localTables.getCount() || !csb->csb_localTables[tableNumber]) ERR_post(Arg::Gds(isc_bad_loctab_num) << Arg::Num(tableNumber)); auto localTable = csb->csb_localTables[tableNumber]; @@ -855,12 +812,11 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN // disect view into component relations - NestConst* arg = viewRse->rse_relations.begin(); - for (const NestConst* const end = viewRse->rse_relations.end(); arg != end; ++arg) + for (const auto sub : viewRse->rse_relations) { // this call not only copies the node, it adds any streams it finds to the map NodeCopier copier(csb->csb_pool, csb, map); - RecordSourceNode* node = (*arg)->copy(tdbb, copier); + RecordSourceNode* node = sub->copy(tdbb, copier); // Now go out and process the base table itself. This table might also be a view, // in which case we will continue the process by recursion. @@ -916,22 +872,9 @@ void RelationSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) pass2(tdbb, csb); } -RecordSource* RelationSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* RelationSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - // we have found a base relation; record its stream - // number in the streams array as a candidate for - // merging into a river - - opt->beds.add(stream); - opt->compileStreams.add(stream); - - // if we have seen any booleans or sort fields, we may be able to - // use an index to optimize them; retrieve the current format of - // all indices at this time so we can determine if it's possible - - const bool needIndices = - opt->opt_conjuncts.getCount() || opt->rse->rse_sorted || opt->rse->rse_aggregate; - OPT_compile_relation(tdbb, relation, opt->opt_csb, stream, needIndices); + opt->compileRelation(stream); return NULL; } @@ -1293,21 +1236,18 @@ void ProcedureSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) pass2(tdbb, csb); } -RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - opt->localStreams.add(stream); - return generate(tdbb, opt); } // Compile and optimize a record selection expression into a set of record source blocks (rsb's). -ProcedureScan* ProcedureSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt) +ProcedureScan* ProcedureSourceNode::generate(thread_db* tdbb, Optimizer* opt) { SET_TDBB(tdbb); - CompilerScratch* const csb = opt->opt_csb; - const string alias = OPT_make_alias(csb, stream); + const auto csb = opt->getCompilerScratch(); + const string alias = opt->makeAlias(stream); return FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureScan(csb, alias, stream, procedure, sourceList, targetList, in_msg); @@ -1325,14 +1265,14 @@ bool ProcedureSourceNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void ProcedureSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void ProcedureSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { if (sourceList) - sourceList->findDependentFromStreams(optRet, streamList); + sourceList->findDependentFromStreams(csb, currentStream, streamList); if (targetList) - targetList->findDependentFromStreams(optRet, streamList); + targetList->findDependentFromStreams(csb, currentStream, streamList); } void ProcedureSourceNode::collectStreams(SortedStreamList& streamList) const @@ -1643,29 +1583,23 @@ bool AggregateSourceNode::containsStream(StreamType checkStream) const return false; } -RecordSource* AggregateSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* AggregateSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - BoolExprNodeStack conjunctStack; - for (USHORT i = 0; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + conjunctStack.push(iter); - RecordSource* const rsb = generate(tdbb, opt, &conjunctStack, stream); - - opt->localStreams.add(stream); - - return rsb; + return generate(tdbb, opt, &conjunctStack, stream); } // Generate a RecordSource (Record Source Block) for each aggregate operation. // Generate an AggregateSort (Aggregate SortedStream Block) for each DISTINCT aggregate. -RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, +RecordSource* AggregateSourceNode::generate(thread_db* tdbb, Optimizer* opt, BoolExprNodeStack* parentStack, StreamType shellStream) { SET_TDBB(tdbb); - CompilerScratch* const csb = opt->opt_csb; + const auto csb = opt->getCompilerScratch(); rse->rse_sorted = group; // AB: Try to distribute items from the HAVING CLAUSE to the WHERE CLAUSE. @@ -1701,7 +1635,7 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, rse->flags |= RseNode::FLAG_OPT_FIRST_ROWS; } - RecordSource* const nextRsb = OPT_compile(tdbb, csb, rse, &deliverStack); + RecordSource* const nextRsb = Optimizer::compile(tdbb, csb, rse, &deliverStack); // allocate and optimize the record source block @@ -1716,7 +1650,7 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, aggNode->indexed = true; } - OPT_gen_aggregate_distincts(tdbb, csb, map); + opt->generateAggregateDistincts(map); return rsb; } @@ -1728,11 +1662,11 @@ bool AggregateSourceNode::computable(CompilerScratch* csb, StreamType stream, return rse->computable(csb, stream, allowOnlyCurrentStream, NULL); } -void AggregateSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void AggregateSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { rse->rse_sorted = group; - rse->findDependentFromStreams(optRet, streamList); + rse->findDependentFromStreams(csb, currentStream, streamList); } @@ -1986,32 +1920,25 @@ bool UnionSourceNode::containsStream(StreamType checkStream) const return false; } -RecordSource* UnionSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* UnionSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - - const FB_SIZE_T oldCount = opt->keyStreams.getCount(); - computeDbKeyStreams(opt->keyStreams); + StreamList keyStreams; + computeDbKeyStreams(keyStreams); BoolExprNodeStack conjunctStack; - for (USHORT i = 0; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + conjunctStack.push(iter); - RecordSource* const rsb = generate(tdbb, opt, opt->keyStreams.begin() + oldCount, - opt->keyStreams.getCount() - oldCount, &conjunctStack, stream); - - opt->localStreams.add(stream); - - return rsb; + return generate(tdbb, opt, keyStreams.begin(), keyStreams.getCount(), &conjunctStack, stream); } // Generate an union complex. -RecordSource* UnionSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, const StreamType* streams, +RecordSource* UnionSourceNode::generate(thread_db* tdbb, Optimizer* opt, const StreamType* streams, FB_SIZE_T nstreams, BoolExprNodeStack* parentStack, StreamType shellStream) { SET_TDBB(tdbb); - CompilerScratch* csb = opt->opt_csb; + const auto csb = opt->getCompilerScratch(); HalfStaticArray rsbs; const ULONG baseImpure = csb->allocImpure(FB_ALIGNMENT, 0); @@ -2031,7 +1958,7 @@ RecordSource* UnionSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, cons if (!recursive) genDeliverUnmapped(csb, &deliverStack, map, parentStack, shellStream); - rsbs.add(OPT_compile(tdbb, csb, rse, &deliverStack)); + rsbs.add(Optimizer::compile(tdbb, csb, rse, &deliverStack)); // hvlad: activate recursive union itself after processing first (non-recursive) // member to allow recursive members be optimized @@ -2075,13 +2002,11 @@ bool UnionSourceNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void UnionSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void UnionSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - NestConst* ptr = clauses.begin(); - - for (NestConst* const end = clauses.end(); ptr != end; ++ptr) - (*ptr)->findDependentFromStreams(optRet, streamList); + for (auto clause : clauses) + clause->findDependentFromStreams(csb, currentStream, streamList); } @@ -2421,24 +2346,12 @@ void WindowSourceNode::collectStreams(SortedStreamList& streamList) const } } -RecordSource* WindowSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* WindowSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - for (ObjectsArray::iterator window = windows.begin(); - window != windows.end(); - ++window) - { - opt->beds.add(window->stream); - } + const auto csb = opt->getCompilerScratch(); - RecordSource* const rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) WindowedStream(tdbb, opt->opt_csb, - windows, OPT_compile(tdbb, opt->opt_csb, rse, NULL)); - - StreamList rsbStreams; - rsb->findUsedStreams(rsbStreams); - - opt->localStreams.join(rsbStreams); - - return rsb; + return FB_NEW_POOL(*tdbb->getDefaultPool()) WindowedStream(tdbb, opt, + windows, Optimizer::compile(tdbb, csb, rse, NULL)); } bool WindowSourceNode::computable(CompilerScratch* csb, StreamType stream, @@ -2457,10 +2370,10 @@ void WindowSourceNode::computeRseStreams(StreamList& streamList) const } } -void WindowSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void WindowSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - rse->findDependentFromStreams(optRet, streamList); + rse->findDependentFromStreams(csb, currentStream, streamList); } @@ -2838,10 +2751,8 @@ RseNode* RseNode::copy(thread_db* tdbb, NodeCopier& copier) const { RseNode* newSource = FB_NEW_POOL(*tdbb->getDefaultPool()) RseNode(*tdbb->getDefaultPool()); - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - newSource->rse_relations.add((*ptr)->copy(tdbb, copier)); + for (const auto sub : rse_relations) + newSource->rse_relations.add(sub->copy(tdbb, copier)); newSource->flags = flags; newSource->rse_jointype = rse_jointype; @@ -2873,10 +2784,9 @@ RseNode* RseNode::pass1(thread_db* tdbb, CompilerScratch* csb) bool topLevelRse = true; - for (ExprNode** node = csb->csb_current_nodes.begin(); - node != csb->csb_current_nodes.end(); ++node) + for (const auto node : csb->csb_current_nodes) { - if (nodeAs(*node)) + if (nodeAs(node)) { topLevelRse = false; break; @@ -2897,14 +2807,13 @@ RseNode* RseNode::pass1(thread_db* tdbb, CompilerScratch* csb) PlanNode* plan = rse_plan; // zip thru RseNode expanding views and inner joins - NestConst* arg = rse_relations.begin(); - for (const NestConst* const end = rse_relations.end(); arg != end; ++arg) - processSource(tdbb, csb, this, *arg, &boolean, stack); + for (auto sub : rse_relations) + processSource(tdbb, csb, this, sub, &boolean, stack); // Now, rebuild the RseNode block. rse_relations.resize(stack.getCount()); - arg = rse_relations.end(); + auto arg = rse_relations.end(); while (stack.hasData()) *--arg = stack.pop(); @@ -2981,9 +2890,8 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, !rse_sorted && !rse_projection && !rse_first && !rse_skip && !rse_plan) { - NestConst* arg = rse_relations.begin(); - for (const NestConst* const end = rse_relations.end(); arg != end; ++arg) - processSource(tdbb, csb, rse, *arg, boolean, stack); + for (auto sub : rse_relations) + processSource(tdbb, csb, rse, sub, boolean, stack); // fold in the boolean for this inner join with the one for the parent @@ -3026,10 +2934,8 @@ void RseNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) if (rse_skip) ExprNode::doPass2(tdbb, csb, rse_skip.getAddress()); - NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->pass2Rse(tdbb, csb); + for (auto sub : rse_relations) + sub->pass2Rse(tdbb, csb); ExprNode::doPass2(tdbb, csb, rse_boolean.getAddress()); ExprNode::doPass2(tdbb, csb, rse_sorted.getAddress()); @@ -3052,12 +2958,8 @@ bool RseNode::containsStream(StreamType checkStream) const // Look through all relation nodes in this RseNode to see // if the field references this instance of the relation. - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) + for (const auto sub : rse_relations) { - const RecordSourceNode* sub = *ptr; - if (sub->containsStream(checkStream)) return true; // do not mark as variant } @@ -3065,15 +2967,13 @@ bool RseNode::containsStream(StreamType checkStream) const return false; } -RecordSource* RseNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) +RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream) { - // for nodes which are not relations, generate an rsb to + // For nodes which are not relations, generate an rsb to // represent that work has to be done to retrieve them; // find all the substreams involved and compile them as well - computeRseStreams(opt->beds); - computeRseStreams(opt->localStreams); - computeDbKeyStreams(opt->keyStreams); + const auto csb = opt->getCompilerScratch(); BoolExprNodeStack conjunctStack; @@ -3081,8 +2981,7 @@ RecordSource* RseNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSub // pass RseNode boolean only to inner substreams because join condition // should never exclude records from outer substreams - if (opt->rse->rse_jointype == blr_inner || - (opt->rse->rse_jointype == blr_left && innerSubStream)) + if (opt->isInnerJoin() || (opt->isLeftJoin() && innerSubStream)) { // AB: For an (X LEFT JOIN Y) mark the outer-streams (X) as // active because the inner-streams (Y) are always "dependent" @@ -3090,49 +2989,35 @@ RecordSource* RseNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSub // // dimitr: the same for lateral derived tables in inner joins - if (opt->rse->rse_jointype == blr_left || isLateral) - { - for (StreamList::iterator i = opt->outerStreams.begin(); - i != opt->outerStreams.end(); - ++i) - { - opt->opt_csb->csb_rpt[*i].activate(); - } + StreamStateHolder stateHolder(csb, opt->getOuterStreams()); - if (opt->rse->rse_jointype == blr_left) + if (opt->isLeftJoin() || isLateral) + { + stateHolder.activate(); + + if (opt->isLeftJoin()) { // Push all conjuncts except "missing" ones (e.g. IS NULL) - for (USHORT i = 0; i < opt->opt_base_missing_conjuncts; i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter) + conjunctStack.push(iter); } } else { - for (USHORT i = 0; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + conjunctStack.push(iter); } - RecordSource* const rsb = OPT_compile(tdbb, opt->opt_csb, this, &conjunctStack); - - if (opt->rse->rse_jointype == blr_left || isLateral) - { - for (StreamList::iterator i = opt->outerStreams.begin(); - i != opt->outerStreams.end(); ++i) - { - opt->opt_csb->csb_rpt[*i].deactivate(); - } - } - - return rsb; + return Optimizer::compile(tdbb, csb, this, &conjunctStack); } // Push only parent conjuncts to the outer stream - for (USHORT i = opt->opt_base_parent_conjuncts; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(false, true); iter.hasData(); ++iter) + conjunctStack.push(iter); - return OPT_compile(tdbb, opt->opt_csb, this, &conjunctStack); + return Optimizer::compile(tdbb, csb, this, &conjunctStack); } // Check that all streams in the RseNode have a plan specified for them. @@ -3141,11 +3026,8 @@ void RseNode::planCheck(const CompilerScratch* csb) const { // if any streams are not marked with a plan, give an error - const NestConst* ptr = rse_relations.begin(); - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) + for (const auto node : rse_relations) { - const RecordSourceNode* node = *ptr; - if (nodeIs(node)) { const StreamType stream = node->getStream(); @@ -3156,8 +3038,8 @@ void RseNode::planCheck(const CompilerScratch* csb) const Arg::Str(csb->csb_rpt[stream].csb_relation->rel_name)); } } - else if (nodeIs(node)) - static_cast(node)->planCheck(csb); + else if (const auto rse = nodeAs(node)) + rse->planCheck(csb); } } @@ -3168,12 +3050,8 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan) { if (plan->type == PlanNode::TYPE_JOIN) { - for (NestConst* ptr = plan->subNodes.begin(), *end = plan->subNodes.end(); - ptr != end; - ++ptr) - { - planSet(csb, *ptr); - } + for (auto planNode : plan->subNodes) + planSet(csb, planNode); } if (plan->type != PlanNode::TYPE_RETRIEVE) @@ -3353,18 +3231,14 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan) void RseNode::computeDbKeyStreams(StreamList& streamList) const { - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->computeDbKeyStreams(streamList); + for (const auto sub : rse_relations) + sub->computeDbKeyStreams(streamList); } void RseNode::computeRseStreams(StreamList& streamList) const { - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->computeRseStreams(streamList); + for (const auto sub : rse_relations) + sub->computeRseStreams(streamList); } bool RseNode::computable(CompilerScratch* csb, StreamType stream, @@ -3376,11 +3250,11 @@ bool RseNode::computable(CompilerScratch* csb, StreamType stream, if (rse_skip && !rse_skip->computable(csb, stream, allowOnlyCurrentStream)) return false; - const NestConst* const end = rse_relations.end(); - NestConst* ptr; - // Set sub-streams of rse active - AutoActivateResetStreams activator(csb, this); + StreamList streams; + computeRseStreams(streams); + StreamStateHolder streamHolder(csb, streams); + streamHolder.activate(true); // Check sub-stream if ((rse_boolean && !rse_boolean->computable(csb, stream, allowOnlyCurrentStream)) || @@ -3390,9 +3264,9 @@ bool RseNode::computable(CompilerScratch* csb, StreamType stream, return false; } - for (ptr = rse_relations.begin(); ptr != end; ++ptr) + for (auto sub : rse_relations) { - if (!(*ptr)->computable(csb, stream, allowOnlyCurrentStream, NULL)) + if (!sub->computable(csb, stream, allowOnlyCurrentStream, NULL)) return false; } @@ -3403,29 +3277,26 @@ bool RseNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void RseNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void RseNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { if (rse_first) - rse_first->findDependentFromStreams(optRet, streamList); + rse_first->findDependentFromStreams(csb, currentStream, streamList); if (rse_skip) - rse_skip->findDependentFromStreams(optRet, streamList); + rse_skip->findDependentFromStreams(csb, currentStream, streamList); if (rse_boolean) - rse_boolean->findDependentFromStreams(optRet, streamList); + rse_boolean->findDependentFromStreams(csb, currentStream, streamList); if (rse_sorted) - rse_sorted->findDependentFromStreams(optRet, streamList); + rse_sorted->findDependentFromStreams(csb, currentStream, streamList); if (rse_projection) - rse_projection->findDependentFromStreams(optRet, streamList); + rse_projection->findDependentFromStreams(csb, currentStream, streamList); - NestConst* ptr; - const NestConst* end; - - for (ptr = rse_relations.begin(), end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->findDependentFromStreams(optRet, streamList); + for (auto sub : rse_relations) + sub->findDependentFromStreams(csb, currentStream, streamList); } void RseNode::collectStreams(SortedStreamList& streamList) const @@ -3444,11 +3315,8 @@ void RseNode::collectStreams(SortedStreamList& streamList) const // rse_sorted->collectStreams(streamList); // rse_projection->collectStreams(streamList); - const NestConst* ptr; - const NestConst* end; - - for (ptr = rse_relations.begin(), end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->collectStreams(streamList); + for (const auto sub : rse_relations) + sub->collectStreams(streamList); } diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index a564b88121..25c35253ad 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -69,7 +69,8 @@ public: SortNode* pass1(thread_db* tdbb, CompilerScratch* csb); SortNode* pass2(thread_db* tdbb, CompilerScratch* csb); bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream); - void findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList); + void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); NullsPlacement getEffectiveNullOrder(unsigned index) const { @@ -339,12 +340,12 @@ public: return true; } - void findDependentFromStreams(const OptimizerRetrieval* /*optRet*/, - SortedStreamList* /*streamList*/) override + void findDependentFromStreams(const CompilerScratch* /*csb*/, + StreamType /*currentStream*/, SortedStreamList* /*streamList*/) override { } - RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) override; + RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream) override; public: Firebird::string alias; @@ -409,12 +410,12 @@ public: return true; } - virtual void findDependentFromStreams(const OptimizerRetrieval* /*optRet*/, - SortedStreamList* /*streamList*/) + virtual void findDependentFromStreams(const CompilerScratch* /*csb*/, + StreamType /*currentStream*/, SortedStreamList* /*streamList*/) { } - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); public: MetaName dsqlName; @@ -481,15 +482,15 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void collectStreams(SortedStreamList& streamList) const; - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: - ProcedureScan* generate(thread_db* tdbb, OptimizerBlk* opt); + ProcedureScan* generate(thread_db* tdbb, Optimizer* opt); public: QualifiedName dsqlName; @@ -565,15 +566,15 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: void genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map); - RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, BoolExprNodeStack* parentStack, + RecordSource* generate(thread_db* tdbb, Optimizer* opt, BoolExprNodeStack* parentStack, StreamType shellStream); public: @@ -631,13 +632,13 @@ public: virtual void computeDbKeyStreams(StreamList& streamList) const; virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: - RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, const StreamType* streams, + RecordSource* generate(thread_db* tdbb, Optimizer* opt, const StreamType* streams, FB_SIZE_T nstreams, BoolExprNodeStack* parentStack, StreamType shellStream); public: @@ -713,9 +714,9 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: NestConst rse; @@ -833,12 +834,12 @@ public: virtual void computeRseStreams(StreamList& streamList) const; virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void collectStreams(SortedStreamList& streamList) const; - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: void planCheck(const CompilerScratch* csb) const; @@ -928,7 +929,7 @@ public: fb_assert(false); } - virtual RecordSource* compile(thread_db* /*tdbb*/, OptimizerBlk* /*opt*/, bool /*innerSubStream*/) + virtual RecordSource* compile(thread_db* /*tdbb*/, Optimizer* /*opt*/, bool /*innerSubStream*/) { fb_assert(false); return NULL; diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index 7d753399a7..3e989d1cb4 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -127,19 +127,18 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a if (!idxTran) idxTran = attachment->getSysTransaction(); - IndexDescAlloc* indices = NULL; + IndexDescList indices; // read indices from "base" index root page - const USHORT idx_count = BTR_all(tdbb, this, &indices, &rel_pages_base); + BTR_all(tdbb, this, indices, &rel_pages_base); - const index_desc* const end = indices->items + idx_count; - for (index_desc* idx = indices->items; idx < end; idx++) + for (auto& idx : indices) { MetaName idx_name; - MET_lookup_index(tdbb, idx_name, this->rel_name, idx->idx_id + 1); + MET_lookup_index(tdbb, idx_name, this->rel_name, idx.idx_id + 1); - idx->idx_root = 0; + idx.idx_root = 0; SelectivityList selectivity(*pool); - IDX_create_index(tdbb, this, idx, idx_name.c_str(), NULL, idxTran, selectivity); + IDX_create_index(tdbb, this, &idx, idx_name.c_str(), NULL, idxTran, selectivity); #ifdef VIO_DEBUG VIO_trace(DEBUG_WRITES, @@ -147,15 +146,14 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a rel_id, newPages->rel_instance_id, newPages->rel_index_root, - idx->idx_id, - idx->idx_root, + idx.idx_id, + idx.idx_root, newPages); #endif } if (poolCreated) dbb->deletePool(pool); - delete indices; return newPages; } diff --git a/src/jrd/VirtualTable.cpp b/src/jrd/VirtualTable.cpp index 7eaa5bb2c2..1c3485425a 100644 --- a/src/jrd/VirtualTable.cpp +++ b/src/jrd/VirtualTable.cpp @@ -29,7 +29,6 @@ #include "../jrd/ids.h" #include "../jrd/ini.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/val.h" #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 073b143cfd..3cc257ccc3 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -340,7 +340,7 @@ void IndexErrorContext::raise(thread_db* tdbb, idx_e result, Record* record) } -USHORT BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescAlloc** csb_idx, RelationPages* relPages) +void BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescList& idxList, RelationPages* relPages) { /************************************** * @@ -362,21 +362,16 @@ USHORT BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescAlloc** csb_idx, Rel index_root_page* const root = fetch_root(tdbb, &window, relation, relPages); if (!root) - return 0; + return; - delete *csb_idx; - *csb_idx = FB_NEW_RPT(*tdbb->getDefaultPool(), root->irt_count) IndexDescAlloc(); - - index_desc* buffer = (*csb_idx)->items; - USHORT count = 0; for (USHORT i = 0; i < root->irt_count; i++) { - if (BTR_description(tdbb, relation, root, &buffer[count], i)) - count++; + index_desc idx; + if (BTR_description(tdbb, relation, root, &idx, i)) + idxList.add(idx); } CCH_RELEASE(tdbb, &window); - return count; } diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 47cc91c009..96fa19494d 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -76,11 +76,7 @@ struct index_desc } idx_rpt[MAX_INDEX_SEGMENTS]; }; -struct IndexDescAlloc : public pool_alloc_rpt -{ - index_desc items[1]; -}; - +typedef Firebird::HalfStaticArray IndexDescList; const USHORT idx_invalid = USHORT(~0); // Applies to idx_id as special value diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index 71a38586fa..83bbcde035 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -29,7 +29,7 @@ #include "../jrd/req.h" #include "../jrd/exe.h" -USHORT BTR_all(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::IndexDescAlloc**, Jrd::RelationPages*); +void BTR_all(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::IndexDescList&, Jrd::RelationPages*); void BTR_complement_key(Jrd::temporary_key*); void BTR_create(Jrd::thread_db*, Jrd::IndexCreation&, Jrd::SelectivityList&); bool BTR_delete_index(Jrd::thread_db*, Jrd::win*, USHORT); diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index ba6c8c4ba3..dab8c7ffe3 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -48,7 +48,6 @@ #include "../jrd/align.h" #include "../jrd/lls.h" #include "../jrd/exe.h" -#include "../jrd/rse.h" #include "../jrd/scl.h" #include "../jrd/tra.h" #include "../jrd/lck.h" @@ -69,12 +68,11 @@ #include "../jrd/jrd_proto.h" #include "../jrd/lck_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../common/dsc_proto.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/SysFunction.h" @@ -520,32 +518,15 @@ RecordSource* CMP_post_rse(thread_db* tdbb, CompilerScratch* csb, RseNode* rse) **************************************/ SET_TDBB(tdbb); - DEV_BLKCHK(csb, type_csb); - DEV_BLKCHK(rse, type_nod); + const auto rsb = Optimizer::compile(tdbb, csb, rse); - RecordSource* rsb = OPT_compile(tdbb, csb, rse, NULL); - - if (rse->flags & RseNode::FLAG_SINGULAR) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) SingularStream(csb, rsb); - - if (rse->flags & RseNode::FLAG_WRITELOCK) - { - for (StreamType i = 0; i < csb->csb_n_stream; i++) - csb->csb_rpt[i].csb_flags |= csb_update; - - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) LockedStream(csb, rsb); - } - - if (rse->flags & RseNode::FLAG_SCROLLABLE) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) BufferedStream(csb, rsb); - - // mark all the substreams as inactive + // Mark all the substreams as inactive StreamList streams; rse->computeRseStreams(streams); - for (StreamList::iterator i = streams.begin(); i != streams.end(); ++i) - csb->csb_rpt[*i].deactivate(); + for (const auto stream : streams) + csb->csb_rpt[stream].deactivate(); return rsb; } diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 130c05b8aa..508a4bc33d 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -462,8 +462,8 @@ const TraNumber MAX_TRA_NUMBER = 0x0000FFFFFFFFFFFF; // ~2.8 * 10^14 // Number of streams, conjuncts, indices that will be statically allocated // in various arrays. Larger numbers will have to be allocated dynamically -// CVC: I think we need to have a special, higher value for streams. -const int OPT_STATIC_ITEMS = 64; +const unsigned OPT_STATIC_ITEMS = 16; +const unsigned OPT_STATIC_STREAMS = 64; #define CURRENT_ENGINE "Engine13" #define EMBEDDED_PROVIDERS "Providers=" CURRENT_ENGINE diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 1c7bc79715..71e5dc2b09 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -48,7 +48,6 @@ #include "../jrd/lck.h" #include "../jrd/cch.h" #include "../jrd/pag.h" -#include "../jrd/rse.h" #include "../jrd/val.h" #include "../jrd/vio_debug.h" #include "../jrd/cch_proto.h" diff --git a/src/jrd/err.cpp b/src/jrd/err.cpp index 582e95d4c9..dd4bd9585c 100644 --- a/src/jrd/err.cpp +++ b/src/jrd/err.cpp @@ -37,7 +37,6 @@ #include "../jrd/ods.h" #include "../jrd/btr.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/tra.h" #include "../jrd/cch_proto.h" #include "../jrd/met_proto.h" diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 5cefa6e919..4e4839f7df 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -78,7 +78,6 @@ #include "../jrd/lls.h" #include "../jrd/intl.h" #include "../jrd/intl_classes.h" -#include "../jrd/rse.h" #include "../jrd/sort.h" #include "firebird/impl/blr.h" #include "../jrd/tra.h" diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 82d84864af..1ab416e332 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -67,7 +67,6 @@ #include "iberror.h" #include "../jrd/ods.h" #include "../jrd/btr.h" -#include "../jrd/rse.h" #include "../jrd/lck.h" #include "../jrd/intl.h" #include "../jrd/sbm.h" @@ -92,7 +91,6 @@ #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../jrd/rlck_proto.h" diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 3a0bd26a25..6bf78430b5 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -42,7 +42,6 @@ #include "iberror.h" #include "../common/dsc.h" -#include "../jrd/rse.h" #include "../jrd/err_proto.h" #include "../jrd/scl.h" @@ -75,7 +74,6 @@ template class vec; class jrd_prc; class Collation; struct index_desc; -struct IndexDescAlloc; class Format; class ForNode; class Cursor; @@ -561,21 +559,20 @@ public: // We must zero-initialize this one csb_repeat(); - void activate(); + void activate(bool subStream = false); void deactivate(); Nullable csb_cursor_number; // Cursor number for this stream StreamType csb_stream; // Map user context to internal stream StreamType csb_view_stream; // stream number for view relation, below USHORT csb_flags; - USHORT csb_indices; // Number of indices jrd_rel* csb_relation; Firebird::string* csb_alias; // SQL alias name for this instance of relation jrd_prc* csb_procedure; jrd_rel* csb_view; // parent view - IndexDescAlloc* csb_idx; // Packed description of indices + IndexDescList* csb_idx; // Packed description of indices MessageNode* csb_message; // Msg for send/receive const Format* csb_format; // Default Format for stream Format* csb_internal_format; // Statement internal format @@ -596,7 +593,6 @@ inline CompilerScratch::csb_repeat::csb_repeat() : csb_stream(0), csb_view_stream(0), csb_flags(0), - csb_indices(0), csb_relation(0), csb_alias(0), csb_procedure(0), @@ -639,9 +635,12 @@ const int csb_unmatched = 512; // stream has conjuncts unmatched by any index const int csb_update = 1024; // erase or modify for relation const int csb_unstable = 2048; // unstable explicit cursor -inline void CompilerScratch::csb_repeat::activate() +inline void CompilerScratch::csb_repeat::activate(bool subStream) { csb_flags |= csb_active; + + if (subStream) + csb_flags |= csb_sub_stream; } inline void CompilerScratch::csb_repeat::deactivate() @@ -671,6 +670,9 @@ public: // must correspond to the declared size of RDB$EXCEPTIONS.RDB$MESSAGE const unsigned XCP_MESSAGE_LENGTH = 1023; +// Array which stores relative pointers to impure areas of invariant nodes +typedef Firebird::SortedArray VarInvariantArray; + } // namespace Jrd #endif // JRD_EXE_H diff --git a/src/jrd/ext.cpp b/src/jrd/ext.cpp index 66b1fbf930..b3e588cbf0 100644 --- a/src/jrd/ext.cpp +++ b/src/jrd/ext.cpp @@ -42,7 +42,6 @@ #include "../jrd/req.h" #include "../jrd/val.h" #include "../jrd/exe.h" -#include "../jrd/rse.h" #include "../jrd/ext.h" #include "../jrd/tra.h" #include "../dsql/ExprNodes.h" diff --git a/src/jrd/ext_proto.h b/src/jrd/ext_proto.h index b58ee5c616..b0ac772187 100644 --- a/src/jrd/ext_proto.h +++ b/src/jrd/ext_proto.h @@ -29,7 +29,6 @@ namespace Jrd { class jrd_tra; class RecordSource; class jrd_rel; - class OptimizerBlk; struct record_param; struct bid; } diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 16d781ed0b..e6997714c4 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -45,7 +45,6 @@ #include "../jrd/exe.h" #include "../jrd/scl.h" #include "../jrd/lck.h" -#include "../jrd/rse.h" #include "../jrd/cch.h" #include "../common/gdsassert.h" #include "../jrd/btr_proto.h" diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index e8c6804a81..8bbf73394a 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -54,7 +54,6 @@ #include "../jrd/cvt_proto.h" #include "../jrd/inf_proto.h" #include "../common/isc_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/os/pio_proto.h" #include "../jrd/tra_proto.h" diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 883b2b14d2..3cd5cd2f62 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -63,7 +63,6 @@ #include "../jrd/exe.h" #include "../jrd/extds/ExtDS.h" #include "../jrd/val.h" -#include "../jrd/rse.h" #include "../jrd/intl.h" #include "../jrd/sbm.h" #include "../jrd/svc.h" @@ -94,7 +93,6 @@ #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/par_proto.h" #include "../jrd/os/pio_proto.h" diff --git a/src/jrd/opt_proto.h b/src/jrd/opt_proto.h deleted file mode 100644 index 3ff42d7dac..0000000000 --- a/src/jrd/opt_proto.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: opt_proto.h - * DESCRIPTION: Prototype header file for opt.cpp - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifndef JRD_OPT_PROTO_H -#define JRD_OPT_PROTO_H - -#include "../jrd/jrd.h" -#include "../jrd/btr.h" -#include "../jrd/rse.h" -#include "../jrd/lls.h" - -namespace Jrd { - class JrdStatement; - class jrd_rel; - class RecordSource; - struct index_desc; - class CompilerScratch; - class OptimizerBlk; - class SortedStream; - class SortNode; - class MapNode; -} - -Firebird::string OPT_get_plan(Jrd::thread_db* tdbb, const Jrd::JrdStatement* statement, bool detailed); -Jrd::RecordSource* OPT_compile(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, - Jrd::RseNode* rse, Jrd::BoolExprNodeStack* parent_stack); -void OPT_compile_relation(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation, Jrd::CompilerScratch* csb, - StreamType stream, bool needIndices); -void OPT_gen_aggregate_distincts(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, Jrd::MapNode* map); -Jrd::SortedStream* OPT_gen_sort(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, const Jrd::StreamList& streams, - const Jrd::StreamList* dbkey_streams, Jrd::RecordSource* prior_rsb, Jrd::SortNode* sort, - bool refetch_flag, bool project_flag); - -#endif // JRD_OPT_PROTO_H diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp new file mode 100644 index 0000000000..97fb72c13d --- /dev/null +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -0,0 +1,530 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Arno Brinkman + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2004 Arno Brinkman + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Adriano dos Santos Fernandes + * Dmitry Yemanov + * + */ + +#include "firebird.h" + +#include "../jrd/jrd.h" +#include "../jrd/exe.h" +#include "../jrd/btr.h" +#include "../jrd/intl.h" +#include "../jrd/Collation.h" +#include "../jrd/ods.h" +#include "../jrd/RecordSourceNodes.h" +#include "../jrd/recsrc/RecordSource.h" +#include "../dsql/BoolNodes.h" +#include "../dsql/ExprNodes.h" +#include "../dsql/StmtNodes.h" +#include "../jrd/btr_proto.h" +#include "../jrd/cch_proto.h" +#include "../jrd/cmp_proto.h" +#include "../jrd/dpm_proto.h" +#include "../jrd/exe_proto.h" +#include "../jrd/ext_proto.h" +#include "../jrd/intl_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/mov_proto.h" +#include "../jrd/par_proto.h" + +#include "../jrd/optimizer/Optimizer.h" + +using namespace Firebird; +using namespace Jrd; + + +// +// Constructor +// + +InnerJoin::InnerJoin(thread_db* aTdbb, Optimizer* opt, + const StreamList& streams, + SortNode* sort_clause, bool hasPlan) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), + optimizer(opt), + csb(opt->getCompilerScratch()), + sort(sort_clause), + plan(hasPlan), + innerStreams(getPool(), streams.getCount()), + joinedStreams(getPool()) +{ + joinedStreams.grow(streams.getCount()); + + for (const auto stream : streams) + innerStreams.add(FB_NEW_POOL(getPool()) StreamInfo(getPool(), stream)); + + calculateStreamInfo(); +} + + +// +// Calculate the needed information for all streams +// + +void InnerJoin::calculateStreamInfo() +{ + StreamList streams; + + // First get the base cost without any relation to any other inner join stream + +#ifdef OPT_DEBUG_RETRIEVAL + optimizer->printf("Base stream info:\n"); +#endif + + for (auto innerStream : innerStreams) + { + streams.add(innerStream->stream); + + const auto tail = &csb->csb_rpt[innerStream->stream]; + tail->activate(); + + Retrieval retrieval(tdbb, optimizer, innerStream->stream, false, false, sort, true); + const auto candidate = retrieval.getInversion(); + + innerStream->baseCost = candidate->cost; + innerStream->baseSelectivity = candidate->selectivity; + innerStream->baseIndexes = candidate->indexes; + innerStream->baseUnique = candidate->unique; + innerStream->baseNavigated = candidate->navigated; + + tail->deactivate(); + } + + StreamStateHolder stateHolder(csb, streams); + stateHolder.activate(); + + // Collect stream inter-dependencies + for (const auto innerStream : innerStreams) + getIndexedRelationships(innerStream); + + // Unless PLAN is enforced, sort the streams based on independency and cost + if (!plan && (innerStreams.getCount() > 1)) + { + auto compare = [] (const void* a, const void* b) + { + const auto first = *static_cast(a); + const auto second = *static_cast(b); + + if (StreamInfo::cheaperThan(first, second)) + return -1; + if (StreamInfo::cheaperThan(second, first)) + return 1; + + return 0; + }; + + qsort(innerStreams.begin(), innerStreams.getCount(), sizeof(StreamInfo*), compare); + } +} + + +// +// Estimate the cost for the stream +// + +void InnerJoin::estimateCost(StreamType stream, + double* cost, + double* resulting_cardinality, + bool start) const +{ + // Create the optimizer retrieval generation class and calculate + // which indexes will be used and the total estimated selectivity will be returned + Retrieval retrieval(tdbb, optimizer, stream, false, false, (start ? sort : nullptr), true); + const auto candidate = retrieval.getInversion(); + + *cost = candidate->cost; + + // Calculate cardinality + const auto tail = &csb->csb_rpt[stream]; + const double cardinality = tail->csb_cardinality * candidate->selectivity; + + *resulting_cardinality = MAX(cardinality, MINIMUM_CARDINALITY); +} + + +// +// Find the best order out of the streams. First return a stream if it can't use +// an index based on a previous stream and it can't be used by another stream. +// Next loop through the remaining streams and find the best order. +// + +bool InnerJoin::findJoinOrder(StreamList& bestStreams) +{ + bestStreams.clear(); + bestCount = 0; + remainingStreams = 0; + +#ifdef OPT_DEBUG + // Debug + printStartOrder(); +#endif + + int filters = 0, navigations = 0; + + for (const auto innerStream : innerStreams) + { + if (!innerStream->used) + { + remainingStreams++; + + const int currentFilter = innerStream->isFiltered() ? 1 : 0; + + if (navigations && currentFilter) + navigations = 0; + + filters += currentFilter; + + if (innerStream->baseNavigated && currentFilter == filters) + navigations++; + + if (innerStream->isIndependent()) + { + if (!bestCount || innerStream->baseCost < bestCost) + { + joinedStreams[0].bestStream = innerStream->stream; + bestCount = 1; + bestCost = innerStream->baseCost; + } + } + } + } + + if (bestCount == 0) + { + IndexedRelationships indexedRelationships; + + for (const auto innerStream : innerStreams) + { + if (!innerStream->used) + { + // If optimization for first rows has been requested and index navigations are + // possible, then consider only join orders starting with a navigational stream. + // Except cases when other streams have local predicates applied. + + const int currentFilter = innerStream->isFiltered() ? 1 : 0; + + if (!optimizer->favorFirstRows() || !navigations || + (innerStream->baseNavigated && currentFilter == filters)) + { + indexedRelationships.clear(); + findBestOrder(0, innerStream, indexedRelationships, 0.0, 1.0); + + if (plan) + { + // If a explicit PLAN was specified we should be ready; + break; + } + } +#ifdef OPT_DEBUG + // Debug + printProcessList(indexedRelationships, innerStream->stream); +#endif + } + } + } + + // Mark streams as used + for (unsigned i = 0; i < bestCount; i++) + { + auto streamInfo = getStreamInfo(joinedStreams[i].bestStream); + streamInfo->used = true; + bestStreams.add(joinedStreams[i].bestStream); + } + +#ifdef OPT_DEBUG + // Debug + printBestOrder(); +#endif + + return bestStreams.hasData(); +} + + +// +// Make different combinations to find out the join order. +// For every position we start with the stream that has the best selectivity +// for that position. If we've have used up all our streams after that +// we assume we're done. +// + +void InnerJoin::findBestOrder(unsigned position, + StreamInfo* stream, + IndexedRelationships& processList, + double cost, + double cardinality) +{ + const bool start = (position == 0); + const auto tail = &csb->csb_rpt[stream->stream]; + + // Do some initializations + tail->activate(); + joinedStreams[position].number = stream->stream; + position++; + + // Save the various flag bits from the optimizer block to reset its + // state after each test + HalfStaticArray streamFlags(innerStreams.getCount()); + for (const auto innerStream : innerStreams) + streamFlags.add(innerStream->used); + + // Compute delta and total estimate cost to fetch this stream + double position_cost, position_cardinality, new_cost = 0, new_cardinality = 0; + + if (!plan) + { + estimateCost(stream->stream, &position_cost, &position_cardinality, start); + new_cost = cost + cardinality * position_cost; + new_cardinality = position_cardinality * cardinality; + } + + // If the partial order is either longer than any previous partial order, + // or the same length and cheap, save order as "best" + if (position > bestCount || (position == bestCount && new_cost < bestCost)) + { + bestCount = position; + bestCost = new_cost; + + const auto end = joinedStreams.begin() + position; + for (auto iter = joinedStreams.begin(); iter != end; ++iter) + { + auto& joinedStream = *iter; + joinedStream.bestStream = joinedStream.number; + } + } + +#ifdef OPT_DEBUG + // Debug information + printFoundOrder(position, position_cost, position_cardinality, new_cost, new_cardinality); +#endif + + // Mark this stream as "used" in the sense that it is already included + // in this particular proposed stream ordering + stream->used = true; + bool done = false; + + // If we've used up all the streams there's no reason to go any further + if (position == remainingStreams) + done = true; + + // If we know a combination with all streams used and the + // current cost is higher as the one from the best we're done + if (bestCount == remainingStreams && bestCost < new_cost) + done = true; + + if (!done && !plan) + { + // Add these relations to the processing list + for (auto& relationship : stream->indexedRelationships) + { + const auto relationStreamInfo = getStreamInfo(relationship.stream); + if (!relationStreamInfo->used) + { + bool found = false; + IndexRelationship* processRelationship = processList.begin(); + for (FB_SIZE_T index = 0; index < processList.getCount(); index++) + { + if (relationStreamInfo->stream == processRelationship[index].stream) + { + // If the cost of this relationship is cheaper then remove the + // old relationship and add this one + if (IndexRelationship::cheaperThan(relationship, processRelationship[index])) + { + processList.remove(index); + break; + } + + found = true; + break; + } + } + if (!found) + { + // Add relationship sorted on cost (cheapest as first) + processList.add(relationship); + } + } + } + + for (const auto& nextRelationship : processList) + { + auto relationStreamInfo = getStreamInfo(nextRelationship.stream); + if (!relationStreamInfo->used) + { + findBestOrder(position, relationStreamInfo, processList, new_cost, new_cardinality); + break; + } + } + } + + if (plan) + { + // If a explicit PLAN was specific pick the next relation. + // The order in innerStreams is expected to be exactly the order as + // specified in the explicit PLAN. + for (auto nextStream : innerStreams) + { + if (!nextStream->used) + { + findBestOrder(position, nextStream, processList, new_cost, new_cardinality); + break; + } + } + } + + // Clean up from any changes made for compute the cost for this stream + tail->deactivate(); + for (FB_SIZE_T i = 0; i < streamFlags.getCount(); i++) + innerStreams[i]->used = streamFlags[i]; +} + + +// +// Check if the testStream can use a index when the baseStream is active. If so +// then we create a indexRelationship and fill it with the needed information. +// The reference is added to the baseStream and the baseStream is added as previous +// expected stream to the testStream. +// + +void InnerJoin::getIndexedRelationships(StreamInfo* testStream) +{ +#ifdef OPT_DEBUG_RETRIEVAL + optimizer->printf("Dependencies for stream %u:\n", testStream->stream); +#endif + + const auto tail = &csb->csb_rpt[testStream->stream]; + + Retrieval retrieval(tdbb, optimizer, testStream->stream, false, false, nullptr, true); + const auto candidate = retrieval.getInversion(); + + for (auto baseStream : innerStreams) + { + if (baseStream->stream != testStream->stream && + candidate->dependentFromStreams.exist(baseStream->stream)) + { + // If we could use more conjunctions on the testing stream + // with the base stream active as without the base stream + // then the test stream has a indexed relationship with the base stream. + IndexRelationship indexRelationship; + indexRelationship.stream = testStream->stream; + indexRelationship.unique = candidate->unique; + indexRelationship.cost = candidate->cost; + indexRelationship.cardinality = candidate->unique ? + tail->csb_cardinality : tail->csb_cardinality * candidate->selectivity; + + // Relationships are kept sorted by cost and uniqueness in the array + baseStream->indexedRelationships.add(indexRelationship); + testStream->previousExpectedStreams++; + } + } +} + + +// +// Return stream information based on the stream number +// + +InnerJoin::StreamInfo* InnerJoin::getStreamInfo(StreamType stream) +{ + for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) + { + if (innerStreams[i]->stream == stream) + return innerStreams[i]; + } + + // We should never come here + fb_assert(false); + return nullptr; +} + +#ifdef OPT_DEBUG +// Dump finally selected stream order +void InnerJoin::printBestOrder() const +{ + optimizer->printf(" best order, streams: "); + auto iter = joinedStreams.begin(); + const auto end = iter + bestCount; + for (; iter < end; iter++) + { + optimizer->printf("%u", iter->bestStream); + if (iter != end - 1) + optimizer->printf(", "); + } + optimizer->printf("\n"); +} + +// Dump currently passed streams to a debug file +void InnerJoin::printFoundOrder(StreamType position, + double positionCost, + double positionCardinality, + double cost, + double cardinality) const +{ + optimizer->printf(" position %2.2u:", position); + optimizer->printf(" pos. cardinality (%10.2f), pos. cost (%10.2f)", positionCardinality, positionCost); + optimizer->printf(" cardinality (%10.2f), cost (%10.2f)", cardinality, cost); + optimizer->printf(", streams: "); + auto iter = joinedStreams.begin(); + const auto end = iter + position; + for (; iter < end; iter++) + { + optimizer->printf("%u", iter->number); + if (iter != end - 1) + optimizer->printf(", "); + } + optimizer->printf("\n"); +} + +// Dump the processlist to a debug file +void InnerJoin::printProcessList(const IndexedRelationships& processList, + StreamType stream) const +{ + optimizer->printf(" base stream %u, relationships: stream (cost)", stream); + const auto end = processList.end(); + for (auto iter = processList.begin(); iter != end; iter++) + { + optimizer->printf("%u (%1.2f)", iter->stream, iter->cost); + if (iter != end - 1) + optimizer->printf(", "); + } + optimizer->printf("\n"); +} + +// Dump finally selected stream order +void InnerJoin::printStartOrder() const +{ + optimizer->printf("Start join order, stream (baseCost): "); + const auto end = innerStreams.end(); + for (auto iter = innerStreams.begin(); iter != end; iter++) + { + const auto innerStream = *iter; + if (!innerStream->used) + { + optimizer->printf("%u (%1.2f)", innerStream->stream, innerStream->baseCost); + if (iter != end - 1) + optimizer->printf(", "); + } + } + optimizer->printf("\n"); +} +#endif diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 09e5b5ecaa..a548b47258 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -1,8 +1,4 @@ /* - * PROGRAM: JRD Access Method - * MODULE: opt.cpp - * DESCRIPTION: Optimizer / record selection expression compiler - * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy @@ -53,7 +49,6 @@ #include "../jrd/ods.h" #include "../jrd/btr.h" #include "../jrd/sort.h" -#include "../jrd/rse.h" #include "../jrd/ini.h" #include "../jrd/intl.h" #include "../jrd/Collation.h" @@ -67,11 +62,9 @@ #include "../jrd/err_proto.h" #include "../jrd/ext_proto.h" #include "../jrd/intl_proto.h" - #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../yvalve/gds_proto.h" #include "../jrd/DataTypeUtil.h" @@ -88,35 +81,75 @@ #include "../jrd/recsrc/Cursor.h" #include "../jrd/Mapping.h" #include "../jrd/DbCreators.h" - -#include "../jrd/Optimizer.h" #include "../dsql/BoolNodes.h" #include "../dsql/ExprNodes.h" #include "../dsql/StmtNodes.h" #include "../jrd/ConfigTable.h" +#include "../jrd/optimizer/Optimizer.h" + using namespace Jrd; using namespace Firebird; -#ifdef DEV_BUILD +#ifdef OPT_DEBUG_RETRIEVAL #define OPT_DEBUG #endif +#ifdef OPT_DEBUG +#define OPTIMIZER_DEBUG_FILE "opt_debug.out" +#endif + namespace { - class River; - typedef HalfStaticArray RiverList; - - inline void compose(MemoryPool& pool, BoolExprNode** node1, BoolExprNode* node2) + inline void SET_DEP_BIT(ULONG* array, const SLONG bit) { - if (node2) - *node1 = (*node1) ? FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and, *node1, node2) : node2; + array[bit / BITS_PER_LONG] |= (1L << (bit % BITS_PER_LONG)); } + inline bool TEST_DEP_BIT(const ULONG* array, const ULONG bit) + { + return (array[bit / BITS_PER_LONG] & (1L << (bit % BITS_PER_LONG))) != 0; + } + + const int CACHE_PAGES_PER_STREAM = 15; + + // enumeration of sort datatypes + + static const UCHAR sort_dtypes[] = + { + 0, // dtype_unknown + SKD_text, // dtype_text + SKD_cstring, // dtype_cstring + SKD_varying, // dtype_varying + 0, + 0, + 0, // dtype_packed + 0, // dtype_byte + SKD_short, // dtype_short + SKD_long, // dtype_long + SKD_quad, // dtype_quad + SKD_float, // dtype_real + SKD_double, // dtype_double + SKD_double, // dtype_d_float + SKD_sql_date, // dtype_sql_date + SKD_sql_time, // dtype_sql_time + SKD_timestamp, // dtype_timestamp + SKD_quad, // dtype_blob + 0, // dtype_array + SKD_int64, // dtype_int64 + SKD_text, // dtype_dbkey - use text sort for backward compatibility + SKD_bytes, // dtype_boolean + SKD_dec64, // dtype_dec64 + SKD_dec128, // dtype_dec128 + SKD_int128, // dtype_int128 + SKD_sql_time_tz, // dtype_sql_time_tz + SKD_timestamp_tz // dtype_timestamp_tz + }; + struct SortField { - SortField() : stream(INVALID_STREAM), id(0), desc(NULL) + SortField() : stream(INVALID_STREAM), id(0), desc(nullptr) {} SortField(StreamType _stream, ULONG _id, const dsc* _desc) @@ -128,132 +161,11 @@ namespace const dsc* desc; }; - class River - { - public: - River(CompilerScratch* csb, RecordSource* rsb, RecordSourceNode* node, const StreamList& streams) - : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) - { - if (node) - m_nodes.add(node); - - m_streams.assign(streams); - } - - River(CompilerScratch* csb, RecordSource* rsb, RiverList& rivers) - : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) - { - for (River** iter = rivers.begin(); iter < rivers.end(); iter++) - { - River* const sub_river = *iter; - - const size_t count = m_streams.getCount(); - const size_t delta = sub_river->m_streams.getCount(); - if (count + delta >= MAX_STREAMS) - ERR_post(Arg::Gds(isc_too_many_contexts)); - - m_nodes.join(sub_river->m_nodes); - m_streams.join(sub_river->m_streams); - } - } - - RecordSource* getRecordSource() const - { - return m_rsb; - } - - const StreamList& getStreams() const - { - return m_streams; - } - - void activate(CompilerScratch* csb) - { - for (const StreamType* iter = m_streams.begin(); iter < m_streams.end(); iter++) - csb->csb_rpt[*iter].activate(); - } - - void deactivate(CompilerScratch* csb) - { - for (const StreamType* iter = m_streams.begin(); iter < m_streams.end(); iter++) - csb->csb_rpt[*iter].deactivate(); - } - - bool isReferenced(const ExprNode* node) const - { - SortedStreamList nodeStreams; - node->collectStreams(nodeStreams); - - if (!nodeStreams.hasData()) - return false; - - for (const StreamType* iter = nodeStreams.begin(); iter != nodeStreams.end(); ++iter) - { - if (!m_streams.exist(*iter)) - return false; - } - - return true; - } - - bool isComputable(CompilerScratch* csb) const - { - for (RecordSourceNode* const* iter = m_nodes.begin(); iter < m_nodes.end(); iter++) - { - if (!(*iter)->computable(csb, INVALID_STREAM, false)) - return false; - } - - return true; - } - - RecordSource* applyLocalBoolean(OptimizerBlk* opt) - { - fb_assert(m_rsb); - - CompilerScratch* const csb = opt->opt_csb; - - StreamStateHolder stateHolder(csb); - stateHolder.deactivate(); - - activate(csb); - - BoolExprNode* boolean = NULL; - - const OptimizerBlk::opt_conjunct* const opt_end = - opt->opt_conjuncts.begin() + opt->opt_base_conjuncts; - - for (OptimizerBlk::opt_conjunct* tail = opt->opt_conjuncts.begin(); - tail < opt_end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - compose(csb->csb_pool, &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - - if (boolean) - m_rsb = FB_NEW_POOL(csb->csb_pool) FilteredStream(csb, m_rsb, boolean); - - return m_rsb; - } - - protected: - RecordSource* m_rsb; - HalfStaticArray m_nodes; - StreamList m_streams; - }; - class CrossJoin : public River { public: CrossJoin(CompilerScratch* csb, RiverList& rivers) - : River(csb, NULL, rivers) + : River(csb, nullptr, rivers) { // Save states of the underlying streams and restore them afterwards @@ -319,216 +231,406 @@ namespace } } }; -} // namespace -static bool augment_stack(ValueExprNode*, ValueExprNodeStack&); -static bool augment_stack(BoolExprNode*, BoolExprNodeStack&); -static void check_indices(const CompilerScratch::csb_repeat*); -static void check_sorts(CompilerScratch*, RseNode*); -static void class_mask(USHORT, ValueExprNode**, ULONG*); -static SLONG decompose(thread_db* tdbb, BoolExprNode* boolNode, BoolExprNodeStack& stack, - CompilerScratch* csb); -static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratch* csb, - USHORT base_count); -static void find_index_relationship_streams(thread_db* tdbb, OptimizerBlk* opt, - const StreamList& streams, StreamList& dependent_streams, StreamList& free_streams); -static void form_rivers(thread_db* tdbb, OptimizerBlk* opt, const StreamList& streams, - RiverList& river_list, SortNode** sort_clause, PlanNode* plan_clause); -static bool form_river(thread_db* tdbb, OptimizerBlk* opt, StreamType count, size_t stream_count, - StreamList& temp, RiverList& river_list, SortNode** sort_clause); -static void gen_join(thread_db* tdbb, OptimizerBlk* opt, const StreamList& streams, - RiverList& river_list, SortNode** sort_clause, PlanNode* plan_clause); -static RecordSource* gen_outer(thread_db* tdbb, OptimizerBlk* opt, RseNode* rse, - RiverList& river_list, SortNode** sort_clause); -static RecordSource* gen_residual_boolean(thread_db* tdbb, OptimizerBlk* opt, RecordSource* prior_rsb); -static RecordSource* gen_retrieval(thread_db* tdbb, OptimizerBlk* opt, StreamType stream, - SortNode** sort_ptr, bool outer_flag, bool inner_flag, BoolExprNode** return_boolean); -static bool gen_equi_join(thread_db*, OptimizerBlk*, RiverList&); -static double get_cardinality(thread_db*, jrd_rel*, const Format*); -static BoolExprNode* make_inference_node(CompilerScratch*, BoolExprNode*, ValueExprNode*, ValueExprNode*); -static bool map_equal(const ValueExprNode*, const ValueExprNode*, const MapNode*); -static void mark_indices(CompilerScratch::csb_repeat* csbTail, SSHORT relationId); -static bool node_equality(const ValueExprNode*, const ValueExprNode*); -static bool node_equality(const BoolExprNode*, const BoolExprNode*); -static ValueExprNode* optimize_like_similar(thread_db*, CompilerScratch*, ComparativeBoolNode*); -static USHORT river_count(USHORT count, ValueExprNode** eq_class); -static bool search_stack(const ValueExprNode*, const ValueExprNodeStack&); -static void set_direction(SortNode*, SortNode*); -static void set_position(const SortNode*, SortNode*, const MapNode*); -static void sort_indices_by_selectivity(CompilerScratch::csb_repeat* csbTail); - - -// macro definitions - -#ifdef OPT_DEBUG -//const int DEBUG_PUNT = 5; -//const int DEBUG_RELATIONSHIPS = 4; -//const int DEBUG_ALL = 3; -//const int DEBUG_CANDIDATE = 2; -//const int DEBUG_BEST = 1; -const int DEBUG_NONE = 0; - -FILE *opt_debug_file = 0; -static int opt_debug_flag = DEBUG_NONE; -#endif - -inline void SET_DEP_BIT(ULONG* array, const SLONG bit) -{ - array[bit / BITS_PER_LONG] |= (1L << (bit % BITS_PER_LONG)); -} - -/* -inline void CLEAR_DEP_BIT(ULONG* array, const SLONG bit) -{ - array[bit / BITS_PER_LONG] &= ~(1L << (bit % BITS_PER_LONG)); -} -*/ - -inline bool TEST_DEP_BIT(const ULONG* array, const ULONG bit) -{ - return (array[bit / BITS_PER_LONG] & (1L << (bit % BITS_PER_LONG))) != 0; -} - -/* -inline bool TEST_DEP_ARRAYS(const ULONG* ar1, const ULONG* ar2) -{ - //return (ar1[0] & ar2[0]) || (ar1[1] & ar2[1]) || (ar1[2] & ar2[2]) || (ar1[3] & ar2[3]) || - // (ar1[4] & ar2[4]) || (ar1[5] & ar2[5]) || (ar1[6] & ar2[6]) || (ar1[7] & ar2[7]); - for (SLONG i = 0; i < BITS_PER_LONG; i++) + inline void compose(MemoryPool& pool, BoolExprNode** node1, BoolExprNode* node2) { - if (ar1[i] & ar2[i]) - return true; + if (node2) + *node1 = (*node1) ? FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and, *node1, node2) : node2; } - return false; -} -*/ -const int CACHE_PAGES_PER_STREAM = 15; - -// enumeration of sort datatypes - -static const UCHAR sort_dtypes[] = -{ - 0, // dtype_unknown - SKD_text, // dtype_text - SKD_cstring, // dtype_cstring - SKD_varying, // dtype_varying - 0, - 0, - 0, // dtype_packed - 0, // dtype_byte - SKD_short, // dtype_short - SKD_long, // dtype_long - SKD_quad, // dtype_quad - SKD_float, // dtype_real - SKD_double, // dtype_double - SKD_double, // dtype_d_float - SKD_sql_date, // dtype_sql_date - SKD_sql_time, // dtype_sql_time - SKD_timestamp, // dtype_timestamp - SKD_quad, // dtype_blob - 0, // dtype_array - SKD_int64, // dtype_int64 - SKD_text, // dtype_dbkey - use text sort for backward compatibility - SKD_bytes, // dtype_boolean - SKD_dec64, // dtype_dec64 - SKD_dec128, // dtype_dec128 - SKD_int128, // dtype_int128 - SKD_sql_time_tz, // dtype_sql_time_tz - SKD_timestamp_tz // dtype_timestamp_tz -}; - - -string OPT_get_plan(thread_db* tdbb, const JrdStatement* statement, bool detailed) -{ -/************************************** - * - * O P T _ g e t _ p l a n - * - ************************************** - * - * Functional description - * Returns a formatted textual plan for all RseNode's in the specified request. - * - **************************************/ - string plan; - - if (statement) + void classMask(unsigned count, ValueExprNode** eq_class, ULONG* mask) { - for (const auto& recordSource : statement->fors) + // Given an sort/merge join equivalence class (vector of node pointers + // of representative values for rivers), return a bit mask of rivers with values + + if (count > MAX_CONJUNCTS) { - plan += detailed ? "\nSelect Expression" : "\nPLAN "; - recordSource->print(tdbb, plan, detailed, 0); + ERR_post(Arg::Gds(isc_optimizer_blk_exc)); + // Msg442: size of optimizer block exceeded + } + + for (unsigned i = 0; i < OPT_STREAM_BITS; i++) + mask[i] = 0; + + for (unsigned i = 0; i < count; i++, eq_class++) + { + if (*eq_class) + { + SET_DEP_BIT(mask, i); + DEV_BLKCHK(*eq_class, type_nod); + } } } - return plan; + + unsigned getRiverCount(unsigned count, const ValueExprNode* const* eq_class) + { + // Given an sort/merge join equivalence class (vector of node pointers + // of representative values for rivers), return the count of rivers with values + + unsigned cnt = 0; + + for (unsigned i = 0; i < count; i++) + { + if (*eq_class++) + cnt++; + } + + return cnt; + } + + bool fieldEqual(const ValueExprNode* node1, const ValueExprNode* node2) + { + if (!node1 || !node2) + return false; + + if (node1->getType() != node2->getType()) + return false; + + if (node1 == node2) + return true; + + const FieldNode* fieldNode1 = nodeAs(node1); + const FieldNode* fieldNode2 = nodeAs(node2); + + if (fieldNode1 && fieldNode2) + { + return fieldNode1->fieldStream == fieldNode2->fieldStream && + fieldNode1->fieldId == fieldNode2->fieldId; + } + + return false; + } + + bool fieldEqual(const BoolExprNode* node1, const BoolExprNode* node2) + { + if (!node1 || !node2) + return false; + + if (node1->getType() != node2->getType()) + return false; + + if (node1 == node2) + return true; + + const auto cmpNode = nodeAs(node1); + const auto cmpNode2 = nodeAs(node2); + + if (cmpNode && cmpNode2 && cmpNode->blrOp == cmpNode2->blrOp && + (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) + { + if (fieldEqual(cmpNode->arg1, cmpNode2->arg1) && + fieldEqual(cmpNode->arg2, cmpNode2->arg2)) + { + return true; + } + + if (fieldEqual(cmpNode->arg1, cmpNode2->arg2) && + fieldEqual(cmpNode->arg2, cmpNode2->arg1)) + { + return true; + } + } + + return false; + } + + + bool augmentStack(ValueExprNode* node, ValueExprNodeStack& stack) + { + for (ValueExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) + { + if (fieldEqual(node, temp.object())) + return false; + } + + stack.push(node); + return true; + } + + bool augmentStack(BoolExprNode* node, BoolExprNodeStack& stack) + { + for (BoolExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) + { + if (fieldEqual(node, temp.object())) + return false; + } + + stack.push(node); + return true; + } + + bool searchStack(const ValueExprNode* node, const ValueExprNodeStack& stack) + { + for (ValueExprNodeStack::const_iterator iter(stack); iter.hasData(); ++iter) + { + if (fieldEqual(node, iter.object())) + return true; + } + + return false; + } + + double getCardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) + { + // Return the estimated cardinality for the given relation + + if (relation->isVirtual()) + return 100.0; // Just a dumb estimation + + if (relation->rel_file) + return EXT_cardinality(tdbb, relation); + + MET_post_existence(tdbb, relation); + const double cardinality = DPM_cardinality(tdbb, relation, format); + MET_release_existence(tdbb, relation); + + return cardinality; + } + + void markIndices(CompilerScratch::csb_repeat* tail, USHORT relationId) + { + // Mark indices that were not included in the user-specified access plan + + const auto plan = tail->csb_plan; + fb_assert(plan); + + if (plan->type != PlanNode::TYPE_RETRIEVE) + return; + + // Go through each of the indices and mark it unusable + // for indexed retrieval unless it was specifically mentioned + // in the plan; also mark indices for navigational access. + + // If there were none indices, this is a sequential retrieval. + + const auto relation = tail->csb_relation; + fb_assert(relation); + + if (!tail->csb_idx) + return; + + for (auto& idx : *tail->csb_idx) + { + if (!plan->accessType) + { + idx.idx_runtime_flags |= idx_plan_dont_use; + continue; + } + + bool first = true, found = false; + for (const auto& arg : plan->accessType->items) + { + if (relationId != arg.relationId) + { + // index %s cannot be used in the specified plan + ERR_post(Arg::Gds(isc_index_unused) << arg.indexName); + } + + if (idx.idx_id == arg.indexId) + { + if (plan->accessType->type == PlanNode::AccessType::TYPE_NAVIGATIONAL && first) + { + // dimitr: navigational access can use only one index, + // hence the extra check added (see the line above) + idx.idx_runtime_flags |= idx_plan_navigate; + } + else + { + // nod_indices + found = true; + break; + } + } + + first = false; + } + + if (!found) + idx.idx_runtime_flags |= idx_plan_dont_use; + } + } + + bool mapEqual(const ValueExprNode* field1, const ValueExprNode* field2, const MapNode* map) + { + // Test to see if two fields are equal, where the fields are in two different streams + // possibly mapped to each other. Order of the input fields is important. + const auto fieldNode1 = nodeAs(field1); + const auto fieldNode2 = nodeAs(field2); + + if (!fieldNode1 || !fieldNode2) + return false; + + // look through the mapping and see if we can find an equivalence. + auto sourcePtr = map->sourceList.begin(); + auto targetPtr = map->targetList.begin(); + + for (const auto sourceEnd = map->sourceList.end(); + sourcePtr != sourceEnd; + ++sourcePtr, ++targetPtr) + { + const auto mapFrom = nodeAs(*sourcePtr); + const auto mapTo = nodeAs(*targetPtr); + + if (!mapFrom || !mapTo) + continue; + + if (fieldNode1->fieldStream != mapFrom->fieldStream || + fieldNode1->fieldId != mapFrom->fieldId) + { + continue; + } + + if (fieldNode2->fieldStream != mapTo->fieldStream || + fieldNode2->fieldId != mapTo->fieldId) + { + continue; + } + + return true; + } + + return false; + } + + void setDirection(SortNode* fromClause, SortNode* toClause) + { + // Update the direction of a GROUP BY, DISTINCT, or ORDER BY + // clause to the same direction as another clause. Do the same + // for the nulls placement flag. + + const auto fromCount = fromClause->expressions.getCount(); + + fb_assert(fromCount <= toClause->expressions.getCount()); + fb_assert(fromCount == fromClause->direction.getCount() && + fromCount == fromClause->nullOrder.getCount()); + fb_assert(toClause->expressions.getCount() == toClause->direction.getCount() && + toClause->expressions.getCount() == toClause->nullOrder.getCount()); + + for (FB_SIZE_T i = 0; i < fromCount; ++i) + { + toClause->direction[i] = fromClause->direction[i]; + toClause->nullOrder[i] = fromClause->nullOrder[i]; + } + } + + void setPosition(const SortNode* from_clause, SortNode* to_clause, const MapNode* map) + { + // Update the fields in a GROUP BY, DISTINCT, or ORDER BY clause to the same position + // as another clause, possibly using a mapping between the streams. + + // Track the position in the from list with "to_swap", and find the corresponding + // field in the from list with "to_ptr", then swap the two fields. By the time + // we get to the end of the from list, all fields in the to list will be reordered. + + auto to_swap = to_clause->expressions.begin(); + + // We need to process no more than the number of nodes in the "from" clause + + const auto count = from_clause->expressions.getCount(); + fb_assert(count <= to_clause->expressions.getCount()); + + auto from_ptr = from_clause->expressions.begin(); + for (const auto from_end = from_ptr + count; from_ptr != from_end; ++from_ptr) + { + NestConst* to_ptr = to_clause->expressions.begin(); + for (const auto to_end = to_ptr + count; to_ptr != to_end; ++to_ptr) + { + const auto fromField = nodeAs(*from_ptr); + const auto toField = nodeAs(*to_ptr); + + if ((map && mapEqual(*to_ptr, *from_ptr, map)) || + (!map && fromField && toField && + fromField->fieldStream == toField->fieldStream && + fromField->fieldId == toField->fieldId)) + { + ValueExprNode* swap = *to_swap; + *to_swap = *to_ptr; + *to_ptr = swap; + } + } + + ++to_swap; + } + } + +} // namespace + + +// +// Constructor +// + +Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), csb(aCsb), rse(aRse), + compileStreams(getPool()), + bedStreams(getPool()), + keyStreams(getPool()), + subStreams(getPool()), + outerStreams(getPool()), + conjuncts(getPool()) +{ } -// Compile and optimize a record selection expression into a set of record source blocks (rsb's). -RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, - BoolExprNodeStack* parent_stack) +// +// Destructor +// + +Optimizer::~Optimizer() { - DEV_BLKCHK(csb, type_csb); - DEV_BLKCHK(rse, type_nod); + // Release memory allocated for index descriptions + for (const auto compileStream : compileStreams) + { + delete csb->csb_rpt[compileStream].csb_idx; + csb->csb_rpt[compileStream].csb_idx = nullptr; + } - SET_TDBB(tdbb); + if (debugFile) + fclose(debugFile); +} -#ifdef OPT_DEBUG - if (opt_debug_flag != DEBUG_NONE && !opt_debug_file) - opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "w"); -#endif +// +// Compile and optimize a record selection expression into a set of record source blocks +// + +RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) +{ // If there is a boolean, there is some work to be done. First, // decompose the boolean into conjunctions. Then get descriptions // of all indices for all relations in the RseNode. This will give // us the info necessary to allocate a optimizer block big // enough to hold this crud. - // Do not allocate the index_desc struct. Let BTR_all do the job. The allocated - // memory will then be in csb->csb_rpt[stream].csb_idx_allocation, which - // gets cleaned up before this function exits. + RecordSource* rsb = nullptr; - MemoryPool* const pool = tdbb->getDefaultPool(); - - AutoPtr opt(FB_NEW_POOL(*pool) OptimizerBlk(pool, rse)); - opt->opt_streams.grow(csb->csb_n_stream); - opt->favorFirstRows = (rse->flags & RseNode::FLAG_OPT_FIRST_ROWS) != 0; - - RecordSource* rsb = NULL; - - try { - - opt->opt_csb = csb; - - RiverList rivers; - - check_sorts(csb, rse); + checkSorts(); SortNode* sort = rse->rse_sorted; SortNode* project = rse->rse_projection; SortNode* aggregate = rse->rse_aggregate; BoolExprNodeStack conjunct_stack; - SLONG conjunct_count = 0; + unsigned conjunct_count = 0; // put any additional booleans on the conjunct stack, and see if we // can generate additional booleans by associativity--this will help // to utilize indices that we might not have noticed if (rse->rse_boolean) - conjunct_count = decompose(tdbb, rse->rse_boolean, conjunct_stack, csb); + conjunct_count = decompose(rse->rse_boolean, conjunct_stack); - conjunct_count += distribute_equalities(conjunct_stack, csb, conjunct_count); + conjunct_count += distributeEqualities(conjunct_stack, conjunct_count); // AB: If we have limit our retrieval with FIRST / SKIP syntax then // we may not deliver above conditions (from higher rse's) to this // rse, because the results should be consistent. if (rse->rse_skip || rse->rse_first) - parent_stack = NULL; + parentStack = nullptr; // Set base-point before the parent/distributed nodes begin. - const USHORT base_count = (USHORT) conjunct_count; - opt->opt_base_conjuncts = base_count; + const unsigned base_count = conjunct_count; + baseConjuncts = base_count; // AB: Add parent conjunctions to conjunct_stack, keep in mind // the outer-streams! For outer streams put missing (IS NULL) @@ -544,21 +646,21 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // allowed = booleans that can never evaluate to NULL/Unknown or turn // NULL/Unknown into a True or False. - USHORT parent_count = 0, distributed_count = 0; + unsigned parent_count = 0, distributed_count = 0; BoolExprNodeStack missing_stack; - if (parent_stack) + if (parentStack) { - for (BoolExprNodeStack::iterator iter(*parent_stack); + for (BoolExprNodeStack::iterator iter(*parentStack); iter.hasData() && conjunct_count < MAX_CONJUNCTS; ++iter) { BoolExprNode* const node = iter.object(); - if (rse->rse_jointype != blr_inner && node->possiblyUnknown()) + if (!isInnerJoin() && node->possiblyUnknown()) { // parent missing conjunctions shouldn't be // distributed to FULL OUTER JOIN streams at all - if (rse->rse_jointype != blr_full) + if (!isFullJoin()) missing_stack.push(node); } else @@ -570,16 +672,16 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, } // We've now merged parent, try again to make more conjunctions. - distributed_count = distribute_equalities(conjunct_stack, csb, conjunct_count); + distributed_count = distributeEqualities(conjunct_stack, conjunct_count); conjunct_count += distributed_count; } // The newly created conjunctions belong to the base conjunctions. // After them are starting the parent conjunctions. - opt->opt_base_parent_conjuncts = opt->opt_base_conjuncts + distributed_count; + baseParentConjuncts = baseConjuncts + distributed_count; // Set base-point before the parent IS NULL nodes begin - opt->opt_base_missing_conjuncts = (USHORT) conjunct_count; + baseMissingConjuncts = conjunct_count; // Check if size of optimizer block exceeded. if (conjunct_count > MAX_CONJUNCTS) @@ -591,10 +693,10 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // Put conjunctions in opt structure. // Note that it's a stack and we get the nodes in reversed order from the stack. - opt->opt_conjuncts.grow(conjunct_count); - SSHORT nodeBase = -1, j = -1; + conjuncts.grow(conjunct_count); + int nodeBase = -1, j = -1; - for (SLONG i = conjunct_count; i > 0; i--, j--) + for (unsigned i = conjunct_count; i > 0; i--, j--) { BoolExprNode* const node = conjunct_stack.pop(); @@ -608,17 +710,17 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, { // The parent conjunctions j = parent_count - 1; - nodeBase = opt->opt_base_parent_conjuncts; + nodeBase = baseParentConjuncts; } else if (i == conjunct_count) { // The new conjunctions created by "distribution" from the stack j = distributed_count - 1; - nodeBase = opt->opt_base_conjuncts; + nodeBase = baseConjuncts; } - fb_assert(nodeBase >= 0 && j >= 0 && nodeBase + j < MAX_CONJUNCTS); - opt->opt_conjuncts[nodeBase + j].opt_conjunct_node = node; + fb_assert(nodeBase >= 0 && j >= 0); + conjuncts[nodeBase + j].node = node; } // Put the parent missing nodes on the stack @@ -627,83 +729,88 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, { BoolExprNode* const node = iter.object(); - opt->opt_conjuncts.grow(conjunct_count + 1); - opt->opt_conjuncts[conjunct_count].opt_conjunct_node = node; + conjuncts.grow(conjunct_count + 1); + conjuncts[conjunct_count].node = node; conjunct_count++; } - // clear the csb_active flag of all streams in the RseNode + // Clear the csb_active flag of all streams in the RseNode StreamList rseStreams; rse->computeRseStreams(rseStreams); for (StreamList::iterator i = rseStreams.begin(); i != rseStreams.end(); ++i) csb->csb_rpt[*i].deactivate(); - // go through the record selection expression generating + // Go through the record selection expression generating // record source blocks for all streams - NestConst* ptr = rse->rse_relations.begin(); - for (NestConst* const end = rse->rse_relations.end(); ptr != end; ++ptr) + RiverList rivers; + + bool innerSubStream = false; + for (auto node : rse->rse_relations) { - const bool innerSubStream = (ptr != rse->rse_relations.begin()); - RecordSourceNode* const node = *ptr; - - opt->localStreams.clear(); - fb_assert(sort == rse->rse_sorted); fb_assert(aggregate == rse->rse_aggregate); - // find the stream number and place it at the end of the beds array + // Find the stream number and place it at the end of the bedStreams array // (if this is really a stream and not another RseNode) - rsb = node->compile(tdbb, opt, innerSubStream); + node->computeRseStreams(bedStreams); + node->computeDbKeyStreams(keyStreams); - // if an rsb has been generated, we have a non-relation; + // Compile the node + rsb = node->compile(tdbb, this, innerSubStream); + + // If an rsb has been generated, we have a non-relation; // so it forms a river of its own since it is separately // optimized from the streams in this rsb if (rsb) { + StreamList localStreams; + rsb->findUsedStreams(localStreams); + // AB: Save all outer-part streams - if (rse->rse_jointype == blr_inner || - (rse->rse_jointype == blr_left && !innerSubStream)) + if (isInnerJoin() || (isLeftJoin() && !innerSubStream)) { - rsb->findUsedStreams(opt->subStreams); - rsb->findUsedStreams(opt->outerStreams); + subStreams.join(localStreams); + outerStreams.join(localStreams); } - const auto river = FB_NEW_POOL(*pool) River(csb, rsb, node, opt->localStreams); + const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, node, localStreams); river->deactivate(csb); rivers.add(river); } else { // We have a relation, just add its stream - fb_assert(opt->beds.hasData()); - opt->outerStreams.add(opt->beds.back()); + fb_assert(bedStreams.hasData()); + outerStreams.add(bedStreams.back()); } + + innerSubStream = true; } - // this is an attempt to make sure we have a large enough cache to + // This is an attempt to make sure we have a large enough cache to // efficiently retrieve this query; make sure the cache has a minimum // number of pages for each stream in the RseNode (the number is just a guess) - if (opt->compileStreams.getCount() > 5) - CCH_expand(tdbb, (ULONG) (opt->compileStreams.getCount() * CACHE_PAGES_PER_STREAM)); + if (compileStreams.getCount() > 5) + CCH_expand(tdbb, (ULONG) (compileStreams.getCount() * CACHE_PAGES_PER_STREAM)); // At this point we are ready to start optimizing. // We will use the opt block to hold information of // a global nature, meaning that it needs to stick // around for the rest of the optimization process. - // attempt to optimize aggregates via an index, if possible + // Attempt to optimize aggregates via an index, if possible if (aggregate && !sort) sort = aggregate; else - rse->rse_aggregate = aggregate = NULL; + rse->rse_aggregate = aggregate = nullptr; // AB: Mark the previous used streams (sub-RseNode's) as active - for (StreamList::iterator i = opt->subStreams.begin(); i != opt->subStreams.end(); ++i) - csb->csb_rpt[*i].activate(); + for (const auto subStream : subStreams) + csb->csb_rpt[subStream].activate(); bool sortCanBeUsed = true; SortNode* const orgSortNode = sort; @@ -716,13 +823,13 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // ORDER BY does not use an index. if (sort && project) { - sort = NULL; + sort = nullptr; sortCanBeUsed = false; } - // outer joins require some extra processing - if (rse->rse_jointype != blr_inner) - rsb = gen_outer(tdbb, opt, rse, rivers, &sort); + // Outer joins are processed their own way + if (!isInnerJoin()) + rsb = generateOuterJoin(rivers, &sort); else { // AB: If previous rsb's are already on the stack we can't use @@ -730,21 +837,18 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // streams are JOINed to the previous ones if (rivers.hasData()) { - sort = NULL; + sort = nullptr; sortCanBeUsed = false; // AB: We could already have multiple rivers at this // point so try to do some hashing or sort/merging now. - while (gen_equi_join(tdbb, opt, rivers)) + while (generateEquiJoin(rivers)) ; - - // AB: Mark the previous used streams (sub-RseNode's) again - // as active, because a SORT/MERGE could reset the flags - for (StreamList::iterator i = opt->subStreams.begin(); i != opt->subStreams.end(); ++i) - csb->csb_rpt[*i].activate(); } - fb_assert(opt->compileStreams.getCount() != 1 || csb->csb_rpt[opt->compileStreams[0]].csb_relation != 0); + StreamList joinStreams(compileStreams); + + fb_assert(joinStreams.getCount() != 1 || csb->csb_rpt[joinStreams[0]].csb_relation); while (true) { @@ -754,38 +858,38 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // currently active rivers. Where in the new cross river // a stream depends (index) on the active rivers. StreamList dependent_streams, free_streams; - find_index_relationship_streams(tdbb, opt, opt->compileStreams, dependent_streams, free_streams); + findDependentStreams(joinStreams, dependent_streams, free_streams); // If we have dependent and free streams then we can't rely on - // the sort node to be used for index navigation. - if (dependent_streams.getCount() && free_streams.getCount()) + // the sort node to be used for index navigation + if (dependent_streams.hasData() && free_streams.hasData()) { - sort = NULL; + sort = nullptr; sortCanBeUsed = false; } - if (dependent_streams.getCount()) + if (dependent_streams.hasData()) { - // copy free streams - opt->compileStreams.assign(free_streams); + // Copy free streams + joinStreams.assign(free_streams); // Make rivers from the dependent streams - gen_join(tdbb, opt, dependent_streams, rivers, &sort, rse->rse_plan); + generateInnerJoin(dependent_streams, rivers, &sort, rse->rse_plan); // Generate one river which holds a cross join rsb between // all currently available rivers - rivers.add(FB_NEW_POOL(*pool) CrossJoin(csb, rivers)); + rivers.add(FB_NEW_POOL(getPool()) CrossJoin(csb, rivers)); rivers.back()->activate(csb); } else { - if (free_streams.getCount()) + if (free_streams.hasData()) { // Deactivate streams from rivers on stack, because // the remaining streams don't have any indexed relationship with them - for (River** iter = rivers.begin(); iter < rivers.end(); iter++) - (*iter)->deactivate(csb); + for (const auto river : rivers) + river->deactivate(csb); } break; @@ -793,63 +897,52 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, } // attempt to form joins in decreasing order of desirability - gen_join(tdbb, opt, opt->compileStreams, rivers, &sort, rse->rse_plan); + generateInnerJoin(joinStreams, rivers, &sort, rse->rse_plan); // If there are multiple rivers, try some hashing or sort/merging - while (gen_equi_join(tdbb, opt, rivers)) + while (generateEquiJoin(rivers)) ; rsb = CrossJoin(csb, rivers).getRecordSource(); // Pick up any residual boolean that may have fallen thru the cracks - rsb = gen_residual_boolean(tdbb, opt, rsb); + rsb = generateResidualBoolean(rsb); } // Assign the sort node back if it wasn't used by the index navigation if (orgSortNode && !sortCanBeUsed) sort = orgSortNode; - // if the aggregate was not optimized via an index, get rid of the + // If the aggregate was not optimized via an index, get rid of the // sort and flag the fact to the calling routine if (aggregate && sort) { - rse->rse_aggregate = NULL; - sort = NULL; + rse->rse_aggregate = nullptr; + sort = nullptr; } - // check index usage in all the base streams to ensure + // Check index usage in all the base streams to ensure // that any user-specified access plan is followed - for (StreamType i = 0; i < opt->compileStreams.getCount(); i++) - check_indices(&csb->csb_rpt[opt->compileStreams[i]]); + checkIndices(); if (project || sort) { - // CVC: I'm not sure how to do this with Array in a clearer way. - // Please, once you agree with my changes or fix them, you can delete the comments. // Eliminate any duplicate dbkey streams - const StreamType* const b_end = opt->beds.end(); - const StreamType* const k_end = opt->keyStreams.end(); - StreamType* k = opt->keyStreams.begin(); - for (const StreamType* p2 = k; p2 < k_end; ++p2) + for (const auto stream: bedStreams) { - const StreamType* q = opt->beds.begin(); - - while (q < b_end && *q != *p2) - q++; - - if (q >= b_end) - *k++ = *p2; + FB_SIZE_T pos; + if (keyStreams.find(stream, pos)) + keyStreams.remove(pos); } - opt->keyStreams.shrink(k - opt->keyStreams.begin()); // Handle project clause, if present if (project) - rsb = OPT_gen_sort(tdbb, opt->opt_csb, opt->beds, &opt->keyStreams, rsb, project, opt->favorFirstRows, true); + rsb = generateSort(bedStreams, &keyStreams, rsb, project, favorFirstRows(), true); // Handle sort clause if present if (sort) - rsb = OPT_gen_sort(tdbb, opt->opt_csb, opt->beds, &opt->keyStreams, rsb, sort, opt->favorFirstRows, false); + rsb = generateSort(bedStreams, &keyStreams, rsb, sort, favorFirstRows(), false); } // Handle first and/or skip. The skip MUST (if present) @@ -858,1074 +951,93 @@ RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // gen_skip before gen_first. if (rse->rse_skip) - rsb = FB_NEW_POOL(*pool) SkipRowsStream(csb, rsb, rse->rse_skip); + rsb = FB_NEW_POOL(getPool()) SkipRowsStream(csb, rsb, rse->rse_skip); if (rse->rse_first) - rsb = FB_NEW_POOL(*pool) FirstRowsStream(csb, rsb, rse->rse_first); + rsb = FB_NEW_POOL(getPool()) FirstRowsStream(csb, rsb, rse->rse_first); + + if (rse->flags & RseNode::FLAG_SINGULAR) + rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) SingularStream(csb, rsb); if (rse->flags & RseNode::FLAG_WRITELOCK) { - for (StreamType i = 0; i < opt->compileStreams.getCount(); ++i) + for (const auto compileStream : compileStreams) { - const StreamType loopStream = opt->compileStreams[i]; - CompilerScratch::csb_repeat* r = &csb->csb_rpt[loopStream]; - r->csb_flags |= csb_update; + const auto tail = &csb->csb_rpt[compileStream]; + tail->csb_flags |= csb_update; - if (r->csb_relation) - { - CMP_post_access(tdbb, csb, r->csb_relation->rel_security_name, - r->csb_view ? r->csb_view->rel_id : 0, - SCL_update, SCL_object_table, r->csb_relation->rel_name); - } - } - } + fb_assert(tail->csb_relation); - // release memory allocated for index descriptions - for (StreamType i = 0; i < opt->compileStreams.getCount(); ++i) - { - const StreamType loopStream = opt->compileStreams[i]; - delete csb->csb_rpt[loopStream].csb_idx; - csb->csb_rpt[loopStream].csb_idx = NULL; - - // CVC: The following line added because OPT_compile is recursive, both directly - // and through gen_union(), too. Otherwise, we happen to step on deallocated memory - // and this is the cause of the crashes with indices that have plagued IB since v4. - - csb->csb_rpt[loopStream].csb_indices = 0; - } - -#ifdef OPT_DEBUG - if (opt_debug_file) - { - fflush(opt_debug_file); - //fclose(opt_debug_file); - //opt_debug_file = 0; - } -#endif - - } // try - catch (const Exception&) - { - for (StreamType i = 0; i < opt->compileStreams.getCount(); ++i) - { - const StreamType loopStream = opt->compileStreams[i]; - delete csb->csb_rpt[loopStream].csb_idx; - csb->csb_rpt[loopStream].csb_idx = NULL; - csb->csb_rpt[loopStream].csb_indices = 0; // Probably needed to be safe + CMP_post_access(tdbb, csb, tail->csb_relation->rel_security_name, + tail->csb_view ? tail->csb_view->rel_id : 0, + SCL_update, SCL_object_table, tail->csb_relation->rel_name); } - throw; + rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) LockedStream(csb, rsb); } + if (rse->flags & RseNode::FLAG_SCROLLABLE) + rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) BufferedStream(csb, rsb); + return rsb; } -// Prepare relation and its indices for optimization. -void OPT_compile_relation(thread_db* tdbb, jrd_rel* relation, CompilerScratch* csb, - StreamType stream, bool needIndices) +// +// Prepare relation and its indices for optimization +// + +void Optimizer::compileRelation(StreamType stream) { - CompilerScratch::csb_repeat* const tail = &csb->csb_rpt[stream]; - RelationPages* const relPages = relation->getPages(tdbb); + // We have found a base relation; record its stream number in the streams array + // as a candidate for merging into a river + + compileStreams.add(stream); + + // If we have any booleans or sort fields, we may be able to + // use an index to optimize them; retrieve the current format of + // all indices at this time so we can determine if it's possible + + const bool needIndices = conjuncts.hasData() || (rse->rse_sorted || rse->rse_aggregate); + + const auto tail = &csb->csb_rpt[stream]; + + const auto relation = tail->csb_relation; + fb_assert(relation); + + tail->csb_idx = nullptr; if (needIndices && !relation->rel_file && !relation->isVirtual()) { - tail->csb_indices = BTR_all(tdbb, relation, &tail->csb_idx, relPages); + const auto relPages = relation->getPages(tdbb); + IndexDescList idxList; + BTR_all(tdbb, relation, idxList, relPages); + + if (idxList.hasData()) + tail->csb_idx = FB_NEW_POOL(getPool()) IndexDescList(getPool(), idxList); if (tail->csb_plan) - mark_indices(tail, relation->rel_id); - else - sort_indices_by_selectivity(tail); - } - else - tail->csb_indices = 0; - - tail->csb_cardinality = - get_cardinality(tdbb, relation, CMP_format(tdbb, csb, stream)); -} - -// Add node (ValueExprNode) to stack unless node is already on stack. -static bool augment_stack(ValueExprNode* node, ValueExprNodeStack& stack) -{ -/************************************** - * - * a u g m e n t _ s t a c k - * - ************************************** - * - * Functional description - * - **************************************/ - - for (ValueExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) - { - if (node_equality(node, temp.object())) - return false; + markIndices(tail, relation->rel_id); } - stack.push(node); - - return true; -} - -// Add node (BoolExprNode) to stack unless node is already on stack. -static bool augment_stack(BoolExprNode* node, BoolExprNodeStack& stack) -{ - for (BoolExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) - { - if (node_equality(node, temp.object())) - return false; - } - - stack.push(node); - - return true; -} - - -static void check_indices(const CompilerScratch::csb_repeat* csb_tail) -{ -/************************************** - * - * c h e c k _ i n d i c e s - * - ************************************** - * - * Functional description - * Check to make sure that the user-specified - * indices were actually utilized by the optimizer. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - - const PlanNode* plan = csb_tail->csb_plan; - if (!plan) - return; - - if (plan->type != PlanNode::TYPE_RETRIEVE) - return; - - const jrd_rel* relation = csb_tail->csb_relation; - - // if there were no indices fetched at all but the - // user specified some, error out using the first index specified - - if (!csb_tail->csb_indices && plan->accessType && !tdbb->getAttachment()->isGbak()) - { - // index %s cannot be used in the specified plan - ERR_post(Arg::Gds(isc_index_unused) << plan->accessType->items[0].indexName); - } - - // check to make sure that all indices are either used or marked not to be used, - // and that there are no unused navigational indices - MetaName index_name; - - const index_desc* idx = csb_tail->csb_idx->items; - - for (USHORT i = 0; i < csb_tail->csb_indices; i++) - { - if (!(idx->idx_runtime_flags & (idx_plan_dont_use | idx_used)) || - ((idx->idx_runtime_flags & idx_plan_navigate) && !(idx->idx_runtime_flags & idx_navigate))) - { - if (relation) - MET_lookup_index(tdbb, index_name, relation->rel_name, (USHORT) (idx->idx_id + 1)); - else - index_name = ""; - - // index %s cannot be used in the specified plan - ERR_post(Arg::Gds(isc_index_unused) << Arg::Str(index_name)); - } - - ++idx; - } -} - - -static void check_sorts(CompilerScratch* csb, RseNode* rse) -{ -/************************************** - * - * c h e c k _ s o r t s - * - ************************************** - * - * Functional description - * Try to optimize out unnecessary sorting. - * - **************************************/ - DEV_BLKCHK(rse, type_nod); - - SortNode* sort = rse->rse_sorted; - SortNode* project = rse->rse_projection; - - // check if a GROUP BY exists using the same fields as the project or sort: - // if so, the projection can be eliminated; if no projection exists, then - // the sort can be eliminated. - - SortNode* group; - RecordSourceNode* sub_rse; - - if ((project || sort) && rse->rse_relations.getCount() == 1 && (sub_rse = rse->rse_relations[0]) && - nodeIs(sub_rse) && - (group = static_cast(sub_rse)->group)) - { - MapNode* const map = static_cast(sub_rse)->map; - - // if all the fields of the project are the same as all the fields - // of the group by, get rid of the project. - - if (project && (project->expressions.getCount() == group->expressions.getCount())) - { - NestConst* project_ptr = project->expressions.begin(); - const NestConst* const project_end = project->expressions.end(); - - for (; project_ptr != project_end; ++project_ptr) - { - const NestConst* group_ptr = group->expressions.begin(); - const NestConst* const group_end = group->expressions.end(); - - for (; group_ptr != group_end; ++group_ptr) - { - if (map_equal(*group_ptr, *project_ptr, map)) - break; - } - - if (group_ptr == group_end) - break; - } - - // we can now ignore the project, but in case the project is being done - // in descending order because of an order by, do the group by the same way. - if (project_ptr == project_end) - { - set_direction(project, group); - project = rse->rse_projection = NULL; - } - } - - // if there is no projection, then we can make a similar optimization - // for sort, except that sort may have fewer fields than group by. - - if (!project && sort && (sort->expressions.getCount() <= group->expressions.getCount())) - { - const size_t count = sort->expressions.getCount(); - const NestConst* sort_ptr = sort->expressions.begin(); - const NestConst* const sort_end = sort_ptr + count; - - for (; sort_ptr != sort_end; ++sort_ptr) - { - const NestConst* group_ptr = group->expressions.begin(); - const NestConst* const group_end = group_ptr + count; - - for (; group_ptr != group_end; ++group_ptr) - { - if (map_equal(*group_ptr, *sort_ptr, map)) - break; - } - - if (group_ptr == group_end) - break; - } - - // if all the fields in the sort list match the first n fields in the - // project list, we can ignore the sort, but update the sort order - // (ascending/descending) to match that in the sort list - - if (sort_ptr == sort_end) - { - set_direction(sort, group); - set_position(sort, group, static_cast(sub_rse)->map); - sort = rse->rse_sorted = NULL; - } - } - - } - - // examine the ORDER BY and DISTINCT clauses; if all the fields in the - // ORDER BY match the first n fields in the DISTINCT in any order, the - // ORDER BY can be removed, changing the fields in the DISTINCT to match - // the ordering of fields in the ORDER BY. - - if (sort && project && (sort->expressions.getCount() <= project->expressions.getCount())) - { - const size_t count = sort->expressions.getCount(); - const NestConst* sort_ptr = sort->expressions.begin(); - const NestConst* const sort_end = sort_ptr + count; - - for (; sort_ptr != sort_end; ++sort_ptr) - { - const NestConst* project_ptr = project->expressions.begin(); - const NestConst* const project_end = project_ptr + count; - - for (; project_ptr != project_end; ++project_ptr) - { - const FieldNode* sortField = nodeAs(*sort_ptr); - const FieldNode* projectField = nodeAs(*project_ptr); - - if (sortField && projectField && - sortField->fieldStream == projectField->fieldStream && - sortField->fieldId == projectField->fieldId) - { - break; - } - } - - if (project_ptr == project_end) - break; - } - - // if all the fields in the sort list match the first n fields - // in the project list, we can ignore the sort, but update - // the project to match the sort. - if (sort_ptr == sort_end) - { - set_direction(sort, project); - set_position(sort, project, NULL); - sort = rse->rse_sorted = NULL; - } - } - - // RP: optimize sort with OUTER JOIN - // if all the fields in the sort list are from one stream, check the stream is - // the most outer stream, if true update rse and ignore the sort - if (sort && !project) - { - StreamType sort_stream = 0; - bool usableSort = true; - NestConst* sort_ptr = sort->expressions.begin(); - const NestConst* const sort_end = sort->expressions.end(); - - for (; sort_ptr != sort_end; ++sort_ptr) - { - const FieldNode* sortField; - - if ((sortField = nodeAs(*sort_ptr))) - { - // Get stream for this field at this position. - const StreamType current_stream = sortField->fieldStream; - - // If this is the first position node, save this stream. - if (sort_ptr == sort->expressions.begin()) - sort_stream = current_stream; - else if (current_stream != sort_stream) - { - // If the current stream is different then the previous stream - // then we can't use this sort for an indexed order retrieval. - usableSort = false; - break; - } - } - else - { - // If this is not the first position node, reject this sort. - // Two expressions cannot be mapped to a single index. - if (sort_ptr > sort->expressions.begin()) - { - usableSort = false; - break; - } - - // This position doesn't use a simple field, thus we should - // check the expression internals. - SortedStreamList streams; - (*sort_ptr)->collectStreams(streams); - - // We can use this sort only if there's a single stream - // referenced by the expression. - if (streams.getCount() == 1) - sort_stream = streams[0]; - else - { - usableSort = false; - break; - } - } - } - - if (usableSort) - { - RseNode* new_rse = NULL; - RecordSourceNode* node = rse; - - while (node) - { - if (nodeIs(node)) - { - new_rse = static_cast(node); - - // AB: Don't distribute the sort when a FIRST/SKIP is supplied, - // because that will affect the behaviour from the deeper RSE. - // dimitr: the same rule applies to explicit/implicit user-defined sorts. - if (new_rse != rse && - (new_rse->rse_first || new_rse->rse_skip || - new_rse->rse_sorted || new_rse->rse_projection)) - { - node = NULL; - break; - } - - // Walk trough the relations of the RSE and see if a - // matching stream can be found. - if (new_rse->rse_jointype == blr_inner) - { - if (new_rse->rse_relations.getCount() == 1) - node = new_rse->rse_relations[0]; - else - { - bool sortStreamFound = false; - for (FB_SIZE_T i = 0; i < new_rse->rse_relations.getCount(); i++) - { - RecordSourceNode* subNode = new_rse->rse_relations[i]; - - if ((nodeIs(subNode) || nodeIs(subNode)) && - subNode->getStream() == sort_stream && - new_rse != rse) - { - // We have found the correct stream - sortStreamFound = true; - break; - } - } - - if (sortStreamFound) - { - // Set the sort to the found stream and clear the original sort - new_rse->rse_sorted = sort; - sort = rse->rse_sorted = NULL; - } - - node = NULL; - } - } - else if (new_rse->rse_jointype == blr_left) - node = new_rse->rse_relations[0]; - else - node = NULL; - } - else - { - if ((nodeIs(node) || nodeIs(node)) && - node->getStream() == sort_stream && - new_rse && new_rse != rse) - { - // We have found the correct stream, thus apply the sort here - new_rse->rse_sorted = sort; - sort = rse->rse_sorted = NULL; - } - - node = NULL; - } - } - } - } -} - - -static void class_mask(USHORT count, ValueExprNode** eq_class, ULONG* mask) -{ -/************************************** - * - * c l a s s _ m a s k - * - ************************************** - * - * Functional description - * Given an sort/merge join equivalence class (vector of node pointers - * of representative values for rivers), return a bit mask of rivers - * with values. - * - **************************************/ - if (count > MAX_CONJUNCTS) - { - ERR_post(Arg::Gds(isc_optimizer_blk_exc)); - // Msg442: size of optimizer block exceeded - } - - for (SLONG i = 0; i < OPT_STREAM_BITS; i++) - mask[i] = 0; - - for (SLONG i = 0; i < count; i++, eq_class++) - { - if (*eq_class) - { - SET_DEP_BIT(mask, i); - DEV_BLKCHK(*eq_class, type_nod); - } - } -} - - -static SLONG decompose(thread_db* tdbb, BoolExprNode* boolNode, BoolExprNodeStack& stack, - CompilerScratch* csb) -{ -/************************************** - * - * d e c o m p o s e - * - ************************************** - * - * Functional description - * Decompose a boolean into a stack of conjuctions. - * - **************************************/ - DEV_BLKCHK(csb, type_csb); - - BinaryBoolNode* binaryNode = nodeAs(boolNode); - ComparativeBoolNode* cmpNode = nodeAs(boolNode); - - if (binaryNode) - { - if (binaryNode->blrOp == blr_and) - { - SLONG count = decompose(tdbb, binaryNode->arg1, stack, csb); - count += decompose(tdbb, binaryNode->arg2, stack, csb); - return count; - } - else if (binaryNode->blrOp == blr_or) - { - BoolExprNodeStack or_stack; - - if (decompose(tdbb, binaryNode->arg1, or_stack, csb) >= 2) - { - binaryNode->arg1 = or_stack.pop(); - - while (or_stack.hasData()) - { - BinaryBoolNode* newBoolNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode( - csb->csb_pool, blr_and); - newBoolNode->arg1 = or_stack.pop(); - newBoolNode->arg2 = binaryNode->arg1; - - binaryNode->arg1 = newBoolNode; - } - } - - or_stack.clear(); - - if (decompose(tdbb, binaryNode->arg2, or_stack, csb) >= 2) - { - binaryNode->arg2 = or_stack.pop(); - - while (or_stack.hasData()) - { - BinaryBoolNode* newBoolNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode( - csb->csb_pool, blr_and); - newBoolNode->arg1 = or_stack.pop(); - newBoolNode->arg2 = binaryNode->arg2; - - binaryNode->arg2 = newBoolNode; - } - } - } - } - else if (cmpNode) - { - // turn a between into (a greater than or equal) AND (a less than or equal) - - if (cmpNode->blrOp == blr_between) - { - ComparativeBoolNode* newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode( - csb->csb_pool, blr_geq); - newCmpNode->arg1 = cmpNode->arg1; - newCmpNode->arg2 = cmpNode->arg2; - - stack.push(newCmpNode); - - newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode(csb->csb_pool, blr_leq); - newCmpNode->arg1 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg1); - newCmpNode->arg2 = cmpNode->arg3; - - stack.push(newCmpNode); - - return 2; - } - - // turn a LIKE/SIMILAR into a LIKE/SIMILAR and a STARTING WITH, if it starts - // with anything other than a pattern-matching character - - ValueExprNode* arg; - - if ((cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar) && - (arg = optimize_like_similar(tdbb, csb, cmpNode))) - { - ComparativeBoolNode* newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode( - csb->csb_pool, blr_starting); - newCmpNode->arg1 = cmpNode->arg1; - newCmpNode->arg2 = arg; - - stack.push(newCmpNode); - stack.push(boolNode); - - return 2; - } - } - - stack.push(boolNode); - - return 1; -} - - -static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratch* csb, - USHORT base_count) -{ -/************************************** - * - * d i s t r i b u t e _ e q u a l i t i e s - * - ************************************** - * - * Functional description - * Given a stack of conjunctions, generate some simple - * inferences. In general, find classes of equalities, - * then find operations based on members of those classes. - * If we find any, generate additional conjunctions. In - * short: - * - * If (a == b) and (a $ c) --> (b $ c) for any - * operation '$'. - * - **************************************/ - - // dimitr: Dumb protection against too many injected conjuncts (see CORE-5381). - // Don't produce more additional conjuncts than we originally had - // (i.e. this routine should never more than double the number of conjuncts). - // Ideally, we need two separate limits here: - // 1) number of injected conjuncts (affects required impure size) - // 2) number of input conjuncts (affects search time inside this routine) - - if (base_count * 2 > MAX_CONJUNCTS) - return 0; - - ObjectsArray classes; - ObjectsArray::iterator eq_class; - - DEV_BLKCHK(csb, type_csb); - - // Zip thru stack of booleans looking for field equalities - - for (BoolExprNodeStack::iterator iter(org_stack); iter.hasData(); ++iter) - { - BoolExprNode* const boolean = iter.object(); - - if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) - continue; - - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - - if (!cmpNode || cmpNode->blrOp != blr_eql) - continue; - - ValueExprNode* const node1 = cmpNode->arg1; - if (!nodeIs(node1)) - continue; - - ValueExprNode* const node2 = cmpNode->arg2; - if (!nodeIs(node2)) - continue; - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - if (search_stack(node1, *eq_class)) - { - augment_stack(node2, *eq_class); - break; - } - else if (search_stack(node2, *eq_class)) - { - eq_class->push(node1); - break; - } - } - - if (eq_class == classes.end()) - { - ValueExprNodeStack& s = classes.add(); - s.push(node1); - s.push(node2); - eq_class = classes.back(); - } - } - - if (classes.isEmpty()) - return 0; - - // Make another pass looking for any equality relationships that may have crept - // in between classes (this could result from the sequence (A = B, C = D, B = C) - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - for (ValueExprNodeStack::const_iterator iter(*eq_class); iter.hasData(); ++iter) - { - for (ObjectsArray::iterator eq_class2(eq_class); - ++eq_class2 != classes.end();) - { - if (search_stack(iter.object(), *eq_class2)) - { - while (eq_class2->hasData()) - augment_stack(eq_class2->pop(), *eq_class); - } - } - } - } - - USHORT count = 0; - - // Start by making a pass distributing field equalities - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - if (eq_class->hasMore(2)) - { - for (ValueExprNodeStack::iterator outer(*eq_class); outer.hasData(); ++outer) - { - for (ValueExprNodeStack::iterator inner(outer); (++inner).hasData(); ) - { - if (count < base_count) - { - AutoPtr cmpNode(FB_NEW_POOL(csb->csb_pool) - ComparativeBoolNode(csb->csb_pool, blr_eql)); - cmpNode->arg1 = outer.object(); - cmpNode->arg2 = inner.object(); - - if (augment_stack(cmpNode, org_stack)) - { - count++; - cmpNode.release(); - } - } - } - } - } - } - - // Now make a second pass looking for non-field equalities - - for (BoolExprNodeStack::iterator iter(org_stack); iter.hasData(); ++iter) - { - BoolExprNode* const boolean = iter.object(); - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - ValueExprNode* node1; - ValueExprNode* node2; - - if (cmpNode && - (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_gtr || cmpNode->blrOp == blr_geq || - cmpNode->blrOp == blr_leq || cmpNode->blrOp == blr_lss || - cmpNode->blrOp == blr_matching || cmpNode->blrOp == blr_containing || - cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar)) - { - node1 = cmpNode->arg1; - node2 = cmpNode->arg2; - } - else - continue; - - bool reverse = false; - - if (!nodeIs(node1)) - { - ValueExprNode* swap_node = node1; - node1 = node2; - node2 = swap_node; - reverse = true; - } - - if (!nodeIs(node1)) - continue; - - if (!nodeIs(node2) && !nodeIs(node2) && !nodeIs(node2)) - continue; - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - if (search_stack(node1, *eq_class)) - { - for (ValueExprNodeStack::iterator temp(*eq_class); temp.hasData(); ++temp) - { - if (!node_equality(node1, temp.object()) && count < base_count) - { - ValueExprNode* arg1; - ValueExprNode* arg2; - - if (reverse) - { - arg1 = cmpNode->arg1; - arg2 = temp.object(); - } - else - { - arg1 = temp.object(); - arg2 = cmpNode->arg2; - } - - // From the conjuncts X(A,B) and A=C, infer the conjunct X(C,B) - AutoPtr newNode(make_inference_node(csb, boolean, arg1, arg2)); - - if (augment_stack(newNode, org_stack)) - { - ++count; - newNode.release(); - } - } - } - - break; - } - } - } - - return count; -} - - -static void find_index_relationship_streams(thread_db* tdbb, - OptimizerBlk* opt, - const StreamList& streams, - StreamList& dependent_streams, - StreamList& free_streams) -{ -/************************************** - * - * f i n d _ i n d e x _ r e l a t i o n s h i p _ s t r e a m s - * - ************************************** - * - * Functional description - * Find the streams that can use an index - * with the currently active streams. - * - **************************************/ - - DEV_BLKCHK(opt, type_opt); - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; - const StreamType* end_stream = streams.end(); - for (const StreamType* stream = streams.begin(); stream < end_stream; stream++) - { - CompilerScratch::csb_repeat* const csb_tail = &csb->csb_rpt[*stream]; - - // Set temporary active flag for this stream - csb_tail->activate(); - - bool indexed_relationship = false; - - if (opt->opt_conjuncts.getCount()) - { - // Calculate the inversion for this stream. - // The returning candidate contains the streams that will be used for - // index retrieval. This meant that if some stream is used this stream - // depends on already active streams and can not be used in a separate - // SORT/MERGE. - - OptimizerRetrieval optimizerRetrieval(*tdbb->getDefaultPool(), opt, *stream, - false, false, NULL); - AutoPtr candidate(optimizerRetrieval.getCost()); - - if (candidate->dependentFromStreams.hasData()) - { - indexed_relationship = true; - } - } - - if (indexed_relationship) - { - dependent_streams.add(*stream); - } - else - { - free_streams.add(*stream); - } - - // Reset active flag - csb_tail->deactivate(); - } -} - - -static void form_rivers(thread_db* tdbb, - OptimizerBlk* opt, - const StreamList& streams, - RiverList& river_list, - SortNode** sort_clause, - PlanNode* plan_clause) -{ -/************************************** - * - * f o r m _ r i v e r s - * - ************************************** - * - * Functional description - * Form streams into rivers according - * to the user-specified plan. - * - **************************************/ - SET_TDBB(tdbb); - DEV_BLKCHK(opt, type_opt); - - StreamList temp; - - // this must be a join or a merge node, so go through - // the substreams and place them into the temp vector - // for formation into a river. - PlanNode* plan_node = NULL; - NestConst* ptr = plan_clause->subNodes.begin(); - - for (const NestConst* const end = plan_clause->subNodes.end(); ptr != end; ++ptr) - { - plan_node = *ptr; - - if (plan_node->type == PlanNode::TYPE_JOIN) - { - form_rivers(tdbb, opt, streams, river_list, sort_clause, plan_node); - continue; - } - - // at this point we must have a retrieval node, so put - // the stream into the river. - fb_assert(plan_node->type == PlanNode::TYPE_RETRIEVE); - - const StreamType stream = plan_node->relationNode->getStream(); - - // dimitr: the plan may contain more retrievals than the "streams" - // array (some streams could already be joined to the active - // rivers), so we populate the "temp" array only with the - // streams that appear in both the plan and the "streams" - // array. - - const StreamType* ptr_stream = streams.begin(); - const StreamType* const end_stream = streams.end(); - - while (ptr_stream < end_stream) - { - if (*ptr_stream++ == stream) - { - temp.add(stream); - break; - } - } - } - - // just because the user specified a join does not mean that - // we are able to form a river; thus form as many rivers out - // of the join are as necessary to exhaust the streams. - // AB: Only form rivers when any retrieval node is seen, for - // example a MERGE on two JOINs will come with no retrievals - // at this point. - // CVC: Notice "plan_node" is pointing to the last element in the loop above. - // If the loop didn't execute, we had garbage in "plan_node". - - if (temp.getCount() != 0) - { - OptimizerInnerJoin innerJoin(*tdbb->getDefaultPool(), opt, temp, - (sort_clause ? *sort_clause : NULL), plan_clause); - - StreamType count; - do { - count = innerJoin.findJoinOrder(); - } while (form_river(tdbb, opt, count, streams.getCount(), temp, river_list, sort_clause)); - } -} - - -static bool form_river(thread_db* tdbb, - OptimizerBlk* opt, - StreamType count, - size_t stream_count, - StreamList& temp, - RiverList& river_list, - SortNode** sort_clause) -{ -/************************************** - * - * f o r m _ r i v e r - * - ************************************** - * - * Functional description - * Form streams into rivers (combinations of streams). - * - **************************************/ - fb_assert(count); - - DEV_BLKCHK(opt, type_opt); - DEV_BLKCHK(plan_clause, type_nod); - - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; - - HalfStaticArray rsbs; - rsbs.resize(count); - RecordSource** ptr = rsbs.begin(); - - StreamList streams; - streams.resize(count); - StreamType* stream = streams.begin(); - - if (count != stream_count) - sort_clause = NULL; - - const OptimizerBlk::opt_stream* const opt_end = opt->opt_streams.begin() + count; - for (OptimizerBlk::opt_stream* tail = opt->opt_streams.begin(); - tail < opt_end; tail++, stream++, ptr++) - { - *stream = tail->opt_best_stream; - *ptr = gen_retrieval(tdbb, opt, *stream, sort_clause, false, false, NULL); - sort_clause = NULL; - } - - RecordSource* const rsb = (count == 1) ? rsbs[0] : - FB_NEW_POOL(*tdbb->getDefaultPool()) NestedLoopJoin(csb, count, rsbs.begin()); - - // Allocate a river block and move the best order into it - River* const river = FB_NEW_POOL(*tdbb->getDefaultPool()) River(csb, rsb, NULL, streams); - river->deactivate(csb); - river_list.push(river); - - stream = temp.begin(); - const StreamType* const end_stream = temp.end(); - - fb_assert(temp.getCount() >= count); - temp.shrink(temp.getCount() - count); - if (!temp.getCount()) - return false; - - // Reform "temp" from streams not consumed - for (const StreamType* t2 = stream; t2 < end_stream; t2++) - { - bool used = false; - - for (OptimizerBlk::opt_stream* tail = opt->opt_streams.begin(); tail < opt_end; tail++) - { - if (*t2 == tail->opt_best_stream) - { - used = true; - break; - } - } - - if (!used) - *stream++ = *t2; - } - - return true; + const auto format = CMP_format(tdbb, csb, stream); + tail->csb_cardinality = getCardinality(tdbb, relation, format); } +// // Generate a separate AggregateSort (Aggregate SortedStream Block) for each distinct operation. // Note that this should be optimized to use indices if possible. -void OPT_gen_aggregate_distincts(thread_db* tdbb, CompilerScratch* csb, MapNode* map) -{ - DSC descriptor; - DSC* desc = &descriptor; - NestConst* ptr = map->sourceList.begin(); +// - for (const NestConst* const end = map->sourceList.end(); ptr != end; ++ptr) +void Optimizer::generateAggregateDistincts(MapNode* map) +{ + dsc descriptor; + dsc* desc = &descriptor; + + for (auto from : map->sourceList) { - ValueExprNode* from = *ptr; - AggNode* aggNode = nodeAs(from); + auto aggNode = nodeAs(from); if (aggNode && aggNode->distinct) { @@ -1938,8 +1050,8 @@ void OPT_gen_aggregate_distincts(thread_db* tdbb, CompilerScratch* csb, MapNode* desc->dsc_length++; } - AggregateSort* asb = FB_NEW_POOL(*tdbb->getDefaultPool()) AggregateSort( - *tdbb->getDefaultPool()); + const auto asb = FB_NEW_POOL(getPool()) AggregateSort(getPool()); + asb->intl = desc->isText() && desc->getTextType() != ttype_none && desc->getTextType() != ttype_binary && desc->getTextType() != ttype_ascii; @@ -1990,485 +1102,20 @@ void OPT_gen_aggregate_distincts(thread_db* tdbb, CompilerScratch* csb, MapNode* } -static void gen_join(thread_db* tdbb, - OptimizerBlk* opt, - const StreamList& streams, - RiverList& river_list, - SortNode** sort_clause, - PlanNode* plan_clause) +// +// Generate a record source block to handle either a sort or a project. +// The two case are virtual identical -- the only difference is that +// project eliminates duplicates. However, since duplicates are +// recognized and handled by sort, the JRD processing is identical. +// + +SortedStream* Optimizer::generateSort(const StreamList& streams, + const StreamList* dbkeyStreams, + RecordSource* rsb, + SortNode* sort, + bool refetchFlag, + bool projectFlag) { -/************************************** - * - * g e n _ j o i n - * - ************************************** - * - * Functional description - * Find all indexed relationships between streams, - * then form streams into rivers (combinations of - * streams). - * - **************************************/ - DEV_BLKCHK(opt, type_opt); - SET_TDBB(tdbb); - - if (!streams.getCount()) - return; - - if (plan_clause && streams.getCount() > 1) - { - // this routine expects a join/merge - form_rivers(tdbb, opt, streams, river_list, sort_clause, plan_clause); - return; - } - - OptimizerInnerJoin innerJoin(*tdbb->getDefaultPool(), opt, streams, - (sort_clause ? *sort_clause : NULL), plan_clause); - - StreamList temp; - temp.assign(streams); - - StreamType count; - do { - count = innerJoin.findJoinOrder(); - } while (form_river(tdbb, opt, count, streams.getCount(), temp, river_list, sort_clause)); -} - - -static RecordSource* gen_outer(thread_db* tdbb, OptimizerBlk* opt, RseNode* rse, - RiverList& river_list, SortNode** sort_clause) -{ -/************************************** - * - * g e n _ o u t e r - * - ************************************** - * - * Functional description - * Generate a top level outer join. The "outer" and "inner" - * sub-streams must be handled differently from each other. - * The inner is like other streams. The outer stream isn't - * because conjuncts may not eliminate records from the - * stream. They only determine if a join with an inner - * stream record is to be attempted. - * - **************************************/ - struct { - RecordSource* stream_rsb; - StreamType stream_num; - } stream_o, stream_i, *stream_ptr[2]; - - DEV_BLKCHK(opt, type_opt); - DEV_BLKCHK(rse, type_nod); - SET_TDBB(tdbb); - - // Determine which stream should be outer and which is inner. - // In the case of a left join, the syntactically left stream is the - // outer, and the right stream is the inner. For all others, swap - // the sense of inner and outer, though for a full join it doesn't - // matter and we should probably try both orders to see which is - // more efficient. - if (rse->rse_jointype != blr_left) - { - stream_ptr[1] = &stream_o; - stream_ptr[0] = &stream_i; - } - else - { - stream_ptr[0] = &stream_o; - stream_ptr[1] = &stream_i; - } - - // Loop through the outer join sub-streams in - // reverse order because rivers may have been PUSHed - for (int i = 1; i >= 0; i--) - { - const RecordSourceNode* node = rse->rse_relations[i]; - - if (nodeIs(node) || nodeIs(node)) - { - stream_ptr[i]->stream_rsb = NULL; - stream_ptr[i]->stream_num = node->getStream(); - } - else - { - River* const river = river_list.pop(); - stream_ptr[i]->stream_rsb = river->getRecordSource(); - } - } - - CompilerScratch* const csb = opt->opt_csb; - - const bool isFullJoin = (rse->rse_jointype == blr_full); - - if (!isFullJoin) - { - // Generate rsbs for the sub-streams. - // For the left sub-stream we also will get a boolean back. - BoolExprNode* boolean = NULL; - - if (!stream_o.stream_rsb) - { - stream_o.stream_rsb = gen_retrieval(tdbb, opt, stream_o.stream_num, sort_clause, - true, false, &boolean); - } - - if (!stream_i.stream_rsb) - { - // AB: the sort clause for the inner stream of an OUTER JOIN - // should never be used for the index retrieval - stream_i.stream_rsb = - gen_retrieval(tdbb, opt, stream_i.stream_num, NULL, false, true, NULL); - } - - // generate a parent boolean rsb for any remaining booleans that - // were not satisfied via an index lookup - stream_i.stream_rsb = gen_residual_boolean(tdbb, opt, stream_i.stream_rsb); - - // Allocate and fill in the rsb - return FB_NEW_POOL(*tdbb->getDefaultPool()) - NestedLoopJoin(csb, stream_o.stream_rsb, stream_i.stream_rsb, - boolean, OUTER_JOIN); - } - - bool hasOuterRsb = true, hasInnerRsb = true; - BoolExprNode* boolean = NULL; - - if (!stream_o.stream_rsb) - { - hasOuterRsb = false; - stream_o.stream_rsb = - gen_retrieval(tdbb, opt, stream_o.stream_num, NULL, true, false, &boolean); - } - - if (!stream_i.stream_rsb) - { - hasInnerRsb = false; - stream_i.stream_rsb = - gen_retrieval(tdbb, opt, stream_i.stream_num, NULL, false, true, NULL); - } - - RecordSource* const innerRsb = gen_residual_boolean(tdbb, opt, stream_i.stream_rsb); - - RecordSource* const rsb1 = FB_NEW_POOL(*tdbb->getDefaultPool()) - NestedLoopJoin(csb, stream_o.stream_rsb, innerRsb, boolean, OUTER_JOIN); - - for (FB_SIZE_T i = 0; i < opt->opt_conjuncts.getCount(); i++) - { - if (opt->opt_conjuncts[i].opt_conjunct_flags & opt_conjunct_used) - { - BoolExprNode* const org_node = opt->opt_conjuncts[i].opt_conjunct_node; - opt->opt_conjuncts[i].opt_conjunct_node = CMP_clone_node_opt(tdbb, csb, org_node); - opt->opt_conjuncts[i].opt_conjunct_flags = 0; - } - } - - if (!hasInnerRsb) - csb->csb_rpt[stream_i.stream_num].deactivate(); - - if (!hasOuterRsb) - csb->csb_rpt[stream_o.stream_num].deactivate(); - - boolean = NULL; - - if (!hasInnerRsb) - { - stream_i.stream_rsb = - gen_retrieval(tdbb, opt, stream_i.stream_num, NULL, true, false, &boolean); - } - - if (!hasOuterRsb) - { - stream_o.stream_rsb = - gen_retrieval(tdbb, opt, stream_o.stream_num, NULL, false, false, NULL); - } - - RecordSource* const outerRsb = gen_residual_boolean(tdbb, opt, stream_o.stream_rsb); - - RecordSource* const rsb2 = FB_NEW_POOL(*tdbb->getDefaultPool()) - NestedLoopJoin(csb, stream_i.stream_rsb, outerRsb, boolean, ANTI_JOIN); - - return FB_NEW_POOL(*tdbb->getDefaultPool()) FullOuterJoin(csb, rsb1, rsb2); -} - - -static RecordSource* gen_residual_boolean(thread_db* tdbb, OptimizerBlk* opt, RecordSource* prior_rsb) -{ -/************************************** - * - * g e n _ r e s i d u a l _ b o o l e a n - * - ************************************** - * - * Functional description - * Pick up any residual boolean remaining, - * meaning those that have not been used - * as part of some join. These booleans - * must still be applied to the result stream. - * - **************************************/ - SET_TDBB(tdbb); - DEV_BLKCHK(opt, type_opt); - DEV_BLKCHK(prior_rsb, type_rsb); - - BoolExprNode* boolean = NULL; - const OptimizerBlk::opt_conjunct* const opt_end = - opt->opt_conjuncts.begin() + opt->opt_base_conjuncts; - - for (OptimizerBlk::opt_conjunct* tail = opt->opt_conjuncts.begin(); tail < opt_end; tail++) - { - BoolExprNode* node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used)) - { - compose(*tdbb->getDefaultPool(), &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - - return boolean ? - FB_NEW_POOL(*tdbb->getDefaultPool()) FilteredStream(opt->opt_csb, prior_rsb, boolean) : - prior_rsb; -} - - -static RecordSource* gen_retrieval(thread_db* tdbb, - OptimizerBlk* opt, - StreamType stream, - SortNode** sort_ptr, - bool outer_flag, - bool inner_flag, - BoolExprNode** return_boolean) -{ -/************************************** - * - * g e n _ r e t r i e v a l - * - ************************************** - * - * Functional description - * Compile and optimize a record selection expression into a - * set of record source blocks (rsb's). - * - **************************************/ - OptimizerBlk::opt_conjunct* tail; - - SET_TDBB(tdbb); - - DEV_BLKCHK(opt, type_opt); - - CompilerScratch* const csb = opt->opt_csb; - CompilerScratch::csb_repeat* const csb_tail = &csb->csb_rpt[stream]; - jrd_rel* const relation = csb_tail->csb_relation; - - fb_assert(relation); - - const string alias = OPT_make_alias(csb, stream); - csb_tail->activate(); - - // Time to find inversions. For each index on the relation - // match all unused booleans against the index looking for upper - // and lower bounds that can be computed by the index. When - // all unused conjunctions are exhausted, see if there is enough - // information for an index retrieval. If so, build up an - // inversion component of the boolean. - - // It's recalculated later. - const OptimizerBlk::opt_conjunct* opt_end = opt->opt_conjuncts.begin() + - (inner_flag ? opt->opt_base_missing_conjuncts : opt->opt_conjuncts.getCount()); - - RecordSource* rsb = NULL; - InversionNode* inversion = NULL; - BoolExprNode* condition = NULL; - Array dbkeyRanges; - - if (relation->rel_file) - { - // External table - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ExternalTableScan(csb, alias, stream, relation); - } - else if (relation->isVirtual()) - { - // Virtual table: monitoring or security - switch (relation->rel_id) - { - case rel_global_auth_mapping: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) GlobalMappingScan(csb, alias, stream, relation); - break; - - case rel_sec_users: - case rel_sec_user_attributes: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) UsersTableScan(csb, alias, stream, relation); - break; - - case rel_sec_db_creators: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) DbCreatorsScan(csb, alias, stream, relation); - break; - - case rel_time_zones: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) TimeZonesTableScan(csb, alias, stream, relation); - break; - - case rel_config: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ConfigTableScan(csb, alias, stream, relation); - break; - - case rel_keywords: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) KeywordsTableScan(csb, alias, stream, relation); - break; - - default: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation); - break; - } - } - else - { - // Persistent table - OptimizerRetrieval optimizerRetrieval(*tdbb->getDefaultPool(), opt, stream, - outer_flag, inner_flag, - (sort_ptr ? *sort_ptr : NULL)); - AutoPtr candidate(optimizerRetrieval.getInversion()); - - if (candidate) - { - inversion = candidate->inversion; - condition = candidate->condition; - dbkeyRanges.assign(candidate->dbkeyRanges); - - // Just for safety sake, this condition must be already checked - // inside OptimizerRetrieval::matchOnIndexes() - - if (inversion && condition && - !condition->computable(csb, stream, false)) - { - fb_assert(false); - inversion = NULL; - condition = NULL; - dbkeyRanges.clear(); - } - } - - IndexTableScan* const nav_rsb = optimizerRetrieval.getNavigation(); - - if (nav_rsb) - { - if (sort_ptr) - *sort_ptr = NULL; - - nav_rsb->setInversion(inversion, condition); - - rsb = nav_rsb; - } - } - - if (outer_flag) - { - fb_assert(return_boolean); - // Now make another pass thru the outer conjuncts only, finding unused, - // computable booleans. When one is found, roll it into a final - // boolean and mark it used. - *return_boolean = NULL; - opt_end = opt->opt_conjuncts.begin() + opt->opt_base_conjuncts; - - for (tail = opt->opt_conjuncts.begin(); tail < opt_end; tail++) - { - BoolExprNode* node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - compose(*tdbb->getDefaultPool(), return_boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - } - - // Now make another pass thru the conjuncts finding unused, computable - // booleans. When one is found, roll it into a final boolean and mark - // it used. If a computable boolean didn't match against an index then - // mark the stream to denote unmatched booleans. - BoolExprNode* boolean = NULL; - opt_end = opt->opt_conjuncts.begin() + (inner_flag ? opt->opt_base_missing_conjuncts : opt->opt_conjuncts.getCount()); - tail = opt->opt_conjuncts.begin(); - - if (outer_flag) - tail += opt->opt_base_parent_conjuncts; - - for (; tail < opt_end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - // If inversion is available, utilize all conjuncts that refer to - // the stream being retrieved. Otherwise, utilize only conjuncts - // that are local to this stream. The remaining ones are left in piece - // as possible candidates for a merge/hash join. - - if ((inversion && node->containsStream(stream)) || - (!inversion && node->computable(csb, stream, true))) - { - compose(*tdbb->getDefaultPool(), &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - - if (!outer_flag && !(tail->opt_conjunct_flags & opt_conjunct_matched)) - csb_tail->csb_flags |= csb_unmatched; - } - } - } - - if (!rsb) - { - if (inversion && condition) - { - RecordSource* const rsb1 = - FB_NEW_POOL(*tdbb->getDefaultPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); - RecordSource* const rsb2 = - FB_NEW_POOL(*tdbb->getDefaultPool()) BitmapTableScan(csb, alias, stream, relation, inversion); - - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ConditionalStream(csb, rsb1, rsb2, condition); - } - else if (inversion) - { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) BitmapTableScan(csb, alias, stream, relation, inversion); - } - else - { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); - - if (boolean) - csb->csb_rpt[stream].csb_flags |= csb_unmatched; - } - } - - return boolean ? FB_NEW_POOL(*tdbb->getDefaultPool()) FilteredStream(csb, rsb, boolean) : rsb; -} - - -SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamList& streams, - const StreamList* dbkey_streams, RecordSource* prior_rsb, SortNode* sort, - bool refetch_flag, bool project_flag) -{ -/************************************** - * - * g e n _ s o r t - * - ************************************** - * - * Functional description - * Generate a record source block to handle either a sort or a project. - * The two case are virtual identical -- the only difference is that - * project eliminates duplicates. However, since duplicates are - * recognized and handled by sort, the JRD processing is identical. - * - **************************************/ - DEV_BLKCHK(prior_rsb, type_rsb); - SET_TDBB(tdbb); - /* We already know the number of keys, but we also need to compute the total number of fields, keys and non-keys, to be pumped thru sort. Starting with the number of keys, count the other field referenced. Since a field @@ -2481,7 +1128,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi * transactions. */ ULONG items = sort->expressions.getCount() + - 3 * streams.getCount() + 2 * (dbkey_streams ? dbkey_streams->getCount() : 0); + 3 * streams.getCount() + 2 * (dbkeyStreams ? dbkeyStreams->getCount() : 0); const NestConst* const end_node = sort->expressions.end(); // Collect all fields involved into the sort @@ -2511,7 +1158,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi // If the field has already been mentioned as a sort key, don't bother to repeat it. // Unless this key is computed/volatile and thus cannot be restored after sorting. - for (auto expr : sort->expressions) + for (const auto expr : sort->expressions) { const auto fieldNode = nodeAs(expr); @@ -2536,18 +1183,18 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi // Unless refetching is requested explicitly (e.g. FIRST ROWS optimization mode), // validate the sort record length against the configured threshold for inline storage - if (!refetch_flag) + if (!refetchFlag) { const auto dbb = tdbb->getDatabase(); const auto threshold = dbb->dbb_config->getInlineSortThreshold(); - refetch_flag = (totalLength > threshold); + refetchFlag = (totalLength > threshold); } // Check for persistent fields to be excluded from the sort. // If nothing is excluded, there's no point in the refetch mode. - if (refetch_flag) + if (refetchFlag) { for (auto& item : fields) { @@ -2558,24 +1205,23 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi !relation->rel_view_rse && !relation->isVirtual()) { - item.desc = NULL; + item.desc = nullptr; --fieldCount; } } - refetch_flag = (fieldCount != fields.getCount()); + refetchFlag = (fieldCount != fields.getCount()); } items += fieldCount; // Now that we know the number of items, allocate a sort map block. - SortedStream::SortMap* map = - FB_NEW_POOL(*tdbb->getDefaultPool()) SortedStream::SortMap(*tdbb->getDefaultPool()); + const auto map = FB_NEW_POOL(getPool()) SortedStream::SortMap(getPool()); - if (project_flag) + if (projectFlag) map->flags |= SortedStream::FLAG_PROJECT; - if (refetch_flag) + if (refetchFlag) map->flags |= SortedStream::FLAG_REFETCH; if (sort->unique) @@ -2653,7 +1299,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi sort_key->skd_flags |= SKD_binary; } - if (SortedStream::hasVolatileKey(desc) && !refetch_flag) + if (SortedStream::hasVolatileKey(desc) && !refetchFlag) sort_key->skd_flags |= SKD_separate_data; map_item->clear(); @@ -2725,11 +1371,11 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi map_item++; } - if (dbkey_streams && dbkey_streams->hasData()) + if (dbkeyStreams && dbkeyStreams->hasData()) { map_length = ROUNDUP(map_length, sizeof(SINT64)); - for (const auto stream : *dbkey_streams) + for (const auto stream : *dbkeyStreams) { map_item->clear(); map_item->fieldId = SortedStream::ID_DBKEY; @@ -2742,7 +1388,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi map_item++; } - for (const auto stream : *dbkey_streams) + for (const auto stream : *dbkeyStreams) { map_item->clear(); map_item->fieldId = SortedStream::ID_DBKEY_VALID; @@ -2800,61 +1446,842 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi // That was most unpleasant. Never the less, it's done (except for the debugging). // All that remains is to build the record source block for the sort. - return FB_NEW_POOL(*tdbb->getDefaultPool()) SortedStream(csb, prior_rsb, map); + return FB_NEW_POOL(getPool()) SortedStream(csb, rsb, map); } -static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_rivers) +// +// Find conjuncts local to the given river and compose an appropriate filter +// + +RecordSource* Optimizer::applyLocalBoolean(const River* river) +{ + StreamStateHolder stateHolder(csb); + stateHolder.deactivate(); + + river->activate(csb); + + BoolExprNode* boolean = nullptr; + + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) + { + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; + } + } + + const auto rsb = river->getRecordSource(); + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean) : rsb; +} + + +// +// Check to make sure that the user-specified indices were actually utilized by the optimizer +// + +void Optimizer::checkIndices() +{ + for (const auto compileStream : compileStreams) + { + const auto tail = &csb->csb_rpt[compileStream]; + + const auto plan = tail->csb_plan; + if (!plan) + continue; + + if (plan->type != PlanNode::TYPE_RETRIEVE) + continue; + + const auto relation = tail->csb_relation; + + // If there were no indices fetched at all but the user specified some, + // error out using the first index specified + + if (!tail->csb_idx && plan->accessType && !tdbb->getAttachment()->isGbak()) + { + // index %s cannot be used in the specified plan + ERR_post(Arg::Gds(isc_index_unused) << plan->accessType->items[0].indexName); + } + + if (!tail->csb_idx) + return; + + // Check to make sure that all indices are either used or marked not to be used, + // and that there are no unused navigational indices + MetaName index_name; + + for (const auto& idx : *tail->csb_idx) + { + if (!(idx.idx_runtime_flags & (idx_plan_dont_use | idx_used)) || + ((idx.idx_runtime_flags & idx_plan_navigate) && !(idx.idx_runtime_flags & idx_navigate))) + { + if (relation) + MET_lookup_index(tdbb, index_name, relation->rel_name, (USHORT) (idx.idx_id + 1)); + else + index_name = ""; + + // index %s cannot be used in the specified plan + ERR_post(Arg::Gds(isc_index_unused) << Arg::Str(index_name)); + } + } + } +} + + +// +// Try to optimize out unnecessary sorting +// + +void Optimizer::checkSorts() +{ + SortNode* sort = rse->rse_sorted; + const auto sortCount = sort ? sort->expressions.getCount() : 0; + + SortNode* project = rse->rse_projection; + const auto projectCount = project ? project->expressions.getCount() : 0; + + // Check if a GROUP BY exists using the same fields as the project or sort: + // if so, the projection can be eliminated; if no projection exists, then + // the sort can be eliminated + + RecordSourceNode* subRse; + AggregateSourceNode* aggregate; + SortNode* group; + + if ((project || sort) && + rse->rse_relations.getCount() == 1 && + (subRse = rse->rse_relations[0]) && + (aggregate = nodeAs(subRse)) && + (group = aggregate->group)) + { + const auto map = aggregate->map; + const auto groupCount = group->expressions.getCount(); + + // If all the fields of the project are the same as all the fields + // of the group by, get rid of the project + + if (project && projectCount == groupCount) + { + bool equal = true; + for (unsigned i = 0; i < groupCount; i++) + { + const auto groupNode = group->expressions[i]; + const auto projectNode = project->expressions[i]; + + if (!mapEqual(groupNode, projectNode, map)) + { + equal = false; + break; + } + } + + // We can now ignore the project, but in case the project is being done + // in descending order because of an order by, do the group by the same way. + if (equal) + { + setDirection(project, group); + project = rse->rse_projection = nullptr; + } + } + + // If there is no projection, then we can make a similar optimization + // for sort, except that sort may have fewer fields than group by + + if (!project && sort && sortCount <= groupCount) + { + bool equal = true; + for (unsigned i = 0; i < sortCount; i++) + { + const auto groupNode = group->expressions[i]; + const auto sortNode = sort->expressions[i]; + + if (!mapEqual(groupNode, sortNode, map)) + { + equal = false; + break; + } + } + + // If all the fields in the sort list match the first n fields in the + // project list, we can ignore the sort, but update the sort order + // (ascending/descending) to match that in the sort list + + if (equal) + { + setDirection(sort, group); + setPosition(sort, group, map); + sort = rse->rse_sorted = nullptr; + } + } + + } + + // Examine the ORDER BY and DISTINCT clauses; if all the fields in the + // ORDER BY match the first n fields in the DISTINCT in any order, the + // ORDER BY can be removed, changing the fields in the DISTINCT to match + // the ordering of fields in the ORDER BY + + if (sort && project && sortCount <= projectCount) + { + bool equal = true; + for (unsigned i = 0; i < sortCount; i++) + { + const auto sortField = nodeAs(sort->expressions[i]); + const auto projectField = nodeAs(project->expressions[i]); + + if (!sortField || !projectField || + sortField->fieldStream != projectField->fieldStream || + sortField->fieldId != projectField->fieldId) + { + equal = false; + break; + } + } + + // If all the fields in the sort list match the first n fields + // in the project list, we can ignore the sort, but update + // the project to match the sort + if (equal) + { + setDirection(sort, project); + setPosition(sort, project, nullptr); + sort = rse->rse_sorted = nullptr; + } + } + + // RP: optimize sort with OUTER JOIN + // if all the fields in the sort list are from one stream, check the stream is + // the most outer stream, if true update rse and ignore the sort + if (sort && !project) + { + StreamType sortStream = 0; + bool usableSort = true; + + for (unsigned i = 0; i < sortCount; i++) + { + const auto sortNode = sort->expressions[i]; + const auto sortField = nodeAs(sortNode); + + if (sortField) + { + // Get stream for this field at this position. + const StreamType currentStream = sortField->fieldStream; + + // If this is the first position node, save this stream + if (i == 0) + sortStream = currentStream; + else if (currentStream != sortStream) + { + // If the current stream is different then the previous stream + // then we can't use this sort for an indexed order retrieval + usableSort = false; + break; + } + } + else + { + // If this is not the first position node, reject this sort. + // Two expressions cannot be mapped to a single index. + if (i > 0) + { + usableSort = false; + break; + } + + // This position doesn't use a simple field, thus we should + // check the expression internals + SortedStreamList streams; + sortNode->collectStreams(streams); + + // We can use this sort only if there's a single stream + // referenced by the expression + if (streams.getCount() == 1) + sortStream = streams[0]; + else + { + usableSort = false; + break; + } + } + } + + if (usableSort) + { + RecordSourceNode* node = rse; + RseNode* newRse = nullptr; + + while (node) + { + if (nodeIs(node)) + { + newRse = static_cast(node); + + // AB: Don't distribute the sort when a FIRST/SKIP is supplied, + // because that will affect the behaviour from the deeper RSE. + // dimitr: the same rule applies to explicit/implicit user-defined sorts. + if (newRse != rse && + (newRse->rse_first || newRse->rse_skip || + newRse->rse_sorted || newRse->rse_projection)) + { + node = nullptr; + break; + } + + // Walk trough the relations of the RSE and see if a + // matching stream can be found. + if (newRse->rse_jointype == blr_inner) + { + if (newRse->rse_relations.getCount() == 1) + node = newRse->rse_relations[0]; + else + { + bool sortStreamFound = false; + for (const auto subRse : newRse->rse_relations) + { + if ((nodeIs(subRse) || nodeIs(subRse)) && + subRse->getStream() == sortStream && + newRse != rse) + { + // We have found the correct stream + sortStreamFound = true; + break; + } + } + + if (sortStreamFound) + { + // Set the sort to the found stream and clear the original sort + newRse->rse_sorted = sort; + sort = rse->rse_sorted = nullptr; + } + + node = nullptr; + } + } + else if (newRse->rse_jointype == blr_left) + node = newRse->rse_relations[0]; + else + node = nullptr; + } + else + { + if ((nodeIs(node) || nodeIs(node)) && + node->getStream() == sortStream && + newRse && newRse != rse) + { + // We have found the correct stream, thus apply the sort here + newRse->rse_sorted = sort; + sort = rse->rse_sorted = nullptr; + } + + node = nullptr; + } + } + } + } +} + + +// +// Decompose a boolean into a stack of conjuctions. +// + +unsigned Optimizer::decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack) +{ + const auto binaryNode = nodeAs(boolNode); + const auto cmpNode = nodeAs(boolNode); + + if (binaryNode) + { + if (binaryNode->blrOp == blr_and) + { + auto count = decompose(binaryNode->arg1, stack); + count += decompose(binaryNode->arg2, stack); + return count; + } + else if (binaryNode->blrOp == blr_or) + { + BoolExprNodeStack or_stack; + + if (decompose(binaryNode->arg1, or_stack) >= 2) + { + binaryNode->arg1 = or_stack.pop(); + + while (or_stack.hasData()) + { + const auto newBoolNode = + FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_and); + newBoolNode->arg1 = or_stack.pop(); + newBoolNode->arg2 = binaryNode->arg1; + + binaryNode->arg1 = newBoolNode; + } + } + + or_stack.clear(); + + if (decompose(binaryNode->arg2, or_stack) >= 2) + { + binaryNode->arg2 = or_stack.pop(); + + while (or_stack.hasData()) + { + const auto newBoolNode = + FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_and); + newBoolNode->arg1 = or_stack.pop(); + newBoolNode->arg2 = binaryNode->arg2; + + binaryNode->arg2 = newBoolNode; + } + } + } + } + else if (cmpNode) + { + // turn a between into (a greater than or equal) AND (a less than or equal) + + if (cmpNode->blrOp == blr_between) + { + auto newCmpNode = FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_geq); + newCmpNode->arg1 = cmpNode->arg1; + newCmpNode->arg2 = cmpNode->arg2; + + stack.push(newCmpNode); + + newCmpNode = FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_leq); + newCmpNode->arg1 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg1); + newCmpNode->arg2 = cmpNode->arg3; + + stack.push(newCmpNode); + + return 2; + } + + // turn a LIKE/SIMILAR into a LIKE/SIMILAR and a STARTING WITH, if it starts + // with anything other than a pattern-matching character + + ValueExprNode* arg; + + if ((cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar) && + (arg = optimizeLikeSimilar(cmpNode))) + { + const auto newCmpNode = + FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_starting); + newCmpNode->arg1 = cmpNode->arg1; + newCmpNode->arg2 = arg; + + stack.push(newCmpNode); + stack.push(boolNode); + + return 2; + } + } + + stack.push(boolNode); + + return 1; +} + + +// +// Given a stack of conjunctions, generate some simple inferences. +// In general, find classes of equalities, then find operations based on members of those classes. +// If we find any, generate additional conjunctions. In short: +// +// if (a == b) and (a $ c) --> (b $ c) for any operation '$'. +// + +unsigned Optimizer::distributeEqualities(BoolExprNodeStack& orgStack, unsigned baseCount) +{ + // dimitr: Dumb protection against too many injected conjuncts (see CORE-5381). + // Don't produce more additional conjuncts than we originally had + // (i.e. this routine should never more than double the number of conjuncts). + // Ideally, we need two separate limits here: + // 1) number of injected conjuncts (affects required impure size) + // 2) number of input conjuncts (affects search time inside this routine) + + if (baseCount * 2 > MAX_CONJUNCTS) + return 0; + + ObjectsArray classes; + ObjectsArray::iterator eq_class; + + // Zip thru stack of booleans looking for field equalities + + for (BoolExprNodeStack::iterator iter(orgStack); iter.hasData(); ++iter) + { + const auto boolean = iter.object(); + + if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) + continue; + + const auto cmpNode = nodeAs(boolean); + + if (!cmpNode || cmpNode->blrOp != blr_eql) + continue; + + auto node1 = cmpNode->arg1; + if (!nodeIs(node1)) + continue; + + auto node2 = cmpNode->arg2; + if (!nodeIs(node2)) + continue; + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + if (searchStack(node1, *eq_class)) + { + augmentStack(node2, *eq_class); + break; + } + else if (searchStack(node2, *eq_class)) + { + eq_class->push(node1); + break; + } + } + + if (eq_class == classes.end()) + { + ValueExprNodeStack& s = classes.add(); + s.push(node1); + s.push(node2); + eq_class = classes.back(); + } + } + + if (classes.isEmpty()) + return 0; + + // Make another pass looking for any equality relationships that may have crept + // in between classes (this could result from the sequence (A = B, C = D, B = C) + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + for (ValueExprNodeStack::const_iterator iter(*eq_class); iter.hasData(); ++iter) + { + for (ObjectsArray::iterator eq_class2(eq_class); + ++eq_class2 != classes.end();) + { + if (searchStack(iter.object(), *eq_class2)) + { + while (eq_class2->hasData()) + augmentStack(eq_class2->pop(), *eq_class); + } + } + } + } + + unsigned count = 0; + + // Start by making a pass distributing field equalities + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + if (eq_class->hasMore(2)) + { + for (ValueExprNodeStack::iterator outer(*eq_class); outer.hasData(); ++outer) + { + for (ValueExprNodeStack::iterator inner(outer); (++inner).hasData(); ) + { + if (count < baseCount) + { + AutoPtr cmpNode(FB_NEW_POOL(getPool()) + ComparativeBoolNode(getPool(), blr_eql)); + cmpNode->arg1 = outer.object(); + cmpNode->arg2 = inner.object(); + + if (augmentStack(cmpNode, orgStack)) + { + count++; + cmpNode.release(); + } + } + } + } + } + } + + // Now make a second pass looking for non-field equalities + + for (BoolExprNodeStack::iterator iter(orgStack); iter.hasData(); ++iter) + { + const auto boolean = iter.object(); + const auto cmpNode = nodeAs(boolean); + ValueExprNode* node1; + ValueExprNode* node2; + + if (cmpNode && + (cmpNode->blrOp == blr_eql || + cmpNode->blrOp == blr_gtr || cmpNode->blrOp == blr_geq || + cmpNode->blrOp == blr_leq || cmpNode->blrOp == blr_lss || + cmpNode->blrOp == blr_matching || cmpNode->blrOp == blr_containing || + cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar)) + { + node1 = cmpNode->arg1; + node2 = cmpNode->arg2; + } + else + continue; + + bool reverse = false; + + if (!nodeIs(node1)) + { + ValueExprNode* swap_node = node1; + node1 = node2; + node2 = swap_node; + reverse = true; + } + + if (!nodeIs(node1)) + continue; + + if (!nodeIs(node2) && !nodeIs(node2) && !nodeIs(node2)) + continue; + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + if (searchStack(node1, *eq_class)) + { + for (ValueExprNodeStack::iterator temp(*eq_class); temp.hasData(); ++temp) + { + if (!fieldEqual(node1, temp.object()) && count < baseCount) + { + ValueExprNode* arg1; + ValueExprNode* arg2; + + if (reverse) + { + arg1 = cmpNode->arg1; + arg2 = temp.object(); + } + else + { + arg1 = temp.object(); + arg2 = cmpNode->arg2; + } + + // From the conjuncts X(A,B) and A=C, infer the conjunct X(C,B) + AutoPtr newNode(makeInferenceNode(boolean, arg1, arg2)); + + if (augmentStack(newNode, orgStack)) + { + ++count; + newNode.release(); + } + } + } + + break; + } + } + } + + return count; +} + + +// +// Find the streams that can use an index with the currently active streams +// + +void Optimizer::findDependentStreams(const StreamList& streams, + StreamList& dependent_streams, + StreamList& free_streams) +{ +#ifdef OPT_DEBUG_RETRIEVAL + if (streams.hasData()) + printf("Detecting dependent streams:\n"); +#endif + + for (const auto stream : streams) + { + const auto tail = &csb->csb_rpt[stream]; + + // Set temporary active flag for this stream + tail->activate(); + + bool indexed_relationship = false; + + if (conjuncts.hasData()) + { + // Calculate the inversion for this stream. + // The returning candidate contains the streams that will be used for + // index retrieval. This meant that if some stream is used this stream + // depends on already active streams and can not be used in a separate + // SORT/MERGE. + + Retrieval retrieval(tdbb, this, stream, false, false, nullptr, true); + const auto candidate = retrieval.getInversion(); + + if (candidate->dependentFromStreams.hasData()) + indexed_relationship = true; + } + + if (indexed_relationship) + dependent_streams.add(stream); + else + free_streams.add(stream); + + // Reset active flag + tail->deactivate(); + } +} + + +// +// Form streams into rivers (combinations of streams) +// + +bool Optimizer::formRiver(unsigned streamCount, + StreamList& streams, + const StreamList& joinedStreams, + RiverList& rivers, + SortNode** sortClause) +{ + const auto count = joinedStreams.getCount(); + fb_assert(count); + + if (count != streamCount) + sortClause = nullptr; + + HalfStaticArray rsbs(count); + + for (const auto stream : joinedStreams) + { + rsbs.add(generateRetrieval(stream, sortClause, false, false, nullptr)); + sortClause = nullptr; + + // Remove already consumed streams from remainingStreams + FB_SIZE_T pos; + if (streams.find(stream, pos)) + streams.remove(pos); + else + fb_assert(false); + } + + const auto rsb = (count == 1) ? rsbs[0] : + FB_NEW_POOL(getPool()) NestedLoopJoin(csb, count, rsbs.begin()); + + // Allocate a river block and move the best order into it + const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, nullptr, joinedStreams); + river->deactivate(csb); + rivers.push(river); + + return streams.hasData(); +} + + +// +// Form streams into rivers according to the user-specified plan +// + +void Optimizer::formRivers(const StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause) +{ + StreamList tempStreams; + + // This must be a join or a merge node, so go through + // the substreams and place them into the temp vector + // for formation into a river + + for (const auto planNode : planClause->subNodes) + { + if (planNode->type == PlanNode::TYPE_JOIN) + { + formRivers(streams, rivers, sortClause, planNode); + continue; + } + + // At this point we must have a retrieval node, so put + // the stream into the river + fb_assert(planNode->type == PlanNode::TYPE_RETRIEVE); + + const StreamType stream = planNode->relationNode->getStream(); + + // dimitr: the plan may contain more retrievals than the "streams" + // array (some streams could already be joined to the active + // rivers), so we populate the "temp" array only with the + // streams that appear in both the plan and the "streams" + // array. + + if (streams.exist(stream)) + tempStreams.add(stream); + } + + // Just because the user specified a join does not mean that + // we are able to form a river; thus form as many rivers out + // of the join are as necessary to exhaust the streams. + // AB: Only form rivers when any retrieval node is seen, for + // example a MERGE on two JOINs will come with no retrievals + // at this point. + // CVC: Notice "plan_node" is pointing to the last element in the loop above. + // If the loop didn't execute, we had garbage in "planNode". + + if (tempStreams.hasData()) + { + const auto count = tempStreams.getCount(); + + InnerJoin innerJoin(tdbb, this, tempStreams, + (sortClause ? *sortClause : nullptr), + (planClause != nullptr)); + + StreamList joinedStreams; + while (innerJoin.findJoinOrder(joinedStreams)) + { + if (!formRiver(count, tempStreams, joinedStreams, rivers, sortClause)) + break; + } + } +} + + +// +// We've got a set of rivers that may or may not be amenable to +// a hash join or a sort/merge join, and it's time to find out. +// If there are, build an appropriate join RecordSource, +// push it on the rsb stack, and update rivers accordingly. +// If two or more rivers were successfully joined, return true. +// If the whole things is a moby no-op, return false. +// + +bool Optimizer::generateEquiJoin(RiverList& org_rivers) { -/************************************** - * - * g e n _ e q u i _ j o i n - * - ************************************** - * - * Functional description - * We've got a set of rivers that may or may not be amenable to - * a hash join or a sort/merge join, and it's time to find out. - * If there are, build an appropriate join RecordSource, - * push it on the rsb stack, and update rivers accordingly. - * If two or more rivers were successfully joined, return true. - * If the whole things is a moby no-op, return false. - * - **************************************/ ULONG selected_rivers[OPT_STREAM_BITS], selected_rivers2[OPT_STREAM_BITS]; ValueExprNode** eq_class; - DEV_BLKCHK(opt, type_opt); - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; // Count the number of "rivers" involved in the operation, then allocate // a scratch block large enough to hold values to compute equality // classes. - const USHORT cnt = (USHORT) org_rivers.getCount(); + const unsigned cnt = (unsigned) org_rivers.getCount(); if (cnt < 2) return false; HalfStaticArray scratch; - scratch.grow(opt->opt_base_conjuncts * cnt); + scratch.grow(baseConjuncts * cnt); ValueExprNode** classes = scratch.begin(); // Compute equivalence classes among streams. This involves finding groups // of streams joined by field equalities. ValueExprNode** last_class = classes; - OptimizerBlk::opt_conjunct* tail = opt->opt_conjuncts.begin(); - const OptimizerBlk::opt_conjunct* const end = tail + opt->opt_base_conjuncts; - for (; tail < end; tail++) + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) { - if (tail->opt_conjunct_flags & opt_conjunct_used) + if (iter & CONJUNCT_USED) continue; - BoolExprNode* const node = tail->opt_conjunct_node; - ComparativeBoolNode* cmpNode = nodeAs(node); + const auto cmpNode = nodeAs(*iter); if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv)) continue; @@ -2875,7 +2302,7 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv { if (!DSC_EQUIV(&result, &desc1, true)) { - CastNode* cast = FB_NEW_POOL(*tdbb->getDefaultPool()) CastNode(*tdbb->getDefaultPool()); + const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); cast->source = node1; cast->castDesc = result; cast->impureOffset = csb->allocImpure(); @@ -2884,7 +2311,7 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv if (!DSC_EQUIV(&result, &desc2, true)) { - CastNode* cast = FB_NEW_POOL(*tdbb->getDefaultPool()) CastNode(*tdbb->getDefaultPool()); + const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); cast->source = node2; cast->castDesc = result; cast->impureOffset = csb->allocImpure(); @@ -2892,11 +2319,9 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv } } - USHORT number1 = 0; - - for (River** iter1 = org_rivers.begin(); iter1 < org_rivers.end(); iter1++, number1++) + for (unsigned i = 0; i < org_rivers.getCount(); i++) { - River* const river1 = *iter1; + const auto river1 = org_rivers[i]; if (!river1->isReferenced(node1)) { @@ -2908,25 +2333,23 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv node2 = temp; } - USHORT number2 = number1 + 1; - - for (River** iter2 = iter1 + 1; iter2 < org_rivers.end(); iter2++, number2++) + for (unsigned j = i + 1; j < org_rivers.getCount(); j++) { - River* const river2 = *iter2; + const auto river2 = org_rivers[j]; if (river2->isReferenced(node2)) { for (eq_class = classes; eq_class < last_class; eq_class += cnt) { - if (node_equality(node1, classes[number1]) || - node_equality(node2, classes[number2])) + if (fieldEqual(node1, classes[i]) || + fieldEqual(node2, classes[j])) { break; } } - eq_class[number1] = node1; - eq_class[number2] = node2; + eq_class[i] = node1; + eq_class[j] = node2; if (eq_class == last_class) last_class += cnt; @@ -2939,23 +2362,23 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv // Obviously, if the set of classes is empty, return false // to indicate that nothing could be done. - USHORT river_cnt = 0; + unsigned river_cnt = 0; HalfStaticArray selected_classes(cnt); for (eq_class = classes; eq_class < last_class; eq_class += cnt) { - USHORT i = river_count(cnt, eq_class); + unsigned i = getRiverCount(cnt, eq_class); if (i > river_cnt) { river_cnt = i; selected_classes.shrink(0); selected_classes.add(eq_class); - class_mask(cnt, eq_class, selected_rivers); + classMask(cnt, eq_class, selected_rivers); } else { - class_mask(cnt, eq_class, selected_rivers2); + classMask(cnt, eq_class, selected_rivers2); for (i = 0; i < OPT_STREAM_BITS; i++) { @@ -2988,8 +2411,8 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv // AB: Get the lowest river position from the rivers that are merged RiverList rivers_to_merge; - USHORT lowest_river_position = MAX_USHORT; - USHORT number = 0; + unsigned lowest_river_position = MAX_ULONG; + unsigned number = 0; for (River** iter = org_rivers.begin(); iter < org_rivers.end(); number++) { @@ -3009,11 +2432,11 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv // Apply local river booleans, if any - RecordSource* rsb = river->applyLocalBoolean(opt); + auto rsb = applyLocalBoolean(river); // Collect RSBs and keys to join - SortNode* const key = FB_NEW_POOL(*tdbb->getDefaultPool()) SortNode(*tdbb->getDefaultPool()); + const auto key = FB_NEW_POOL(getPool()) SortNode(getPool()); if (prefer_merge_over_hash) { @@ -3029,7 +2452,7 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv StreamList streams; streams.assign(river->getStreams()); - rsb = OPT_gen_sort(tdbb, opt->opt_csb, streams, NULL, rsb, key, opt->favorFirstRows, false); + rsb = generateSort(streams, nullptr, rsb, key, favorFirstRows(), false); } else { @@ -3062,45 +2485,43 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv // Build a join stream - RecordSource* rsb = NULL; + RecordSource* rsb = nullptr; if (prefer_merge_over_hash) { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) + rsb = FB_NEW_POOL(getPool()) MergeJoin(csb, rsbs.getCount(), (SortedStream**) rsbs.begin(), keys.begin()); } else { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) + rsb = FB_NEW_POOL(getPool()) HashJoin(tdbb, csb, rsbs.getCount(), rsbs.begin(), keys.begin()); } // Activate streams of all the rivers being merged - for (River** iter = rivers_to_merge.begin(); iter < rivers_to_merge.end(); iter++) - (*iter)->activate(csb); + for (const auto river : rivers_to_merge) + river->activate(csb); // Pick up any boolean that may apply - BoolExprNode* boolean = NULL; + BoolExprNode* boolean = nullptr; - for (tail = opt->opt_conjuncts.begin(); tail < end; tail++) + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) { - compose(*tdbb->getDefaultPool(), &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; } } if (boolean) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) FilteredStream(csb, rsb, boolean); + rsb = FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean); - River* const merged_river = FB_NEW_POOL(*tdbb->getDefaultPool()) River(csb, rsb, rivers_to_merge); + const auto merged_river = FB_NEW_POOL(getPool()) River(csb, rsb, rivers_to_merge); org_rivers.insert(lowest_river_position, merged_river); @@ -3108,87 +2529,460 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv } -static double get_cardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) +// +// Find all indexed relationships between streams, +// then form streams into rivers (combinations of streams) +// + +void Optimizer::generateInnerJoin(StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause) { -/************************************** - * - * g e t _ c a r d i n a l i t y - * - ************************************** - * - * Functional description - * Return the estimated cardinality for - * the given relation. - * - **************************************/ - SET_TDBB(tdbb); + if (streams.isEmpty()) + return; - if (relation->isVirtual()) + if (planClause && streams.getCount() > 1) { - // Just a dumb estimation - return 100.0; + // this routine expects a join/merge + formRivers(streams, rivers, sortClause, planClause); + return; } - if (relation->rel_file) + const auto count = streams.getCount(); + + InnerJoin innerJoin(tdbb, this, streams, + (sortClause ? *sortClause : nullptr), + (planClause != nullptr)); + + StreamList joinedStreams; + while (innerJoin.findJoinOrder(joinedStreams)) { - return EXT_cardinality(tdbb, relation); + if (!formRiver(count, streams, joinedStreams, rivers, sortClause)) + break; } - - MET_post_existence(tdbb, relation); - const double cardinality = DPM_cardinality(tdbb, relation, format); - MET_release_existence(tdbb, relation); - - return cardinality; } -static BoolExprNode* make_inference_node(CompilerScratch* csb, BoolExprNode* boolean, - ValueExprNode* arg1, ValueExprNode* arg2) -{ -/************************************** - * - * m a k e _ i n f e r e n c e _ n o d e - * - ************************************** - * - * Defined - * 1996-Jan-15 David Schnepper - * - * Functional description - * From the predicate, boolean, and infer a new - * predicate using arg1 & arg2 as the first two - * parameters to the predicate. - * - * This is used when the engine knows A(boolean); +RecordSource* Optimizer::generateOuterJoin(RiverList& rivers, + SortNode** sortClause) +{ + struct { + RecordSource* stream_rsb; + StreamType stream_num; + } stream_o, stream_i, *stream_ptr[2]; + + // Determine which stream should be outer and which is inner. + // In the case of a left join, the syntactically left stream is the + // outer, and the right stream is the inner. For all others, swap + // the sense of inner and outer, though for a full join it doesn't + // matter and we should probably try both orders to see which is + // more efficient. + if (rse->rse_jointype != blr_left) + { + stream_ptr[1] = &stream_o; + stream_ptr[0] = &stream_i; + } + else + { + stream_ptr[0] = &stream_o; + stream_ptr[1] = &stream_i; + } + + // Loop through the outer join sub-streams in + // reverse order because rivers may have been PUSHed + for (int i = 1; i >= 0; i--) + { + const auto node = rse->rse_relations[i]; + + if (nodeIs(node) || nodeIs(node)) + { + stream_ptr[i]->stream_rsb = nullptr; + stream_ptr[i]->stream_num = node->getStream(); + } + else + { + River* const river = rivers.pop(); + stream_ptr[i]->stream_rsb = river->getRecordSource(); + } + } + + if (!isFullJoin()) + { + // Generate rsbs for the sub-streams. + // For the left sub-stream we also will get a boolean back. + BoolExprNode* boolean = nullptr; + + if (!stream_o.stream_rsb) + { + stream_o.stream_rsb = + generateRetrieval(stream_o.stream_num, sortClause, true, false, &boolean); + } + + if (!stream_i.stream_rsb) + { + // AB: the sort clause for the inner stream of an OUTER JOIN + // should never be used for the index retrieval + stream_i.stream_rsb = + generateRetrieval(stream_i.stream_num, nullptr, false, true, nullptr); + } + + // generate a parent boolean rsb for any remaining booleans that + // were not satisfied via an index lookup + stream_i.stream_rsb = generateResidualBoolean(stream_i.stream_rsb); + + // Allocate and fill in the rsb + return FB_NEW_POOL(getPool()) + NestedLoopJoin(csb, stream_o.stream_rsb, stream_i.stream_rsb, + boolean, OUTER_JOIN); + } + + bool hasOuterRsb = true, hasInnerRsb = true; + BoolExprNode* boolean = nullptr; + + if (!stream_o.stream_rsb) + { + hasOuterRsb = false; + stream_o.stream_rsb = + generateRetrieval(stream_o.stream_num, nullptr, true, false, &boolean); + } + + if (!stream_i.stream_rsb) + { + hasInnerRsb = false; + stream_i.stream_rsb = + generateRetrieval(stream_i.stream_num, nullptr, false, true, nullptr); + } + + const auto innerRsb = generateResidualBoolean(stream_i.stream_rsb); + + const auto rsb1 = FB_NEW_POOL(getPool()) + NestedLoopJoin(csb, stream_o.stream_rsb, innerRsb, boolean, OUTER_JOIN); + + for (auto iter = getConjuncts(); iter.hasData(); ++iter) + { + if (iter & CONJUNCT_USED) + iter.reset(CMP_clone_node_opt(tdbb, csb, iter)); + } + + if (!hasInnerRsb) + csb->csb_rpt[stream_i.stream_num].deactivate(); + + if (!hasOuterRsb) + csb->csb_rpt[stream_o.stream_num].deactivate(); + + boolean = nullptr; + + if (!hasInnerRsb) + { + stream_i.stream_rsb = + generateRetrieval(stream_i.stream_num, nullptr, true, false, &boolean); + } + + if (!hasOuterRsb) + { + stream_o.stream_rsb = + generateRetrieval(stream_o.stream_num, nullptr, false, false, nullptr); + } + + const auto outerRsb = generateResidualBoolean(stream_o.stream_rsb); + + const auto rsb2 = FB_NEW_POOL(getPool()) + NestedLoopJoin(csb, stream_i.stream_rsb, outerRsb, boolean, ANTI_JOIN); + + return FB_NEW_POOL(getPool()) FullOuterJoin(csb, rsb1, rsb2); +} + + +// +// Pick up any residual boolean remaining, meaning those that have not been used +// as part of some join. These booleans must still be applied to the result stream. +// + +RecordSource* Optimizer::generateResidualBoolean(RecordSource* rsb) +{ + BoolExprNode* boolean = nullptr; + + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED)) + { + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; + } + } + + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean) : rsb; +} + + +// +// Compile a record retrieval source +// + +RecordSource* Optimizer::generateRetrieval(StreamType stream, + SortNode** sortClause, + bool outerFlag, + bool innerFlag, + BoolExprNode** returnBoolean) +{ + const auto tail = &csb->csb_rpt[stream]; + const auto relation = tail->csb_relation; + fb_assert(relation); + + const string alias = makeAlias(stream); + tail->activate(); + + // Time to find inversions. For each index on the relation + // match all unused booleans against the index looking for upper + // and lower bounds that can be computed by the index. When + // all unused conjunctions are exhausted, see if there is enough + // information for an index retrieval. If so, build up an + // inversion component of the boolean. + + RecordSource* rsb = nullptr; + InversionNode* inversion = nullptr; + BoolExprNode* condition = nullptr; + Array dbkeyRanges; + + if (relation->rel_file) + { + // External table + rsb = FB_NEW_POOL(getPool()) ExternalTableScan(csb, alias, stream, relation); + } + else if (relation->isVirtual()) + { + // Virtual table: monitoring or security + switch (relation->rel_id) + { + case rel_global_auth_mapping: + rsb = FB_NEW_POOL(getPool()) GlobalMappingScan(csb, alias, stream, relation); + break; + + case rel_sec_users: + case rel_sec_user_attributes: + rsb = FB_NEW_POOL(getPool()) UsersTableScan(csb, alias, stream, relation); + break; + + case rel_sec_db_creators: + rsb = FB_NEW_POOL(getPool()) DbCreatorsScan(csb, alias, stream, relation); + break; + + case rel_time_zones: + rsb = FB_NEW_POOL(getPool()) TimeZonesTableScan(csb, alias, stream, relation); + break; + + case rel_config: + rsb = FB_NEW_POOL(getPool()) ConfigTableScan(csb, alias, stream, relation); + break; + + case rel_keywords: + rsb = FB_NEW_POOL(getPool()) KeywordsTableScan(csb, alias, stream, relation); + break; + + default: + rsb = FB_NEW_POOL(getPool()) MonitoringTableScan(csb, alias, stream, relation); + break; + } + } + else + { + // Persistent table + Retrieval retrieval(tdbb, this, stream, outerFlag, innerFlag, + (sortClause ? *sortClause : nullptr), false); + const auto candidate = retrieval.getInversion(); + + if (candidate) + { + inversion = candidate->inversion; + condition = candidate->condition; + dbkeyRanges.assign(candidate->dbkeyRanges); + + // Just for safety sake, this condition must be already checked + // inside OptimizerRetrieval::matchOnIndexes() + + if (inversion && condition && + !condition->computable(csb, stream, false)) + { + fb_assert(false); + inversion = nullptr; + condition = nullptr; + dbkeyRanges.clear(); + } + } + + const auto navigation = retrieval.getNavigation(); + + if (navigation) + { + if (sortClause) + *sortClause = nullptr; + + navigation->setInversion(inversion, condition); + + rsb = navigation; + } + } + + if (outerFlag) + { + fb_assert(returnBoolean); + *returnBoolean = nullptr; + + // Now make another pass thru the outer conjuncts only, finding unused, + // computable booleans. When one is found, roll it into a final + // boolean and mark it used. + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) + { + compose(getPool(), returnBoolean, iter); + iter |= CONJUNCT_USED; + } + } + } + + // Now make another pass thru the conjuncts finding unused, computable + // booleans. When one is found, roll it into a final boolean and mark + // it used. If a computable boolean didn't match against an index then + // mark the stream to denote unmatched booleans. + BoolExprNode* boolean = nullptr; + + for (auto iter = getConjuncts(innerFlag, outerFlag); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) + { + // If inversion is available, utilize all conjuncts that refer to + // the stream being retrieved. Otherwise, utilize only conjuncts + // that are local to this stream. The remaining ones are left in piece + // as possible candidates for a merge/hash join. + + if ((inversion && iter->containsStream(stream)) || + (!inversion && iter->computable(csb, stream, true))) + { + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; + + if (!outerFlag && !(iter & CONJUNCT_MATCHED)) + tail->csb_flags |= csb_unmatched; + } + } + } + + if (!rsb) + { + if (inversion && condition) + { + RecordSource* const rsb1 = + FB_NEW_POOL(getPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); + RecordSource* const rsb2 = + FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, inversion); + + rsb = FB_NEW_POOL(getPool()) ConditionalStream(csb, rsb1, rsb2, condition); + } + else if (inversion) + { + rsb = FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, inversion); + } + else + { + rsb = FB_NEW_POOL(getPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); + + if (boolean) + csb->csb_rpt[stream].csb_flags |= csb_unmatched; + } + } + + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean) : rsb; +} + + +// +// Make an alias string suitable for printing as part of the plan. +// For views, this means multiple aliases to distinguish the base table. +// + +string Optimizer::makeAlias(StreamType stream) +{ + string alias; + + const CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; + + if (csb_tail->csb_view || csb_tail->csb_alias) + { + ObjectsArray alias_list; + + while (csb_tail) + { + if (csb_tail->csb_alias) + alias_list.push(*csb_tail->csb_alias); + else if (csb_tail->csb_relation) + alias_list.push(csb_tail->csb_relation->rel_name.c_str()); + + if (!csb_tail->csb_view) + break; + + csb_tail = &csb->csb_rpt[csb_tail->csb_view_stream]; + } + + while (alias_list.hasData()) + { + alias += alias_list.pop(); + + if (alias_list.hasData()) + alias += ' '; + } + } + else if (csb_tail->csb_relation) + alias = csb_tail->csb_relation->rel_name.c_str(); + else if (csb_tail->csb_procedure) + alias = csb_tail->csb_procedure->getName().toString(); + //// TODO: LocalTableSourceNode + else + fb_assert(false); + + return alias; +} + + +// +// From the predicate, boolean, and infer a new predicate using arg1 & arg2 as the first two +// parameters to the predicate. +// +// This is used when the engine knows A(boolean); fb_assert(cmpNode); // see our caller // Clone the input predicate - ComparativeBoolNode* newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode( - csb->csb_pool, cmpNode->blrOp); + const auto newCmpNode = + FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), cmpNode->blrOp); // We may safely copy invariantness flag because // (1) we only distribute field equalities @@ -3217,196 +3011,15 @@ static BoolExprNode* make_inference_node(CompilerScratch* csb, BoolExprNode* boo } -static bool map_equal(const ValueExprNode* field1, const ValueExprNode* field2, const MapNode* map) +// +// Optimize a LIKE/SIMILAR expression, if possible, into a "STARTING WITH" AND a "LIKE/SIMILAR". +// This will allow us to use the index for the starting with, and the LIKE/SIMILAR can just tag +// along for the ride. But on the ride it does useful work, consider match LIKE/SIMILAR "ab%c". +// This is optimized by adding AND STARTING WITH "ab", but the LIKE/SIMILAR clause is still needed. +// + +ValueExprNode* Optimizer::optimizeLikeSimilar(ComparativeBoolNode* cmpNode) { -/************************************** - * - * m a p _ e q u a l - * - ************************************** - * - * Functional description - * Test to see if two fields are equal, where the fields - * are in two different streams possibly mapped to each other. - * Order of the input fields is important. - * - **************************************/ - const FieldNode* fieldNode1 = nodeAs(field1); - const FieldNode* fieldNode2 = nodeAs(field2); - - if (!fieldNode1 || !fieldNode2) - return false; - - // look through the mapping and see if we can find an equivalence. - const NestConst* sourcePtr = map->sourceList.begin(); - const NestConst* targetPtr = map->targetList.begin(); - - for (const NestConst* const sourceEnd = map->sourceList.end(); - sourcePtr != sourceEnd; - ++sourcePtr, ++targetPtr) - { - const FieldNode* mapFrom = nodeAs(*sourcePtr); - const FieldNode* mapTo = nodeAs(*targetPtr); - - if (!mapFrom || !mapTo) - continue; - - if (fieldNode1->fieldStream != mapFrom->fieldStream || fieldNode1->fieldId != mapFrom->fieldId) - continue; - - if (fieldNode2->fieldStream != mapTo->fieldStream || fieldNode2->fieldId != mapTo->fieldId) - continue; - - return true; - } - - return false; -} - - -// Mark indices that were not included in the user-specified access plan. -static void mark_indices(CompilerScratch::csb_repeat* tail, SSHORT relationId) -{ - const PlanNode* const plan = tail->csb_plan; - - if (plan->type != PlanNode::TYPE_RETRIEVE) - return; - - // Go through each of the indices and mark it unusable - // for indexed retrieval unless it was specifically mentioned - // in the plan; also mark indices for navigational access. - - // If there were none indices, this is a sequential retrieval. - - index_desc* idx = tail->csb_idx->items; - - for (USHORT i = 0; i < tail->csb_indices; i++) - { - if (plan->accessType) - { - ObjectsArray::iterator arg = plan->accessType->items.begin(); - const ObjectsArray::iterator end = plan->accessType->items.end(); - - for (; arg != end; ++arg) - { - if (relationId != arg->relationId) - { - // index %s cannot be used in the specified plan - ERR_post(Arg::Gds(isc_index_unused) << arg->indexName); - } - - if (idx->idx_id == arg->indexId) - { - if (plan->accessType->type == PlanNode::AccessType::TYPE_NAVIGATIONAL && - arg == plan->accessType->items.begin()) - { - // dimitr: navigational access can use only one index, - // hence the extra check added (see the line above) - idx->idx_runtime_flags |= idx_plan_navigate; - } - else - { - // nod_indices - break; - } - } - } - - if (arg == end) - idx->idx_runtime_flags |= idx_plan_dont_use; - } - else - idx->idx_runtime_flags |= idx_plan_dont_use; - - ++idx; - } -} - - -// Test two field node pointers for symbolic equality. - -static bool node_equality(const ValueExprNode* node1, const ValueExprNode* node2) -{ - if (!node1 || !node2) - return false; - - if (node1->getType() != node2->getType()) - return false; - - if (node1 == node2) - return true; - - const FieldNode* fieldNode1 = nodeAs(node1); - const FieldNode* fieldNode2 = nodeAs(node2); - - if (fieldNode1 && fieldNode2) - { - return fieldNode1->fieldStream == fieldNode2->fieldStream && - fieldNode1->fieldId == fieldNode2->fieldId; - } - - return false; -} - -static bool node_equality(const BoolExprNode* node1, const BoolExprNode* node2) -{ - DEV_BLKCHK(node1, type_nod); - DEV_BLKCHK(node2, type_nod); - - if (!node1 || !node2) - return false; - - if (node1->getType() != node2->getType()) - return false; - - if (node1 == node2) - return true; - - const ComparativeBoolNode* cmpNode = nodeAs(node1); - const ComparativeBoolNode* cmpNode2 = nodeAs(node2); - - if (cmpNode && cmpNode2 && cmpNode->blrOp == cmpNode2->blrOp && - (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) - { - if (node_equality(cmpNode->arg1, cmpNode2->arg1) && - node_equality(cmpNode->arg2, cmpNode2->arg2)) - { - return true; - } - - if (node_equality(cmpNode->arg1, cmpNode2->arg2) && - node_equality(cmpNode->arg2, cmpNode2->arg1)) - { - return true; - } - } - - return false; -} - - -static ValueExprNode* optimize_like_similar(thread_db* tdbb, CompilerScratch* csb, ComparativeBoolNode* cmpNode) -{ -/************************************** - * - * o p t i m i z e _ l i k e _ s i m i l a r - * - ************************************** - * - * Functional description - * Optimize a LIKE/SIMILAR expression, if possible, - * into a "starting with" AND a "LIKE/SIMILAR". This - * will allow us to use the index for the - * starting with, and the LIKE/SIMILAR can just tag - * along for the ride. - * But on the ride it does useful work, consider - * match LIKE/SIMILAR "ab%c". This is optimized by adding - * AND starting_with "ab", but the LIKE/SIMILAR clause is - * still needed. - * - **************************************/ - SET_TDBB(tdbb); - ValueExprNode* matchNode = cmpNode->arg1; ValueExprNode* patternNode = cmpNode->arg2; ValueExprNode* escapeNode = cmpNode->arg3; @@ -3483,9 +3096,9 @@ static ValueExprNode* optimize_like_similar(thread_db* tdbb, CompilerScratch* cs // allocate a literal node to store the starting with string; // assume it will be shorter than the pattern string - LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); + const auto literal = FB_NEW_POOL(getPool()) LiteralNode(getPool()); literal->litDesc = *patternDesc; - UCHAR* q = literal->litDesc.dsc_address = FB_NEW_POOL(csb->csb_pool) UCHAR[literal->litDesc.dsc_length]; + UCHAR* q = literal->litDesc.dsc_address = FB_NEW_POOL(getPool()) UCHAR[literal->litDesc.dsc_length]; // Set the string length to point till the first wildcard character. @@ -3574,230 +3187,29 @@ static ValueExprNode* optimize_like_similar(thread_db* tdbb, CompilerScratch* cs // Allocate a literal node to store the starting with string. // Use the match text type as the pattern string is converted to it. - LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); + const auto literal = FB_NEW_POOL(getPool()) LiteralNode(getPool()); literal->litDesc.makeText(prefixBuffer.getCount(), INTL_TTYPE(&matchDesc), - FB_NEW_POOL(csb->csb_pool) UCHAR[prefixBuffer.getCount()]); + FB_NEW_POOL(getPool()) UCHAR[prefixBuffer.getCount()]); memcpy(literal->litDesc.dsc_address, prefixBuffer.begin(), prefixBuffer.getCount()); return literal; } } - -static USHORT river_count(USHORT count, ValueExprNode** eq_class) +void Optimizer::printf(const char* format, ...) { -/************************************** - * - * r i v e r _ c o u n t - * - ************************************** - * - * Functional description - * Given an sort/merge join equivalence class (vector of node pointers - * of representative values for rivers), return the count of rivers - * with values. - * - **************************************/ - USHORT cnt = 0; +#ifdef OPT_DEBUG + if (!debugFile) + debugFile = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - for (USHORT i = 0; i < count; i++, eq_class++) - { - if (*eq_class) - { - cnt++; - DEV_BLKCHK(*eq_class, type_nod); - } - } + fb_assert(debugFile); - return cnt; -} - - -static bool search_stack(const ValueExprNode* node, const ValueExprNodeStack& stack) -{ -/************************************** - * - * s e a r c h _ s t a c k - * - ************************************** - * - * Functional description - * Search a stack for the presence of a particular value. - * - **************************************/ - for (ValueExprNodeStack::const_iterator iter(stack); iter.hasData(); ++iter) - { - if (node_equality(node, iter.object())) - return true; - } - - return false; -} - - -static void set_direction(SortNode* fromClause, SortNode* toClause) -{ -/************************************** - * - * s e t _ d i r e c t i o n - * - ************************************** - * - * Functional description - * Update the direction of a GROUP BY, DISTINCT, or ORDER BY - * clause to the same direction as another clause. Do the same - * for the nulls placement flag. - * - **************************************/ - const size_t fromCount = fromClause->expressions.getCount(); - - fb_assert(fromCount <= toClause->expressions.getCount()); - fb_assert(fromCount == fromClause->direction.getCount() && - fromCount == fromClause->nullOrder.getCount()); - fb_assert(toClause->expressions.getCount() == toClause->direction.getCount() && - toClause->expressions.getCount() == toClause->nullOrder.getCount()); - - for (FB_SIZE_T i = 0; i < fromCount; ++i) - { - toClause->direction[i] = fromClause->direction[i]; - toClause->nullOrder[i] = fromClause->nullOrder[i]; - } -} - - -static void set_position(const SortNode* from_clause, SortNode* to_clause, const MapNode* map) -{ -/************************************** - * - * s e t _ p o s i t i o n - * - ************************************** - * - * Functional description - * Update the fields in a GROUP BY, DISTINCT, or ORDER BY - * clause to the same position as another clause, possibly - * using a mapping between the streams. - * - **************************************/ - DEV_BLKCHK(from_clause, type_nod); - - // Track the position in the from list with "to_swap", and find the corresponding - // field in the from list with "to_ptr", then swap the two fields. By the time - // we get to the end of the from list, all fields in the to list will be reordered. - - NestConst* to_swap = to_clause->expressions.begin(); - - // We need to process no more than the number of nodes in the "from" clause - - const size_t count = from_clause->expressions.getCount(); - fb_assert(count <= to_clause->expressions.getCount()); - - const NestConst* from_ptr = from_clause->expressions.begin(); - - for (const NestConst* const from_end = from_ptr + count; - from_ptr != from_end; ++from_ptr) - { - NestConst* to_ptr = to_clause->expressions.begin(); - - for (const NestConst* const to_end = to_ptr + count; - to_ptr != to_end; ++to_ptr) - { - const FieldNode* const fromField = nodeAs(*from_ptr); - const FieldNode* const toField = nodeAs(*to_ptr); - - if ((map && map_equal(*to_ptr, *from_ptr, map)) || - (!map && fromField && toField && - fromField->fieldStream == toField->fieldStream && - fromField->fieldId == toField->fieldId)) - { - ValueExprNode* swap = *to_swap; - *to_swap = *to_ptr; - *to_ptr = swap; - } - } - - ++to_swap; - } - -} - - -static void sort_indices_by_selectivity(CompilerScratch::csb_repeat* tail) -{ -/************************************** - * - * s o r t _ i n d i c e s _ b y _ s e l e c t i v i t y - * - ************************************** - * - * Functional description - * Sort indices based on their selectivity. Lowest selectivy as first, highest as last. - * - **************************************/ - - index_desc* selectedIdx = NULL; - Array idxSort(tail->csb_indices); - bool sameSelectivity = false; - - // Walk through the indices and sort them into into idxSort - // where idxSort[0] contains the lowest selectivity (best) and - // idxSort[csbTail->csb_indices - 1] the highest (worst) - - if (tail->csb_idx && (tail->csb_indices > 1)) - { - for (USHORT j = 0; j < tail->csb_indices; j++) - { - float selectivity = 1; // Maximum selectivity is 1 (when all keys are the same) - index_desc* idx = tail->csb_idx->items; - - for (USHORT i = 0; i < tail->csb_indices; i++) - { - // Prefer ASC indices in the case of almost the same selectivities - if (selectivity > idx->idx_selectivity) - sameSelectivity = ((selectivity - idx->idx_selectivity) <= 0.00001); - else - sameSelectivity = ((idx->idx_selectivity - selectivity) <= 0.00001); - - if (!(idx->idx_runtime_flags & idx_marker) && - (idx->idx_selectivity <= selectivity) && - !((idx->idx_flags & idx_descending) && sameSelectivity)) - { - selectivity = idx->idx_selectivity; - selectedIdx = idx; - } - - ++idx; - } - - // If no index was found than pick the first one available out of the list - if ((!selectedIdx) || (selectedIdx->idx_runtime_flags & idx_marker)) - { - idx = tail->csb_idx->items; - - for (USHORT i = 0; i < tail->csb_indices; i++) - { - if (!(idx->idx_runtime_flags & idx_marker)) - { - selectedIdx = idx; - break; - } - - ++idx; - } - } - - selectedIdx->idx_runtime_flags |= idx_marker; - idxSort.add(*selectedIdx); - } - - // Finally store the right order in cbs_tail->csb_idx - index_desc* idx = tail->csb_idx->items; - - for (USHORT j = 0; j < tail->csb_indices; j++) - { - idx->idx_runtime_flags &= ~idx_marker; - memcpy(idx, &idxSort[j], sizeof(index_desc)); - ++idx; - } - } + va_list arglist; + va_start(arglist, format); + Firebird::string str; + str.vprintf(format, arglist); + va_end(arglist); + + fprintf(debugFile, str.c_str()); +#endif } diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index 4c73ad4d55..dafe816e10 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -32,19 +32,14 @@ //#define OPT_DEBUG //#define OPT_DEBUG_RETRIEVAL -//#ifdef OPT_DEBUG -#define OPTIMIZER_DEBUG_FILE "opt_debug.out" -//#endif - #include "../common/classes/alloc.h" #include "../common/classes/array.h" +#include "../common/classes/fb_string.h" #include "../jrd/RecordSourceNodes.h" -#include "../jrd/rse.h" #include "../jrd/exe.h" namespace Jrd { - // AB: 2005-11-05 // Constants below needs some discussions and ideas const double REDUCE_SELECTIVITY_FACTOR_BETWEEN = 0.0025; @@ -65,261 +60,23 @@ const double THRESHOLD_CARDINALITY = 5.0; // also representing the minimal cost of the index scan. // We assume that the root page would be always cached, // so it's not included here. -const int DEFAULT_INDEX_COST = 3; +const double DEFAULT_INDEX_COST = 3.0; struct index_desc; -class OptimizerBlk; class jrd_rel; class IndexTableScan; class ComparativeBoolNode; class InversionNode; class PlanNode; class SortNode; - -Firebird::string OPT_make_alias(const CompilerScratch*, StreamType stream); - -enum segmentScanType { - segmentScanNone, - segmentScanGreater, - segmentScanLess, - segmentScanBetween, - segmentScanEqual, - segmentScanEquivalent, - segmentScanMissing, - segmentScanStarting -}; - -class IndexScratchSegment -{ -public: - explicit IndexScratchSegment(MemoryPool& p); - IndexScratchSegment(MemoryPool& p, IndexScratchSegment* segment); +class River; +class SortedStream; - ValueExprNode* lowerValue; // lower bound on index value - ValueExprNode* upperValue; // upper bound on index value - bool excludeLower; // exclude lower bound value from scan - bool excludeUpper; // exclude upper bound value from scan - int scope; // highest scope level - segmentScanType scanType; // scan type - - Firebird::Array matches; -}; - -class IndexScratch -{ -public: - IndexScratch(MemoryPool& p, thread_db* tdbb, index_desc* idx, CompilerScratch::csb_repeat* csb_tail); - IndexScratch(MemoryPool& p, const IndexScratch& scratch); - ~IndexScratch(); - - index_desc* idx; // index descriptor - double selectivity; // calculated selectivity for this index - bool candidate; // used when deciding which indices to use - bool scopeCandidate; // used when making inversion based on scope - int lowerCount; // - int upperCount; // - int nonFullMatchedSegments; // - bool fuzzy; // Need to use INTL_KEY_PARTIAL in btr lookups - double cardinality; // Estimated cardinality when using the whole index - - Firebird::Array segments; -}; - -class InversionCandidate -{ -public: - explicit InversionCandidate(MemoryPool& p); - - double selectivity; - double cost; - USHORT nonFullMatchedSegments; - USHORT matchedSegments; - int indexes; - int dependencies; - BoolExprNode* boolean; - BoolExprNode* condition; - InversionNode* inversion; - IndexScratch* scratch; - bool used; - bool unique; - bool navigated; - - Firebird::Array matches; - Firebird::Array dbkeyRanges; - SortedStreamList dependentFromStreams; -}; - -typedef Firebird::HalfStaticArray InversionCandidateList; -typedef Firebird::ObjectsArray IndexScratchList; - -class OptimizerRetrieval -{ -public: - OptimizerRetrieval(MemoryPool& p, OptimizerBlk* opt, StreamType streamNumber, - bool outer, bool inner, SortNode* sortNode); - ~OptimizerRetrieval(); - - MemoryPool& getPool() const - { - return pool; - } - - InversionCandidate* getInversion() - { - createIndexScanNodes = true; - setConjunctionsMatched = true; - - return generateInversion(); - } - - InversionCandidate* getCost() - { - createIndexScanNodes = false; - setConjunctionsMatched = false; - - return generateInversion(); - } - - IndexTableScan* getNavigation(); - -protected: - void analyzeNavigation(const InversionCandidateList& inversions); - bool betterInversion(const InversionCandidate* inv1, const InversionCandidate* inv2, - bool ignoreUnmatched) const; - InversionNode* composeInversion(InversionNode* node1, InversionNode* node2, - InversionNode::Type node_type) const; - const Firebird::string& getAlias(); - InversionCandidate* generateInversion(); - void getInversionCandidates(InversionCandidateList* inversions, - IndexScratchList* indexScratches, USHORT scope) const; - InversionNode* makeIndexScanNode(IndexScratch* indexScratch) const; - InversionCandidate* makeInversion(InversionCandidateList* inversions) const; - bool matchBoolean(IndexScratch* indexScratch, BoolExprNode* boolean, USHORT scope) const; - InversionCandidate* matchDbKey(BoolExprNode* boolean) const; - InversionCandidate* matchOnIndexes(IndexScratchList* indexScratches, - BoolExprNode* boolean, USHORT scope) const; - ValueExprNode* findDbKey(ValueExprNode* dbkey, SLONG* position) const; - -#ifdef OPT_DEBUG_RETRIEVAL - void printCandidate(const InversionCandidate* candidate) const; - void printCandidates(const InversionCandidateList* inversions) const; - void printFinalCandidate(const InversionCandidate* candidate) const; -#endif - - bool validateStarts(IndexScratch* indexScratch, ComparativeBoolNode* cmpNode, - USHORT segment) const; - -private: - MemoryPool& pool; - thread_db* tdbb; - -public: - StreamType stream; - Firebird::string alias; - SortNode* sort; - jrd_rel* relation; - CompilerScratch* csb; - Database* database; - OptimizerBlk* optimizer; - IndexScratchList indexScratches; - InversionCandidateList inversionCandidates; - bool innerFlag; - bool outerFlag; - bool createIndexScanNodes; - bool setConjunctionsMatched; - InversionCandidate* navigationCandidate; -}; - -class IndexRelationship -{ -public: - IndexRelationship() - : stream(0), unique(false), cost(0), cardinality(0) - {} - - StreamType stream; - bool unique; - double cost; - double cardinality; -}; - -typedef Firebird::Array IndexedRelationships; - -class InnerJoinStreamInfo -{ -public: - explicit InnerJoinStreamInfo(MemoryPool& p) - : indexedRelationships(p), - stream(0), baseUnique(false), baseCost(0), baseSelectivity(0), - baseIndexes(0), baseNavigated(false), - used(false), previousExpectedStreams(0) - {} - - bool isIndependent() const - { - // Return true if this stream can't be used by other streams - // and it can't use index retrieval based on other streams - - return (indexedRelationships.isEmpty() && !previousExpectedStreams); - } - - bool isFiltered() const - { - return (baseIndexes || baseSelectivity < MAXIMUM_SELECTIVITY); - } - - IndexedRelationships indexedRelationships; - StreamType stream; - bool baseUnique; - double baseCost; - double baseSelectivity; - int baseIndexes; - bool baseNavigated; - bool used; - int previousExpectedStreams; -}; - -typedef Firebird::HalfStaticArray StreamInfoList; - -class OptimizerInnerJoin -{ -public: - OptimizerInnerJoin(MemoryPool& p, OptimizerBlk* opt, const StreamList& streams, - SortNode* sort_clause, PlanNode* plan_clause); - ~OptimizerInnerJoin(); - - StreamType findJoinOrder(); - -protected: - void calculateStreamInfo(); - bool cheaperRelationship(IndexRelationship* checkRelationship, - IndexRelationship* withRelationship) const; - void estimateCost(StreamType stream, double* cost, double* resulting_cardinality, bool start) const; - void findBestOrder(StreamType position, InnerJoinStreamInfo* stream, - IndexedRelationships* processList, double cost, double cardinality); - void getIndexedRelationships(InnerJoinStreamInfo* testStream); - InnerJoinStreamInfo* getStreamInfo(StreamType stream); -#ifdef OPT_DEBUG - void printBestOrder() const; - void printFoundOrder(StreamType position, double positionCost, - double positionCardinality, double cost, double cardinality) const; - void printProcessList(const IndexedRelationships* processList, StreamType stream) const; - void printStartOrder() const; -#endif - -private: - MemoryPool& pool; - thread_db* tdbb; - SortNode* sort; - PlanNode* plan; - CompilerScratch* csb; - Database* database; - OptimizerBlk* optimizer; - StreamInfoList innerStreams; - StreamType remainingStreams; -}; +// +// StreamStateHolder +// class StreamStateHolder { @@ -354,16 +111,16 @@ public: } } - void activate() + void activate(bool subStream = false) { - for (const StreamType* iter = m_streams.begin(); iter != m_streams.end(); ++iter) - m_csb->csb_rpt[*iter].activate(); + for (const auto stream : m_streams) + m_csb->csb_rpt[stream].activate(subStream); } void deactivate() { - for (const StreamType* iter = m_streams.begin(); iter != m_streams.end(); ++iter) - m_csb->csb_rpt[*iter].deactivate(); + for (const auto stream : m_streams) + m_csb->csb_rpt[stream].deactivate(); } private: @@ -385,6 +142,620 @@ private: Firebird::HalfStaticArray m_flags; }; + +// +// River +// + +typedef Firebird::HalfStaticArray RiverList; + +class River +{ +public: + River(CompilerScratch* csb, RecordSource* rsb, RecordSourceNode* node, const StreamList& streams) + : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) + { + if (node) + m_nodes.add(node); + + m_streams.assign(streams); + } + + River(CompilerScratch* csb, RecordSource* rsb, RiverList& rivers) + : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) + { + for (const auto subRiver : rivers) + { + m_nodes.join(subRiver->m_nodes); + m_streams.join(subRiver->m_streams); + } + } + + RecordSource* getRecordSource() const + { + return m_rsb; + } + + const StreamList& getStreams() const + { + return m_streams; + } + + void activate(CompilerScratch* csb) const + { + for (const auto stream : m_streams) + csb->csb_rpt[stream].activate(); + } + + void deactivate(CompilerScratch* csb) const + { + for (const auto stream : m_streams) + csb->csb_rpt[stream].deactivate(); + } + + bool isReferenced(const ExprNode* node) const + { + SortedStreamList nodeStreams; + node->collectStreams(nodeStreams); + + if (!nodeStreams.hasData()) + return false; + + for (const auto stream : nodeStreams) + { + if (!m_streams.exist(stream)) + return false; + } + + return true; + } + + bool isComputable(CompilerScratch* csb) const + { + for (const auto node : m_nodes) + { + if (!node->computable(csb, INVALID_STREAM, false)) + return false; + } + + return true; + } + +protected: + RecordSource* m_rsb; + Firebird::HalfStaticArray m_nodes; + StreamList m_streams; +}; + + +// +// Optimizer +// + +class Optimizer : public Firebird::PermanentStorage +{ +public: + struct Conjunct + { + // Conjunctions and their options + BoolExprNode* node; + unsigned flags; + }; + + static const unsigned CONJUNCT_USED = 1; // conjunct is used + static const unsigned CONJUNCT_MATCHED = 2; // conjunct matches an index segment + + typedef Firebird::HalfStaticArray ConjunctList; + + class ConjunctIterator + { + friend class Optimizer; + + public: + operator BoolExprNode*() const + { + return iter->node; + } + + BoolExprNode* operator->() const + { + return iter->node; + } + + BoolExprNode* operator*() const + { + return iter->node; + } + + unsigned operator&(unsigned flags) const + { + return (iter->flags & flags); + } + + void operator|=(unsigned flags) + { + iter->flags |= flags; + } + + void operator++() + { + iter++; + } + + bool hasData() const + { + return (iter < end); + } + + void rewind() + { + iter = begin; + } + + void reset(BoolExprNode* node) + { + iter->node = node; + iter->flags = 0; + } + + private: + Conjunct* const begin; + const Conjunct* const end; + Conjunct* iter; + + ConjunctIterator(Conjunct* _begin, const Conjunct* _end) + : begin(_begin), end(_end) + { + rewind(); + } + + explicit ConjunctIterator(const ConjunctIterator& other) + : begin(other.begin), end(other.end), iter(other.iter) + {} + }; + + ConjunctIterator getBaseConjuncts() + { + const auto begin = conjuncts.begin(); + const auto end = begin + baseConjuncts; + + return ConjunctIterator(begin, end); + } + + ConjunctIterator getConjuncts(bool inner = false, bool outer = false) + { + const auto begin = conjuncts.begin() + (outer ? baseParentConjuncts : 0); + const auto end = inner ? begin + baseMissingConjuncts : conjuncts.end(); + + return ConjunctIterator(begin, end); + } + + static Firebird::string getPlan(thread_db* tdbb, const JrdStatement* statement, bool detailed) + { + return statement ? statement->getPlan(tdbb, detailed) : ""; + } + + static RecordSource* compile(thread_db* tdbb, + CompilerScratch* csb, + RseNode* rse, + BoolExprNodeStack* parentStack = nullptr) + { + return Optimizer(tdbb, csb, rse).compile(parentStack); + } + + ~Optimizer(); + + void compileRelation(StreamType stream); + void generateAggregateDistincts(MapNode* map); + SortedStream* generateSort(const StreamList& streams, + const StreamList* dbkeyStreams, + RecordSource* rsb, SortNode* sort, + bool refetchFlag, bool projectFlag); + + CompilerScratch* getCompilerScratch() const + { + return csb; + } + + bool isInnerJoin() const + { + return (rse->rse_jointype == blr_inner); + } + + bool isLeftJoin() const + { + return (rse->rse_jointype == blr_left); + } + + bool isFullJoin() const + { + return (rse->rse_jointype == blr_full); + } + + const StreamList& getOuterStreams() const + { + return outerStreams; + } + + bool favorFirstRows() const + { + return (rse->flags & RseNode::FLAG_OPT_FIRST_ROWS) != 0; + } + + Firebird::string makeAlias(StreamType stream); + void printf(const char* format, ...); + +private: + Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse); + + RecordSource* compile(BoolExprNodeStack* parentStack); + + RecordSource* applyLocalBoolean(const River* river); + void checkIndices(); + void checkSorts(); + unsigned decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack); + unsigned distributeEqualities(BoolExprNodeStack& orgStack, unsigned baseCount); + void findDependentStreams(const StreamList& streams, + StreamList& dependent_streams, + StreamList& free_streams); + bool formRiver(unsigned streamCount, + StreamList& streams, + const StreamList& joinedStreams, + RiverList& rivers, + SortNode** sortClause); + void formRivers(const StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause); + bool generateEquiJoin(RiverList& org_rivers); + void generateInnerJoin(StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause); + RecordSource* generateOuterJoin(RiverList& rivers, + SortNode** sortClause); + RecordSource* generateResidualBoolean(RecordSource* rsb); + RecordSource* generateRetrieval(StreamType stream, + SortNode** sortClause, + bool outerFlag, + bool innerFlag, + BoolExprNode** returnBoolean); + BoolExprNode* makeInferenceNode(BoolExprNode* boolean, + ValueExprNode* arg1, + ValueExprNode* arg2); + ValueExprNode* optimizeLikeSimilar(ComparativeBoolNode* cmpNode); + + thread_db* const tdbb; + CompilerScratch* const csb; + RseNode* const rse; + + FILE* debugFile = nullptr; + unsigned baseConjuncts = 0; // number of conjuncts in our rse, next conjuncts are distributed parent + unsigned baseParentConjuncts = 0; // number of conjuncts in our rse + distributed with parent, next are parent + unsigned baseMissingConjuncts = 0; // number of conjuncts in our and parent rse, but without missing + + StreamList compileStreams, bedStreams, keyStreams, subStreams, outerStreams; + ConjunctList conjuncts; +}; + + +// +// IndexScratch +// + +enum segmentScanType { + segmentScanNone, + segmentScanGreater, + segmentScanLess, + segmentScanBetween, + segmentScanEqual, + segmentScanEquivalent, + segmentScanMissing, + segmentScanStarting +}; + +typedef Firebird::HalfStaticArray MatchedBooleanList; + +struct IndexScratchSegment +{ + explicit IndexScratchSegment(MemoryPool& p) + : matches(p) + {} + + explicit IndexScratchSegment(MemoryPool& p, const IndexScratchSegment& other) + : lowerValue(other.lowerValue), + upperValue(other.upperValue), + excludeLower(other.excludeLower), + excludeUpper(other.excludeUpper), + scope(other.scope), + scanType(other.scanType), + matches(p, other.matches) + {} + + ValueExprNode* lowerValue = nullptr; // lower bound on index value + ValueExprNode* upperValue = nullptr; // upper bound on index value + bool excludeLower = false; // exclude lower bound value from scan + bool excludeUpper = false; // exclude upper bound value from scan + unsigned scope = 0; // highest scope level + segmentScanType scanType = segmentScanNone; // scan type + + MatchedBooleanList matches; // matched booleans +}; + +struct IndexScratch +{ + IndexScratch(MemoryPool& p, index_desc* idx, double cardinality); + IndexScratch(MemoryPool& p, const IndexScratch& other); + + index_desc* index = nullptr; // index descriptor + double cardinality = 0; // estimated cardinality of the whole index + double selectivity = MAXIMUM_SELECTIVITY; // calculated selectivity for this index + bool candidate = false; // used when deciding which indices to use + bool scopeCandidate = false; // used when making inversion based on scope + unsigned lowerCount = 0; + unsigned upperCount = 0; + unsigned nonFullMatchedSegments = 0; + bool fuzzy = false; // Need to use INTL_KEY_PARTIAL in btr lookups + + Firebird::ObjectsArray segments; +}; + +typedef Firebird::ObjectsArray IndexScratchList; + +// +// InversionCandidate +// + +struct InversionCandidate +{ + explicit InversionCandidate(MemoryPool& p) + : matches(p), dbkeyRanges(p), dependentFromStreams(p) + {} + + double selectivity = MAXIMUM_SELECTIVITY; + double cost = 0; + unsigned nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; + unsigned matchedSegments = 0; + unsigned indexes = 0; + unsigned dependencies = 0; + BoolExprNode* boolean = nullptr; + BoolExprNode* condition = nullptr; + InversionNode* inversion = nullptr; + IndexScratch* scratch = nullptr; + bool used = false; + bool unique = false; + bool navigated = false; + + MatchedBooleanList matches; + Firebird::Array dbkeyRanges; + SortedStreamList dependentFromStreams; +}; + +typedef Firebird::HalfStaticArray InversionCandidateList; + + +// +// Retrieval +// + +class Retrieval : private Firebird::PermanentStorage +{ +public: + Retrieval(thread_db* tdbb, Optimizer* opt, StreamType streamNumber, + bool outer, bool inner, SortNode* sortNode, bool costOnly); + + ~Retrieval() + { + for (auto candidate : inversionCandidates) + delete candidate; + } + + InversionCandidate* getInversion(); + IndexTableScan* getNavigation(); + +protected: + void analyzeNavigation(const InversionCandidateList& inversions); + bool betterInversion(const InversionCandidate* inv1, const InversionCandidate* inv2, + bool ignoreUnmatched) const; + InversionNode* composeInversion(InversionNode* node1, InversionNode* node2, + InversionNode::Type node_type) const; + const Firebird::string& getAlias(); + void getInversionCandidates(InversionCandidateList& inversions, + IndexScratchList& indexScratches, unsigned scope) const; + InversionNode* makeIndexScanNode(IndexScratch* indexScratch) const; + InversionCandidate* makeInversion(InversionCandidateList& inversions) const; + bool matchBoolean(IndexScratch* indexScratch, BoolExprNode* boolean, unsigned scope) const; + InversionCandidate* matchDbKey(BoolExprNode* boolean) const; + InversionCandidate* matchOnIndexes(IndexScratchList& indexScratches, + BoolExprNode* boolean, unsigned scope) const; + ValueExprNode* findDbKey(ValueExprNode* dbkey, SLONG* position) const; + bool validateStarts(IndexScratch* indexScratch, ComparativeBoolNode* cmpNode, + unsigned segment) const; + +#ifdef OPT_DEBUG_RETRIEVAL + void printCandidate(const InversionCandidate* candidate) const; + void printCandidates(const InversionCandidateList& inversions) const; + void printFinalCandidate(const InversionCandidate* candidate) const; +#endif + +private: + thread_db* const tdbb; + Optimizer* const optimizer; + CompilerScratch* const csb; + const StreamType stream; + const bool innerFlag; + const bool outerFlag; + SortNode* const sort; + jrd_rel* relation; + const bool createIndexScanNodes; + const bool setConjunctionsMatched; + Firebird::string alias; + IndexScratchList indexScratches; + InversionCandidateList inversionCandidates; + Firebird::AutoPtr finalCandidate; + Firebird::AutoPtr navigationCandidate; +}; + + +// +// InnerJoin +// + +class InnerJoin : private Firebird::PermanentStorage +{ + struct IndexRelationship + { + static bool cheaperThan(const IndexRelationship& item1, const IndexRelationship& item2) + { + if (item1.cost == 0) + return true; + + if (item2.cost == 0) + return false; + + const double compare = item1.cost / item2.cost; + if (compare >= 0.98 && compare <= 1.02) + { + // cost is nearly the same, now check uniqueness and cardinality + + if (item1.unique == item2.unique) + { + if (item1.cardinality < item2.cardinality) + return true; + } + else if (item1.unique) + return true; + else if (item2.unique) + return false; + } + else if (item1.cost < item2.cost) + return true; + + return false; + } + + // Needed for SortedArray + bool operator>(const IndexRelationship& other) const + { + return !cheaperThan(*this, other); + } + + StreamType stream = 0; + bool unique = false; + double cost = 0; + double cardinality = 0; + }; + + typedef Firebird::SortedArray IndexedRelationships; + + class StreamInfo + { + public: + StreamInfo(MemoryPool& p, StreamType streamNumber) + : stream(streamNumber), indexedRelationships(p) + {} + + bool isIndependent() const + { + // Return true if this stream can't be used by other streams + // and it can't use index retrieval based on other streams + + return (indexedRelationships.isEmpty() && !previousExpectedStreams); + } + + bool isFiltered() const + { + return (baseIndexes || baseSelectivity < MAXIMUM_SELECTIVITY); + } + + static bool cheaperThan(const StreamInfo* item1, const StreamInfo* item2) + { + // First those streams which cannot be used by other streams + // or cannot depend on a stream + if (item1->isIndependent() && !item2->isIndependent()) + return true; + + // Next those with the lowest previous expected streams + const int compare = item1->previousExpectedStreams - + item2->previousExpectedStreams; + + if (compare < 0) + return true; + + // Next those with the cheapest base cost + if (item1->baseCost < item2->baseCost) + return true; + + return false; + } + + const StreamType stream; + + bool baseUnique = false; + double baseCost = 0; + double baseSelectivity = 0; + unsigned baseIndexes = 0; + bool baseNavigated = false; + bool used = false; + unsigned previousExpectedStreams = 0; + + IndexedRelationships indexedRelationships; + }; + + typedef Firebird::HalfStaticArray StreamInfoList; + + struct JoinedStreamInfo + { + // Streams and their options + StreamType bestStream; // stream in best join order seen so far + StreamType number; // stream in position of join order + }; + + typedef Firebird::HalfStaticArray JoinedStreamList; + +public: + InnerJoin(thread_db* tdbb, Optimizer* opt, + const StreamList& streams, + SortNode* sort_clause, bool hasPlan); + + ~InnerJoin() + { + for (auto innerStream : innerStreams) + delete innerStream; + } + + bool findJoinOrder(StreamList& bestStreams); + +protected: + void calculateStreamInfo(); + void estimateCost(StreamType stream, double* cost, double* resulting_cardinality, bool start) const; + void findBestOrder(unsigned position, StreamInfo* stream, + IndexedRelationships& processList, double cost, double cardinality); + void getIndexedRelationships(StreamInfo* testStream); + StreamInfo* getStreamInfo(StreamType stream); +#ifdef OPT_DEBUG + void printBestOrder() const; + void printFoundOrder(StreamType position, double positionCost, + double positionCardinality, double cost, double cardinality) const; + void printProcessList(const IndexedRelationships& processList, StreamType stream) const; + void printStartOrder() const; +#endif + +private: + thread_db* const tdbb; + Optimizer* const optimizer; + CompilerScratch* const csb; + SortNode* const sort; + const bool plan; + + unsigned remainingStreams = 0; + unsigned bestCount = 0; // longest length of indexable streams + double bestCost = 0; // cost of best join order + + StreamInfoList innerStreams; + JoinedStreamList joinedStreams; +}; + } // namespace Jrd #endif // OPTIMIZER_H diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index b2bf260624..192fd8b222 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -1,8 +1,4 @@ /* - * PROGRAM: Client/Server Common Code - * MODULE: Optimizer.cpp - * DESCRIPTION: Optimizer - * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the @@ -23,6 +19,7 @@ * All Rights Reserved. * Contributor(s): ______________________________________. * Adriano dos Santos Fernandes + * Dmitry Yemanov * */ @@ -33,15 +30,12 @@ #include "../jrd/btr.h" #include "../jrd/intl.h" #include "../jrd/Collation.h" -#include "../jrd/rse.h" #include "../jrd/ods.h" -#include "../jrd/Optimizer.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/recsrc/RecordSource.h" #include "../dsql/BoolNodes.h" #include "../dsql/ExprNodes.h" #include "../dsql/StmtNodes.h" - #include "../jrd/btr_proto.h" #include "../jrd/cch_proto.h" #include "../jrd/cmp_proto.h" @@ -53,9 +47,12 @@ #include "../jrd/mov_proto.h" #include "../jrd/par_proto.h" +#include "../jrd/optimizer/Optimizer.h" + using namespace Firebird; using namespace Jrd; + namespace { // Check the index for being an expression one and @@ -71,8 +68,8 @@ namespace // so try to recover it (see CORE-4118). while (!idx->idx_expression->sameAs(node, true)) { - DerivedExprNode* const derivedExpr = nodeAs(node); - CastNode* const cast = nodeAs(node); + const auto derivedExpr = nodeAs(node); + const auto cast = nodeAs(node); if (derivedExpr) node = derivedExpr->arg; @@ -147,294 +144,85 @@ namespace } // namespace -namespace Jrd + +IndexScratch::IndexScratch(MemoryPool& p, index_desc* idx, double card) + : index(idx), cardinality(card), segments(p) { - string OPT_make_alias(const CompilerScratch* csb, StreamType stream) - { - // Make an alias string suitable for printing as part of the plan. - // For views, this means multiple aliases to distinguish the base table. - - string alias; - - const CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; - - if (csb_tail->csb_view || csb_tail->csb_alias) - { - ObjectsArray alias_list; - - while (csb_tail) - { - if (csb_tail->csb_alias) - alias_list.push(*csb_tail->csb_alias); - else if (csb_tail->csb_relation) - alias_list.push(csb_tail->csb_relation->rel_name.c_str()); - - if (!csb_tail->csb_view) - break; - - csb_tail = &csb->csb_rpt[csb_tail->csb_view_stream]; - } - - while (alias_list.hasData()) - { - alias += alias_list.pop(); - - if (alias_list.hasData()) - alias += ' '; - } - } - else if (csb_tail->csb_relation) - alias = csb_tail->csb_relation->rel_name.c_str(); - else if (csb_tail->csb_procedure) - alias = csb_tail->csb_procedure->getName().toString(); - //// TODO: LocalTableSourceNode - else - fb_assert(false); - - return alias; - } + segments.resize(index->idx_count); } -IndexScratchSegment::IndexScratchSegment(MemoryPool& p) : - matches(p) +IndexScratch::IndexScratch(MemoryPool& p, const IndexScratch& other) + : index(other.index), + cardinality(other.cardinality), + selectivity(other.selectivity), + candidate(other.candidate), + scopeCandidate(other.scopeCandidate), + lowerCount(other.lowerCount), + upperCount(other.upperCount), + nonFullMatchedSegments(other.nonFullMatchedSegments), + fuzzy(other.fuzzy), + segments(p, other.segments) +{} + + +Retrieval::Retrieval(thread_db* aTdbb, Optimizer* opt, StreamType streamNumber, + bool outer, bool inner, SortNode* sortNode, bool costOnly) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), + optimizer(opt), + csb(opt->getCompilerScratch()), + stream(streamNumber), + innerFlag(inner), + outerFlag(outer), + sort(sortNode), + createIndexScanNodes(!costOnly), + setConjunctionsMatched(!costOnly), + alias(getPool()), + indexScratches(getPool()), + inversionCandidates(getPool()) { -/************************************** - * - * I n d e x S c r a t c h S e g m e n t - * - ************************************** - * - * Functional description - * - **************************************/ - lowerValue = NULL; - upperValue = NULL; - excludeLower = false; - excludeUpper = false; - scope = 0; - scanType = segmentScanNone; -} - -IndexScratchSegment::IndexScratchSegment(MemoryPool& p, IndexScratchSegment* segment) : - matches(p) -{ -/************************************** - * - * I n d e x S c r a t c h S e g m e n t - * - ************************************** - * - * Functional description - * - **************************************/ - lowerValue = segment->lowerValue; - upperValue = segment->upperValue; - excludeLower = segment->excludeLower; - excludeUpper = segment->excludeUpper; - scope = segment->scope; - scanType = segment->scanType; - - for (FB_SIZE_T i = 0; i < segment->matches.getCount(); i++) { - matches.add(segment->matches[i]); - } -} - -IndexScratch::IndexScratch(MemoryPool& p, thread_db* tdbb, index_desc* ix, - CompilerScratch::csb_repeat* csb_tail) : - idx(ix), segments(p) -{ -/************************************** - * - * I n d e x S c r a t c h - * - ************************************** - * - * Functional description - * - **************************************/ - // Allocate needed segments - selectivity = MAXIMUM_SELECTIVITY; - candidate = false; - scopeCandidate = false; - lowerCount = 0; - upperCount = 0; - nonFullMatchedSegments = 0; - fuzzy = false; - - segments.grow(idx->idx_count); - - IndexScratchSegment** segment = segments.begin(); - for (FB_SIZE_T i = 0; i < segments.getCount(); i++) { - segment[i] = FB_NEW_POOL(p) IndexScratchSegment(p); - } - - const int length = ROUNDUP(BTR_key_length(tdbb, csb_tail->csb_relation, idx), sizeof(SLONG)); - - // AB: Calculate the cardinality which should reflect the total number - // of index pages for this index. - // We assume that the average index-key can be compressed by a factor 0.5 - // In the future the average key-length should be stored and retrieved - // from a system table (RDB$INDICES for example). - // Multiplying the selectivity with this cardinality gives the estimated - // number of index pages that are read for the index retrieval. - double factor = 0.5; - if (segments.getCount() > 1) - { - // Compound indexes are generally less compressed. - factor = 0.7; - } - const Database* const dbb = tdbb->getDatabase(); - cardinality = (csb_tail->csb_cardinality * (2 + (length * factor))) / (dbb->dbb_page_size - BTR_SIZE); - cardinality = MAX(cardinality, MINIMUM_CARDINALITY); -} -IndexScratch::IndexScratch(MemoryPool& p, const IndexScratch& scratch) : - segments(p) -{ -/************************************** - * - * I n d e x S c r a t c h - * - ************************************** - * - * Functional description - * - **************************************/ - selectivity = scratch.selectivity; - cardinality = scratch.cardinality; - candidate = scratch.candidate; - scopeCandidate = scratch.scopeCandidate; - lowerCount = scratch.lowerCount; - upperCount = scratch.upperCount; - nonFullMatchedSegments = scratch.nonFullMatchedSegments; - fuzzy = scratch.fuzzy; - idx = scratch.idx; + const auto tail = &csb->csb_rpt[stream]; - // Allocate needed segments - segments.grow(scratch.segments.getCount()); + relation = tail->csb_relation; + fb_assert(relation); - IndexScratchSegment* const* scratchSegment = scratch.segments.begin(); - IndexScratchSegment** segment = segments.begin(); - for (FB_SIZE_T i = 0; i < segments.getCount(); i++) { - segment[i] = FB_NEW_POOL(p) IndexScratchSegment(p, scratchSegment[i]); + if (!tail->csb_idx) + return; + + for (auto& index : *tail->csb_idx) + { + const auto length = ROUNDUP(BTR_key_length(tdbb, relation, &index), sizeof(SLONG)); + + // AB: Calculate the cardinality which should reflect the total number + // of index pages for this index. + // We assume that the average index-key can be compressed by a factor 0.5 + // In the future the average key-length should be stored and retrieved + // from a system table (RDB$INDICES for example). + // Multiplying the selectivity with this cardinality gives the estimated + // number of index pages that are read for the index retrieval. + // Compound indexes are generally less compressed. + const double factor = (index.idx_count == 1) ? 0.5 : 0.7; + + double cardinality = tail->csb_cardinality; + cardinality *= (2 + length * factor); + cardinality /= (dbb->dbb_page_size - BTR_SIZE); + cardinality = MAX(cardinality, MINIMUM_CARDINALITY); + + indexScratches.add(IndexScratch(getPool(), &index, cardinality)); } } -IndexScratch::~IndexScratch() + +// +// Melt two inversions together by the type given in node_type +// + +InversionNode* Retrieval::composeInversion(InversionNode* node1, + InversionNode* node2, + InversionNode::Type node_type) const { -/************************************** - * - * ~I n d e x S c r a t c h - * - ************************************** - * - * Functional description - * - **************************************/ - IndexScratchSegment** segment = segments.begin(); - for (FB_SIZE_T i = 0; i < segments.getCount(); i++) { - delete segment[i]; - } -} - -InversionCandidate::InversionCandidate(MemoryPool& p) : - matches(p), dbkeyRanges(p), dependentFromStreams(p) -{ -/************************************** - * - * I n v e r s i o n C a n d i d a t e - * - ************************************** - * - * Functional description - * - **************************************/ - selectivity = MAXIMUM_SELECTIVITY; - cost = 0; - indexes = 0; - dependencies = 0; - nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; - matchedSegments = 0; - boolean = NULL; - condition = NULL; - inversion = NULL; - scratch = NULL; - used = false; - unique = false; - navigated = false; -} - -OptimizerRetrieval::OptimizerRetrieval(MemoryPool& p, OptimizerBlk* opt, - StreamType streamNumber, bool outer, - bool inner, SortNode* sortNode) - : pool(p), alias(p), indexScratches(p), inversionCandidates(p) -{ -/************************************** - * - * O p t i m i z e r R e t r i e v a l - * - ************************************** - * - * Functional description - * - **************************************/ - createIndexScanNodes = false; - setConjunctionsMatched = false; - - tdbb = NULL; - SET_TDBB(tdbb); - - this->database = tdbb->getDatabase(); - this->stream = streamNumber; - this->optimizer = opt; - this->csb = this->optimizer->opt_csb; - this->innerFlag = inner; - this->outerFlag = outer; - this->sort = sortNode; - this->navigationCandidate = NULL; - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[this->stream]; - relation = csb_tail->csb_relation; - - // Allocate needed indexScratches - - index_desc* idx = csb_tail->csb_idx->items; - for (int i = 0; i < csb_tail->csb_indices; ++i, ++idx) - indexScratches.add(IndexScratch(p, tdbb, idx, csb_tail)); -} - -OptimizerRetrieval::~OptimizerRetrieval() -{ -/************************************** - * - * ~O p t i m i z e r R e t r i e v a l - * - ************************************** - * - * Functional description - * - **************************************/ - if (navigationCandidate) - delete navigationCandidate; - for (FB_SIZE_T i = 0; i < inversionCandidates.getCount(); ++i) - delete inversionCandidates[i]; -} - -InversionNode* OptimizerRetrieval::composeInversion(InversionNode* node1, InversionNode* node2, - InversionNode::Type node_type) const -{ -/************************************** - * - * c o m p o s e I n v e r s i o n - * - ************************************** - * - * Functional description - * Melt two inversions together by the - * type given in node_type. - * - **************************************/ - if (!node2) return node1; @@ -457,82 +245,60 @@ InversionNode* OptimizerRetrieval::composeInversion(InversionNode* node1, Invers } } - return FB_NEW_POOL(pool) InversionNode(node_type, node1, node2); + return FB_NEW_POOL(getPool()) InversionNode(node_type, node1, node2); } -const string& OptimizerRetrieval::getAlias() +const string& Retrieval::getAlias() { -/************************************** - * - * g e t A l i a s - * - ************************************** - * - * Functional description - * - **************************************/ if (alias.isEmpty()) - alias = OPT_make_alias(csb, this->stream); + alias = optimizer->makeAlias(this->stream); return alias; } -InversionCandidate* OptimizerRetrieval::generateInversion() +InversionCandidate* Retrieval::getInversion() { -/************************************** - * - * g e n e r a t e I n v e r s i o n - * - ************************************** - * - * Functional description - * - **************************************/ - OptimizerBlk::opt_conjunct* const opt_begin = - optimizer->opt_conjuncts.begin() + (outerFlag ? optimizer->opt_base_parent_conjuncts : 0); + if (finalCandidate) + return finalCandidate; - const OptimizerBlk::opt_conjunct* const opt_end = - innerFlag ? optimizer->opt_conjuncts.begin() + optimizer->opt_base_missing_conjuncts : - optimizer->opt_conjuncts.end(); + auto iter = optimizer->getConjuncts(innerFlag, outerFlag); - InversionCandidate* invCandidate = NULL; + InversionCandidate* invCandidate = nullptr; if (relation && !relation->rel_file && !relation->isVirtual()) { InversionCandidateList inversions; // First, handle "AND" comparisons (all nodes except OR) - for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) + for (iter.rewind(); iter.hasData(); ++iter) { - BoolExprNode* const node = tail->opt_conjunct_node; - BinaryBoolNode* booleanNode = nodeAs(node); + const auto booleanNode = nodeAs(*iter); - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && node && + if (!(iter & Optimizer::CONJUNCT_USED) && (!booleanNode || booleanNode->blrOp != blr_or)) { - invCandidate = matchOnIndexes(&indexScratches, node, 1); + invCandidate = matchOnIndexes(indexScratches, iter, 1); if (invCandidate) inversions.add(invCandidate); } } - getInversionCandidates(&inversions, &indexScratches, 1); + getInversionCandidates(inversions, indexScratches, 1); // Second, handle "OR" comparisons - for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) + for (iter.rewind(); iter.hasData(); ++iter) { - BoolExprNode* const node = tail->opt_conjunct_node; - BinaryBoolNode* booleanNode = nodeAs(node); + const auto booleanNode = nodeAs(*iter); - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && node && + if (!(iter & Optimizer::CONJUNCT_USED) && (booleanNode && booleanNode->blrOp == blr_or)) { - invCandidate = matchOnIndexes(&indexScratches, node, 1); + invCandidate = matchOnIndexes(indexScratches, iter, 1); if (invCandidate) { - invCandidate->boolean = node; + invCandidate->boolean = iter; inversions.add(invCandidate); } } @@ -543,16 +309,17 @@ InversionCandidate* OptimizerRetrieval::generateInversion() #ifdef OPT_DEBUG_RETRIEVAL // Debug - printCandidates(&inversions); + printCandidates(inversions); #endif - invCandidate = makeInversion(&inversions); + invCandidate = makeInversion(inversions); - // Clean up inversion list - InversionCandidate** inversion = inversions.begin(); - for (FB_SIZE_T i = 0; i < inversions.getCount(); i++) - if (inversion[i] != navigationCandidate) - delete inversion[i]; + // Clean up intermediate inversion candidates + for (const auto candidate : inversions) + { + if (candidate != navigationCandidate) + delete candidate; + } } if (!invCandidate) @@ -560,7 +327,7 @@ InversionCandidate* OptimizerRetrieval::generateInversion() // No index will be used, thus create a dummy inversion candidate // representing the natural table access. All the necessary properties // (selectivity: 1.0, cost: 0, unique: false) are set up by the constructor. - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); } if (invCandidate->unique) @@ -575,46 +342,32 @@ InversionCandidate* OptimizerRetrieval::generateInversion() invCandidate->cost += csb->csb_rpt[stream].csb_cardinality * invCandidate->selectivity; } - // Adjust the effective selectivity by treating computable conjunctions as filters - for (const OptimizerBlk::opt_conjunct* tail = optimizer->opt_conjuncts.begin(); - tail < optimizer->opt_conjuncts.end(); tail++) + for (iter.rewind(); iter.hasData(); ++iter) { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - node->computable(csb, stream, true) && - !invCandidate->matches.exist(node)) + if (!(iter & Optimizer::CONJUNCT_USED)) { - const ComparativeBoolNode* const cmpNode = nodeAs(node); + const auto matched = invCandidate->matches.exist(iter); - const double factor = (cmpNode && cmpNode->blrOp == blr_eql) ? - REDUCE_SELECTIVITY_FACTOR_EQUALITY : REDUCE_SELECTIVITY_FACTOR_INEQUALITY; - invCandidate->selectivity *= factor; + // Adjust the effective selectivity by treating computable conjunctions as filters + if (iter->computable(csb, stream, true) && !matched) + { + const auto cmpNode = nodeAs(*iter); + + const double factor = (cmpNode && cmpNode->blrOp == blr_eql) ? + REDUCE_SELECTIVITY_FACTOR_EQUALITY : REDUCE_SELECTIVITY_FACTOR_INEQUALITY; + invCandidate->selectivity *= factor; + } + + if (setConjunctionsMatched && matched) + iter |= Optimizer::CONJUNCT_MATCHED; } } - // Add the streams where this stream is depending on. - for (FB_SIZE_T i = 0; i < invCandidate->matches.getCount(); i++) - invCandidate->matches[i]->findDependentFromStreams(this, &invCandidate->dependentFromStreams); - - if (setConjunctionsMatched) + // Add the streams where this stream is depending on + for (auto match : invCandidate->matches) { - SortedArray matches; - - // AB: Putting a unsorted array in a sorted array directly by join isn't - // very safe at the moment, but in our case Array holds a sorted list. - // However SortedArray class should be updated to handle join right! - - matches.join(invCandidate->matches); - - for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) - { - if (!(tail->opt_conjunct_flags & opt_conjunct_used)) - { - if (matches.exist(tail->opt_conjunct_node)) - tail->opt_conjunct_flags |= opt_conjunct_matched; - } - } + match->findDependentFromStreams(csb, stream, + &invCandidate->dependentFromStreams); } #ifdef OPT_DEBUG_RETRIEVAL @@ -622,56 +375,42 @@ InversionCandidate* OptimizerRetrieval::generateInversion() printFinalCandidate(invCandidate); #endif + finalCandidate = invCandidate; + return invCandidate; } -IndexTableScan* OptimizerRetrieval::getNavigation() +IndexTableScan* Retrieval::getNavigation() { -/************************************** - * - * g e t N a v i g a t i o n - * - ************************************** - * - * Functional description - * - **************************************/ if (!navigationCandidate) - return NULL; + return nullptr; IndexScratch* const scratch = navigationCandidate->scratch; // Looks like we can do a navigational walk. Flag that // we have used this index for navigation, and allocate // a navigational rsb for it. - scratch->idx->idx_runtime_flags |= idx_navigate; + scratch->index->idx_runtime_flags |= idx_navigate; const USHORT key_length = - ROUNDUP(BTR_key_length(tdbb, relation, scratch->idx), sizeof(SLONG)); + ROUNDUP(BTR_key_length(tdbb, relation, scratch->index), sizeof(SLONG)); InversionNode* const index_node = makeIndexScanNode(scratch); - return FB_NEW_POOL(*tdbb->getDefaultPool()) + return FB_NEW_POOL(getPool()) IndexTableScan(csb, getAlias(), stream, relation, index_node, key_length); } -void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversions) +void Retrieval::analyzeNavigation(const InversionCandidateList& inversions) { -/************************************** - * - * a n a l y z e N a v i g a t i o n - * - ************************************** - * - * Functional description - * - **************************************/ fb_assert(sort); - for (FB_SIZE_T i = 0; i < indexScratches.getCount(); ++i) + HalfStaticArray tempCandidates; + InversionCandidate* bestCandidate = nullptr; + + for (auto& indexScratch : indexScratches) { - IndexScratch* const indexScratch = &indexScratches[i]; - const index_desc* const idx = indexScratch->idx; + auto idx = indexScratch.index; // if the number of fields in the sort is greater than the number of // fields in the index, the index will not be used to optimize the @@ -700,16 +439,14 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi // check to see if the fields in the sort match the fields in the index // in the exact same order - const IndexScratchSegment* const* segment = indexScratch->segments.begin(); - const IndexScratchSegment* const* const end_segment = - segment + MIN(indexScratch->lowerCount, indexScratch->upperCount); - int equalSegments = 0; - - for (; segment < end_segment; segment++) + unsigned equalSegments = 0; + for (unsigned i = 0; i < MIN(indexScratch.lowerCount, indexScratch.upperCount); i++) { - if ((*segment)->scanType == segmentScanEqual || - (*segment)->scanType == segmentScanEquivalent || - (*segment)->scanType == segmentScanMissing) + const auto& segment = indexScratch.segments[i]; + + if (segment.scanType == segmentScanEqual || + segment.scanType == segmentScanEquivalent || + segment.scanType == segmentScanMissing) { equalSegments++; } @@ -735,13 +472,9 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi HalfStaticArray nodes; nodes.add(orgNode); - for (const OptimizerBlk::opt_conjunct* tail = optimizer->opt_conjuncts.begin(); - tail < optimizer->opt_conjuncts.end(); tail++) + for (auto iter = optimizer->getConjuncts(); iter.hasData(); ++iter) { - BoolExprNode* const boolean = tail->opt_conjunct_node; - fb_assert(boolean); - - ComparativeBoolNode* const cmpNode = nodeAs(boolean); + const auto cmpNode = nodeAs(*iter); if (cmpNode && (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) { @@ -758,10 +491,8 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi // Check whether any of the equivalent nodes is suitable for index navigation - for (ValueExprNode** iter = nodes.begin(); iter != nodes.end(); ++iter) + for (const auto node : nodes) { - ValueExprNode* const node = *iter; - if (idx->idx_flags & idx_expressn) { if (!checkExpressionIndex(csb, idx, node, stream)) @@ -775,7 +506,7 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi { for (; idx_tail < idx_end && fieldNode->fieldId != idx_tail->idx_field; idx_tail++) { - const int segmentNumber = idx_tail - idx->idx_rpt; + const unsigned segmentNumber = idx_tail - idx->idx_rpt; if (segmentNumber >= equalSegments) break; @@ -834,14 +565,13 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi // Lookup the inversion candidate matching our navigational index - InversionCandidate* candidate = NULL; + InversionCandidate* candidate = nullptr; - for (InversionCandidate* const* iter = inversions.begin(); - iter != inversions.end(); ++iter) + for (const auto inversion : inversions) { - if ((*iter)->scratch == indexScratch) + if (inversion->scratch == &indexScratch) { - candidate = *iter; + candidate = inversion; break; } } @@ -852,20 +582,16 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi // then don't consider any (possibly better) alternatives. // Another exception is when the FIRST ROWS optimization strategy is applied. - if (candidate && !optimizer->favorFirstRows && - !(indexScratch->idx->idx_runtime_flags & idx_plan_navigate)) + if (candidate && !optimizer->favorFirstRows() && + !(idx->idx_runtime_flags & idx_plan_navigate)) { - for (const InversionCandidate* const* iter = inversions.begin(); - iter != inversions.end(); ++iter) + for (const auto otherCandidate : inversions) { - const InversionCandidate* const otherCandidate = *iter; - if (otherCandidate != candidate) { - for (BoolExprNode* const* iter2 = otherCandidate->matches.begin(); - iter2 != otherCandidate->matches.end(); ++iter2) + for (const auto otherMatch : otherCandidate->matches) { - if (candidate->matches.exist(*iter2) && + if (candidate->matches.exist(otherMatch) && betterInversion(otherCandidate, candidate, true)) { usableIndex = false; @@ -889,24 +615,34 @@ void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversi { // If no inversion candidate is found, create a fake one representing full index scan - candidate = FB_NEW_POOL(pool) InversionCandidate(pool); - candidate->cost = indexScratch->cardinality; + candidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + candidate->cost = indexScratch.cardinality; candidate->indexes = 1; - candidate->scratch = indexScratch; - candidate->nonFullMatchedSegments = (int) indexScratch->segments.getCount(); + candidate->scratch = &indexScratch; + candidate->nonFullMatchedSegments = indexScratch.segments.getCount(); + tempCandidates.add(candidate); } - if (!navigationCandidate || - betterInversion(candidate, navigationCandidate, false)) + if (!bestCandidate || + betterInversion(candidate, bestCandidate, false)) { - navigationCandidate = candidate; + bestCandidate = candidate; } } + + // Clean up intermediate inversion candidates + for (const auto candidate : tempCandidates) + { + if (candidate != bestCandidate) + delete candidate; + } + + navigationCandidate = bestCandidate; } -bool OptimizerRetrieval::betterInversion(const InversionCandidate* inv1, - const InversionCandidate* inv2, - bool ignoreUnmatched) const +bool Retrieval::betterInversion(const InversionCandidate* inv1, + const InversionCandidate* inv2, + bool ignoreUnmatched) const { // Return true if inversion1 is *better* than inversion2. // It's mostly about the retrieval cost, but other aspects are also taken into account. @@ -987,27 +723,17 @@ bool OptimizerRetrieval::betterInversion(const InversionCandidate* inv1, return false; } -void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversions, - IndexScratchList* fromIndexScratches, USHORT scope) const +void Retrieval::getInversionCandidates(InversionCandidateList& inversions, + IndexScratchList& fromIndexScratches, + unsigned scope) const { -/************************************** - * - * g e t I n v e r s i o n C a n d i d a t e s - * - ************************************** - * - * Functional description - * - **************************************/ const double cardinality = csb->csb_rpt[stream].csb_cardinality; // Walk through indexes to calculate selectivity / candidate - Array matches; - FB_SIZE_T i = 0; + MatchedBooleanList matches; - for (i = 0; i < fromIndexScratches->getCount(); i++) + for (auto& scratch : fromIndexScratches) { - IndexScratch& scratch = (*fromIndexScratches)[i]; scratch.scopeCandidate = false; scratch.lowerCount = 0; scratch.upperCount = 0; @@ -1020,17 +746,18 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio scratch.selectivity = MAXIMUM_SELECTIVITY; bool unique = false; + const auto idx = scratch.index; - for (int j = 0; j < scratch.idx->idx_count; j++) + for (unsigned j = 0; j < scratch.segments.getCount(); j++) { - const IndexScratchSegment* const segment = scratch.segments[j]; + const auto& segment = scratch.segments[j]; - if (segment->scope == scope) + if (segment.scope == scope) scratch.scopeCandidate = true; - if (segment->scanType != segmentScanMissing && !(scratch.idx->idx_flags & idx_unique)) + if (segment.scanType != segmentScanMissing && !(idx->idx_flags & idx_unique)) { - const USHORT iType = scratch.idx->idx_rpt[j].idx_itype; + const auto iType = idx->idx_rpt[j].idx_itype; if (iType >= idx_first_intl_string) { @@ -1048,32 +775,32 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio // Check if this is the last usable segment if (!scratch.fuzzy && - (segment->scanType == segmentScanEqual || - segment->scanType == segmentScanEquivalent || - segment->scanType == segmentScanMissing)) + (segment.scanType == segmentScanEqual || + segment.scanType == segmentScanEquivalent || + segment.scanType == segmentScanMissing)) { // This is a perfect usable segment thus update root selectivity scratch.lowerCount++; scratch.upperCount++; - scratch.selectivity = scratch.idx->idx_rpt[j].idx_selectivity; - scratch.nonFullMatchedSegments = scratch.idx->idx_count - (j + 1); + scratch.selectivity = idx->idx_rpt[j].idx_selectivity; + scratch.nonFullMatchedSegments = idx->idx_count - (j + 1); // Add matches for this segment to the main matches list - matches.join(segment->matches); + matches.join(segment.matches); // An equality scan for any unique index cannot retrieve more // than one row. The same is true for an equivalence scan for // any primary index. const bool single_match = - (segment->scanType == segmentScanEqual && - (scratch.idx->idx_flags & idx_unique)) || - (segment->scanType == segmentScanEquivalent && - (scratch.idx->idx_flags & idx_primary)); + (segment.scanType == segmentScanEqual && + (idx->idx_flags & idx_unique)) || + (segment.scanType == segmentScanEquivalent && + (idx->idx_flags & idx_primary)); // dimitr: IS NULL scan against primary key is guaranteed // to return zero rows. Do we need yet another // special case here? - if (single_match && ((j + 1) == scratch.idx->idx_count)) + if (single_match && ((j + 1) == idx->idx_count)) { // We have found a full equal matching index and it's unique, // so we can stop looking further, because this is the best @@ -1095,24 +822,24 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio double selectivity = scratch.selectivity; double factor = 1; - switch (segment->scanType) + switch (segment.scanType) { case segmentScanBetween: scratch.lowerCount++; scratch.upperCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; + selectivity = idx->idx_rpt[j].idx_selectivity; factor = REDUCE_SELECTIVITY_FACTOR_BETWEEN; break; case segmentScanLess: scratch.upperCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; + selectivity = idx->idx_rpt[j].idx_selectivity; factor = REDUCE_SELECTIVITY_FACTOR_LESS; break; case segmentScanGreater: scratch.lowerCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; + selectivity = idx->idx_rpt[j].idx_selectivity; factor = REDUCE_SELECTIVITY_FACTOR_GREATER; break; @@ -1121,12 +848,12 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio case segmentScanEquivalent: scratch.lowerCount++; scratch.upperCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; + selectivity = idx->idx_rpt[j].idx_selectivity; factor = REDUCE_SELECTIVITY_FACTOR_STARTING; break; default: - fb_assert(segment->scanType == segmentScanNone); + fb_assert(segment.scanType == segmentScanNone); break; } @@ -1138,10 +865,10 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio fb_assert(selectivity <= scratch.selectivity); scratch.selectivity = selectivity; - if (segment->scanType != segmentScanNone) + if (segment.scanType != segmentScanNone) { - matches.join(segment->matches); - scratch.nonFullMatchedSegments = scratch.idx->idx_count - j; + matches.join(segment.matches); + scratch.nonFullMatchedSegments = idx->idx_count - j; } break; } @@ -1164,7 +891,7 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio selectivity = DEFAULT_SELECTIVITY; } - InversionCandidate* invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate->unique = unique; invCandidate->selectivity = selectivity; // Calculate the cost (only index pages) for this index. @@ -1175,34 +902,27 @@ void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio invCandidate->scratch = &scratch; invCandidate->matches.join(matches); - for (FB_SIZE_T k = 0; k < invCandidate->matches.getCount(); k++) + for (auto match : invCandidate->matches) { - invCandidate->matches[k]->findDependentFromStreams(this, + match->findDependentFromStreams(csb, stream, &invCandidate->dependentFromStreams); } - invCandidate->dependencies = (int) invCandidate->dependentFromStreams.getCount(); - inversions->add(invCandidate); + invCandidate->dependencies = invCandidate->dependentFromStreams.getCount(); + inversions.add(invCandidate); } } } } -ValueExprNode* OptimizerRetrieval::findDbKey(ValueExprNode* dbkey, SLONG* position) const -{ -/************************************** - * - * f i n d D b K e y - * - ************************************** - * - * Functional description - * Search a dbkey (possibly a concatenated one) for - * a dbkey for specified stream. - * - **************************************/ - const RecordKeyNode* keyNode = nodeAs(dbkey); +// +// Search a dbkey (possibly a concatenated one) for a dbkey for specified stream +// + +ValueExprNode* Retrieval::findDbKey(ValueExprNode* dbkey, SLONG* position) const +{ + const auto keyNode = nodeAs(dbkey); if (keyNode && keyNode->blrOp == blr_dbkey) { @@ -1210,10 +930,10 @@ ValueExprNode* OptimizerRetrieval::findDbKey(ValueExprNode* dbkey, SLONG* positi return dbkey; ++*position; - return NULL; + return nullptr; } - ConcatenateNode* concatNode = nodeAs(dbkey); + const auto concatNode = nodeAs(dbkey); if (concatNode) { @@ -1228,27 +948,20 @@ ValueExprNode* OptimizerRetrieval::findDbKey(ValueExprNode* dbkey, SLONG* positi return dbkey_temp; } - return NULL; + return nullptr; } -InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) const +// +// Build node for index scan +// + +InversionNode* Retrieval::makeIndexScanNode(IndexScratch* indexScratch) const { -/************************************** - * - * m a k e I n d e x S c a n N o d e - * - ************************************** - * - * Functional description - * Build node for index scan. - * - **************************************/ - if (!createIndexScanNodes) - return NULL; + return nullptr; - index_desc* const idx = indexScratch->idx; + index_desc* const idx = indexScratch->index; // Check whether this is during a compile or during a SET INDEX operation if (csb) @@ -1264,8 +977,8 @@ InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) if (!(csb->csb_g_flags & csb_internal)) MET_lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1); - IndexRetrieval* const retrieval = - FB_NEW_POOL(pool) IndexRetrieval(pool, relation, idx, indexName); + const auto retrieval = + FB_NEW_POOL(getPool()) IndexRetrieval(getPool(), relation, idx, indexName); // Pick up lower bound segment values ValueExprNode** lower = retrieval->irb_value; @@ -1283,13 +996,13 @@ InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) retrieval->irb_generic |= irb_descending; } - int i = 0; bool ignoreNullsOnScan = true; - IndexScratchSegment** segment = indexScratch->segments.begin(); + const auto& segments = indexScratch->segments; - for (i = 0; i < MAX(indexScratch->lowerCount, indexScratch->upperCount); i++) + const auto count = MAX(indexScratch->lowerCount, indexScratch->upperCount); + for (unsigned i = 0; i < count; i++) { - if (segment[i]->scanType == segmentScanMissing) + if (segments[i].scanType == segmentScanMissing) { *lower++ = *upper++ = NullNode::instance(); ignoreNullsOnScan = false; @@ -1297,27 +1010,25 @@ InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) else { if (i < indexScratch->lowerCount) - *lower++ = segment[i]->lowerValue; + *lower++ = segments[i].lowerValue; if (i < indexScratch->upperCount) - *upper++ = segment[i]->upperValue; + *upper++ = segments[i].upperValue; - if (segment[i]->scanType == segmentScanEquivalent) + if (segments[i].scanType == segmentScanEquivalent) ignoreNullsOnScan = false; } } - i = MAX(indexScratch->lowerCount, indexScratch->upperCount) - 1; - - if (i >= 0) + if (count) { - if (segment[i]->scanType == segmentScanStarting) + if (segments[count - 1].scanType == segmentScanStarting) retrieval->irb_generic |= irb_starting; - if (segment[i]->excludeLower) + if (segments[count - 1].excludeLower) retrieval->irb_generic |= irb_exclude_lower; - if (segment[i]->excludeUpper) + if (segments[count - 1].excludeUpper) retrieval->irb_generic |= irb_exclude_upper; } @@ -1335,11 +1046,10 @@ InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) if (retrieval->irb_lower_count == retrieval->irb_upper_count) { retrieval->irb_generic |= irb_equality; - segment = indexScratch->segments.begin(); - for (i = 0; i < retrieval->irb_lower_count; i++) + for (unsigned i = 0; i < retrieval->irb_lower_count; i++) { - if (segment[i]->lowerValue != segment[i]->upperValue) + if (segments[i].lowerValue != segments[i].upperValue) { retrieval->irb_generic &= ~irb_equality; break; @@ -1363,28 +1073,17 @@ InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) idx->idx_runtime_flags |= idx_used; const ULONG impure = csb ? csb->allocImpure() : 0; - return FB_NEW_POOL(pool) InversionNode(retrieval, impure); + return FB_NEW_POOL(getPool()) InversionNode(retrieval, impure); } -InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* inversions) const -{ -/************************************** - * - * m a k e I n v e r s i o n - * - ************************************** - * - * Select best available inversion candidates - * and compose them to 1 inversion. - * This was never implemented: - * If top is true the datapages-cost is - * also used in the calculation (only needed - * for top InversionNode generation). - * - **************************************/ +// +// Select best available inversion candidates and compose them to 1 inversion +// - if (inversions->isEmpty() && !navigationCandidate) - return NULL; +InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) const +{ + if (inversions.isEmpty() && !navigationCandidate) + return nullptr; const double streamCardinality = MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); @@ -1414,28 +1113,25 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in // Force to always choose at least one index bool firstCandidate = true; - InversionCandidate* invCandidate = NULL; + InversionCandidate* invCandidate = nullptr; - for (InversionCandidate** iter = inversions->begin(); - iter != inversions->end(); ++iter) + for (auto inversion : inversions) { - InversionCandidate* const inversion = *iter; - const IndexScratch* const indexScratch = inversion->scratch; + const auto indexScratch = inversion->scratch; // If the explicit plan doesn't mention this index, fake it as used // thus excluding it from the cost-based algorithm. Otherwise, // given this index is suitable for navigation, also mark it as used. if ((indexScratch && - (indexScratch->idx->idx_runtime_flags & idx_plan_dont_use)) || + (indexScratch->index->idx_runtime_flags & idx_plan_dont_use)) || (!customPlan && inversion == navigationCandidate)) { inversion->used = true; } } - // The matches returned in this inversion are always sorted. - SortedArray matches; + MatchedBooleanList matches; if (navigationCandidate) { @@ -1450,15 +1146,14 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in firstCandidate = false; } - for (FB_SIZE_T i = 0; i < inversions->getCount(); i++) + for (FB_SIZE_T i = 0; i < inversions.getCount(); i++) { // Initialize vars before walking through candidates - InversionCandidate* bestCandidate = NULL; + InversionCandidate* bestCandidate = nullptr; bool restartLoop = false; - for (FB_SIZE_T currentPosition = 0; currentPosition < inversions->getCount(); ++currentPosition) + for (const auto currentInv : inversions) { - InversionCandidate* currentInv = (*inversions)[currentPosition]; if (!currentInv->used) { // If this is a unique full equal matched inversion we're done, so @@ -1466,7 +1161,7 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in if (currentInv->unique && currentInv->dependencies && !currentInv->condition) { if (!invCandidate) - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); if (!currentInv->inversion && currentInv->scratch) invCandidate->inversion = makeIndexScanNode(currentInv->scratch); @@ -1483,10 +1178,10 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in invCandidate->dependencies = currentInv->dependencies; matches.clear(); - for (FB_SIZE_T j = 0; j < currentInv->matches.getCount(); j++) + for (const auto currentMatch : currentInv->matches) { - if (!matches.exist(currentInv->matches[j])) - matches.add(currentInv->matches[j]); + if (!matches.exist(currentMatch)) + matches.add(currentMatch); } if (currentInv->boolean) @@ -1504,14 +1199,14 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in // Look if a match is already used by previous matches. bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false; - for (FB_SIZE_T k = 0; k < currentInv->matches.getCount(); k++) + for (const auto currentMatch : currentInv->matches) { - if (matches.exist(currentInv->matches[k])) + if (matches.exist(currentMatch)) { anyMatchAlreadyUsed = true; if (navigationCandidate && - navigationCandidate->matches.exist(currentInv->matches[k])) + navigationCandidate->matches.exist(currentMatch)) { matchUsedByNavigation = true; } @@ -1529,10 +1224,10 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in // If a match on this index was already used by another // index, add also the other matches from this index. - for (FB_SIZE_T j = 0; j < currentInv->matches.getCount(); j++) + for (const auto currentMatch : currentInv->matches) { - if (!matches.exist(currentInv->matches[j])) - matches.add(currentInv->matches[j]); + if (!matches.exist(currentMatch)) + matches.add(currentMatch); } // Restart loop, because other indexes could also be excluded now. @@ -1633,7 +1328,7 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in if (!invCandidate) { - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); if (!bestCandidate->inversion && bestCandidate->scratch) { invCandidate->inversion = makeIndexScanNode(bestCandidate->scratch); } @@ -1684,10 +1379,10 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in MAX(bestCandidate->matchedSegments, invCandidate->matchedSegments); invCandidate->dependencies += bestCandidate->dependencies; - for (FB_SIZE_T j = 0; j < bestCandidate->matches.getCount(); j++) + for (const auto bestMatch : bestCandidate->matches) { - if (!matches.exist(bestCandidate->matches[j])) - matches.add(bestCandidate->matches[j]); + if (!matches.exist(bestMatch)) + matches.add(bestMatch); } if (bestCandidate->boolean) @@ -1721,7 +1416,7 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in if (navigationCandidate) { if (!invCandidate) - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate->unique = navigationCandidate->unique; invCandidate->selectivity *= navigationCandidate->selectivity; @@ -1736,28 +1431,20 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in return invCandidate; } -bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* boolean, - USHORT scope) const +bool Retrieval::matchBoolean(IndexScratch* indexScratch, + BoolExprNode* boolean, + unsigned scope) const { -/************************************** - * - * m a t c h B o o l e a n - * - ************************************** - * - * Functional description - * - **************************************/ if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) return false; - ComparativeBoolNode* cmpNode = nodeAs(boolean); - MissingBoolNode* missingNode = nodeAs(boolean); - NotBoolNode* notNode = nodeAs(boolean); - RseBoolNode* rseNode = nodeAs(boolean); + const auto cmpNode = nodeAs(boolean); + const auto missingNode = nodeAs(boolean); + const auto notNode = nodeAs(boolean); + const auto rseNode = nodeAs(boolean); bool forward = true; - ValueExprNode* value = NULL; - ValueExprNode* match = NULL; + ValueExprNode* value = nullptr; + ValueExprNode* match = nullptr; if (cmpNode) { @@ -1775,19 +1462,21 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* } ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ? - cmpNode->arg3 : NULL; + cmpNode->arg3 : nullptr; - if (indexScratch->idx->idx_flags & idx_expressn) + const auto idx = indexScratch->index; + + if (idx->idx_flags & idx_expressn) { // see if one side or the other is matchable to the index expression - fb_assert(indexScratch->idx->idx_expression != NULL); + fb_assert(idx->idx_expression); - if (!checkExpressionIndex(csb, indexScratch->idx, match, stream) || + if (!checkExpressionIndex(csb, idx, match, stream) || (value && !value->computable(csb, stream, false))) { if ((!cmpNode || cmpNode->blrOp != blr_starting) && value && - checkExpressionIndex(csb, indexScratch->idx, value, stream) && + checkExpressionIndex(csb, idx, value, stream) && match->computable(csb, stream, false)) { ValueExprNode* temp = match; @@ -1851,27 +1540,27 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* // match the field to an index, if possible, and save the value to be matched // as either the lower or upper bound for retrieval, or both - FieldNode* const fieldNode = nodeAs(match); + const auto fieldNode = nodeAs(match); - if (!(indexScratch->idx->idx_flags & idx_expressn)) + if (!(idx->idx_flags & idx_expressn)) fb_assert(fieldNode); - const bool isDesc = (indexScratch->idx->idx_flags & idx_descending); + const bool isDesc = (idx->idx_flags & idx_descending); // Needed for int64 matches, see injectCast() function - CastNode *cast = NULL, *cast2 = NULL; + CastNode *cast = nullptr, *cast2 = nullptr; - fb_assert(indexScratch->segments.getCount() == indexScratch->idx->idx_count); + fb_assert(indexScratch->segments.getCount() == idx->idx_count); - for (int i = 0; i < indexScratch->idx->idx_count; i++) + for (unsigned i = 0; i < idx->idx_count; i++) { - if (!(indexScratch->idx->idx_flags & idx_expressn) && - fieldNode->fieldId != indexScratch->idx->idx_rpt[i].idx_field) + if (!(idx->idx_flags & idx_expressn) && + fieldNode->fieldId != idx->idx_rpt[i].idx_field) { continue; } - IndexScratchSegment* const segment = indexScratch->segments[i]; + const auto segment = &indexScratch->segments[i]; if (cmpNode) { @@ -2021,7 +1710,7 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* if (!((segment->scanType == segmentScanEqual) || (segment->scanType == segmentScanEquivalent))) { - segment->lowerValue = segment->upperValue = NULL; + segment->lowerValue = segment->upperValue = nullptr; segment->scanType = segmentScanMissing; segment->excludeLower = false; segment->excludeUpper = false; @@ -2049,24 +1738,18 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* return false; } -InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const +// +// Check whether a boolean is a DB_KEY based comparison +// + +InversionCandidate* Retrieval::matchDbKey(BoolExprNode* boolean) const { -/************************************** - * - * m a t c h D b K e y - * - ************************************** - * - * Functional description - * Check whether a boolean is a DB_KEY based comparison. - * - **************************************/ // If this isn't an equality, it isn't even interesting - ComparativeBoolNode* cmpNode = nodeAs(boolean); + const auto cmpNode = nodeAs(boolean); if (!cmpNode) - return NULL; + return nullptr; switch (cmpNode->blrOp) { @@ -2080,7 +1763,7 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const break; default: - return NULL; + return nullptr; } // Find the side of the equality that is potentially a dbkey. @@ -2105,14 +1788,14 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const } if (!dbkey) - return NULL; + return nullptr; // Make sure we have the correct stream const auto keyNode = nodeAs(dbkey); if (!keyNode || keyNode->blrOp != blr_dbkey || keyNode->recStream != stream) - return NULL; + return nullptr; // If this is a dbkey for the appropriate stream, it's invertable @@ -2122,8 +1805,8 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const bool unique = false; double selectivity = 0; - ValueExprNode* lower = NULL; - ValueExprNode* upper = NULL; + ValueExprNode* lower = nullptr; + ValueExprNode* upper = nullptr; switch (cmpNode->blrOp) { @@ -2172,22 +1855,22 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const break; default: - return NULL; + return nullptr; } if (lower && !lower->computable(csb, stream, false)) - return NULL; + return nullptr; if (upper && !upper->computable(csb, stream, false)) - return NULL; + return nullptr; - InversionCandidate* const invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate->unique = unique; invCandidate->selectivity = selectivity; invCandidate->cost = 0; invCandidate->matches.add(boolean); - boolean->findDependentFromStreams(this, &invCandidate->dependentFromStreams); - invCandidate->dependencies = (int) invCandidate->dependentFromStreams.getCount(); + boolean->findDependentFromStreams(csb, stream, &invCandidate->dependentFromStreams); + invCandidate->dependencies = invCandidate->dependentFromStreams.getCount(); if (createIndexScanNodes) { @@ -2195,7 +1878,7 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const { fb_assert(lower == upper); - InversionNode* const inversion = FB_NEW_POOL(pool) InversionNode(lower, n); + const auto inversion = FB_NEW_POOL(getPool()) InversionNode(lower, n); inversion->impure = csb->allocImpure(); invCandidate->inversion = inversion; } @@ -2204,7 +1887,7 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const fb_assert(n == 0); fb_assert(lower || upper); - DbKeyRangeNode* const dbkeyRange = FB_NEW_POOL(pool) DbKeyRangeNode(lower, upper); + const auto dbkeyRange = FB_NEW_POOL(getPool()) DbKeyRangeNode(lower, upper); invCandidate->dbkeyRanges.add(dbkeyRange); } } @@ -2212,22 +1895,16 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const return invCandidate; } -InversionCandidate* OptimizerRetrieval::matchOnIndexes( - IndexScratchList* inputIndexScratches, BoolExprNode* boolean, USHORT scope) const +// +// Try to match boolean on every index. +// If the boolean is an "OR" node then a inversion candidate could be returned. +// + +InversionCandidate* Retrieval::matchOnIndexes(IndexScratchList& inputIndexScratches, + BoolExprNode* boolean, + unsigned scope) const { -/************************************** - * - * m a t c h O n I n d e x e s - * - ************************************** - * - * Functional description - * Try to match boolean on every index. - * If the boolean is an "OR" node then a - * inversion candidate could be returned. - * - **************************************/ - BinaryBoolNode* binaryNode = nodeAs(boolean); + const auto binaryNode = nodeAs(boolean); // Handle the "OR" case up front if (binaryNode && binaryNode->blrOp == blr_or) @@ -2235,54 +1912,34 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( InversionCandidateList inversions; // Make list for index matches - IndexScratchList indexOrScratches; // Copy information from caller - - FB_SIZE_T i = 0; - - for (; i < inputIndexScratches->getCount(); i++) - { - IndexScratch& scratch = (*inputIndexScratches)[i]; - indexOrScratches.add(scratch); - } + IndexScratchList indexOrScratches(inputIndexScratches); // We use a scope variable to see on how // deep we are in a nested or conjunction. scope++; - InversionCandidate* invCandidate1 = - matchOnIndexes(&indexOrScratches, binaryNode->arg1, scope); + auto invCandidate1 = matchOnIndexes(indexOrScratches, binaryNode->arg1, scope); if (invCandidate1) inversions.add(invCandidate1); - BinaryBoolNode* childBoolNode = nodeAs(binaryNode->arg1); + auto childBoolNode = nodeAs(binaryNode->arg1); // Get usable inversions based on indexOrScratches and scope if (!childBoolNode || childBoolNode->blrOp != blr_or) - getInversionCandidates(&inversions, &indexOrScratches, scope); + getInversionCandidates(inversions, indexOrScratches, scope); - invCandidate1 = makeInversion(&inversions); - - // Clear list to remove previously matched conjunctions - indexOrScratches.clear(); + invCandidate1 = makeInversion(inversions); // Copy information from caller - - i = 0; - - for (; i < inputIndexScratches->getCount(); i++) - { - IndexScratch& scratch = (*inputIndexScratches)[i]; - indexOrScratches.add(scratch); - } + indexOrScratches = inputIndexScratches; // Clear inversion list inversions.clear(); - InversionCandidate* invCandidate2 = matchOnIndexes( - &indexOrScratches, binaryNode->arg2, scope); + auto invCandidate2 = matchOnIndexes(indexOrScratches, binaryNode->arg2, scope); if (invCandidate2) inversions.add(invCandidate2); @@ -2291,15 +1948,15 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( // Make inversion based on indexOrScratches and scope if (!childBoolNode || childBoolNode->blrOp != blr_or) - getInversionCandidates(&inversions, &indexOrScratches, scope); + getInversionCandidates(inversions, indexOrScratches, scope); - invCandidate2 = makeInversion(&inversions); + invCandidate2 = makeInversion(inversions); if (invCandidate1 && invCandidate2 && (invCandidate1->indexes || invCandidate1->unique) && (invCandidate2->indexes || invCandidate2->unique)) { - InversionCandidate* invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); + const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate->inversion = composeInversion(invCandidate1->inversion, invCandidate2->inversion, InversionNode::TYPE_OR); invCandidate->selectivity = invCandidate1->selectivity + invCandidate2->selectivity - @@ -2313,8 +1970,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( if (invCandidate1->condition && invCandidate2->condition) { - BinaryBoolNode* const newNode = - FB_NEW_POOL(*tdbb->getDefaultPool()) BinaryBoolNode(*tdbb->getDefaultPool(), blr_or); + const auto newNode = FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_or); newNode->arg1 = invCandidate1->condition; newNode->arg2 = invCandidate2->condition; invCandidate->condition = newNode; @@ -2329,18 +1985,15 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( } // Add matches conjunctions that exists in both left and right inversion - if ((invCandidate1->matches.getCount()) && (invCandidate2->matches.getCount())) + if (invCandidate1->matches.hasData() && invCandidate2->matches.hasData()) { SortedArray matches; - for (FB_SIZE_T j = 0; j < invCandidate1->matches.getCount(); j++) - matches.add(invCandidate1->matches[j]); + for (const auto match : invCandidate1->matches) + matches.add(match); - for (FB_SIZE_T j = 0; j < invCandidate2->matches.getCount(); j++) - { - if (matches.exist(invCandidate2->matches[j])) - invCandidate->matches.add(invCandidate2->matches[j]); - } + for (const auto match : invCandidate2->matches) + matches.add(match); } return invCandidate; @@ -2354,9 +2007,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( { if (invCandidate1->condition) { - BinaryBoolNode* const newNode = - FB_NEW_POOL(*tdbb->getDefaultPool()) - BinaryBoolNode(*tdbb->getDefaultPool(), blr_or); + const auto newNode = FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_or); newNode->arg1 = invCandidate1->condition; newNode->arg2 = condition; condition = newNode; @@ -2375,9 +2026,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( { if (invCandidate2->condition) { - BinaryBoolNode* const newNode = - FB_NEW_POOL(*tdbb->getDefaultPool()) - BinaryBoolNode(*tdbb->getDefaultPool(), blr_or); + const auto newNode = FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_or); newNode->arg1 = invCandidate2->condition; newNode->arg2 = condition; condition = newNode; @@ -2388,7 +2037,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( } } - return NULL; + return nullptr; } if (binaryNode && binaryNode->blrOp == blr_and) @@ -2409,20 +2058,18 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( if (invCandidate) inversions.add(invCandidate); - return makeInversion(&inversions); + return makeInversion(inversions); } // Check for DB_KEY comparison InversionCandidate* const invCandidate = matchDbKey(boolean); // Walk through indexes - for (FB_SIZE_T i = 0; i < inputIndexScratches->getCount(); i++) + for (auto& indexScratch : inputIndexScratches) { - IndexScratch& indexScratch = (*inputIndexScratches)[i]; - // Try to match the boolean against a index. - if (!(indexScratch.idx->idx_runtime_flags & idx_plan_dont_use) || - (indexScratch.idx->idx_runtime_flags & idx_plan_navigate)) + if (!(indexScratch.index->idx_runtime_flags & idx_plan_dont_use) || + (indexScratch.index->idx_runtime_flags & idx_plan_navigate)) { matchBoolean(&indexScratch, boolean, scope); } @@ -2432,102 +2079,14 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( } -#ifdef OPT_DEBUG_RETRIEVAL -void OptimizerRetrieval::printCandidate(const InversionCandidate* candidate) const +// +// Check if the boolean is valid for using it against the given index segment +// + +bool Retrieval::validateStarts(IndexScratch* indexScratch, + ComparativeBoolNode* cmpNode, + unsigned segment) const { -/************************************** - * - * p r i n t C a n d i d a t e - * - ************************************** - * - * Functional description - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " cost(%1.2f), selectivity(%1.10f), indexes(%d), matched(%d, %d)", - candidate->cost, candidate->selectivity, candidate->indexes, candidate->matchedSegments, - candidate->nonFullMatchedSegments); - if (candidate->unique) - fprintf(opt_debug_file, ", unique"); - int depFromCount = candidate->dependentFromStreams.getCount(); - if (depFromCount >= 1) - { - fprintf(opt_debug_file, ", dependent from "); - for (int i = 0; i < depFromCount; i++) - { - if (i == 0) - fprintf(opt_debug_file, "%d", candidate->dependentFromStreams[i]); - else - fprintf(opt_debug_file, ", %d", candidate->dependentFromStreams[i]); - } - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerRetrieval::printCandidates(const InversionCandidateList* inversions) const -{ -/************************************** - * - * p r i n t C a n d i d a t e s - * - ************************************** - * - * Functional description - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " retrieval candidates:\n"); - fclose(opt_debug_file); - const InversionCandidate* const* inversion = inversions->begin(); - for (int i = 0; i < inversions->getCount(); i++) - { - const InversionCandidate* candidate = inversion[i]; - printCandidate(candidate); - } -} - -void OptimizerRetrieval::printFinalCandidate(const InversionCandidate* candidate) const -{ -/************************************** - * - * p r i n t F i n a l C a n d i d a t e - * - ************************************** - * - * Functional description - * - **************************************/ - - if (candidate) - { - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " final candidate: "); - fclose(opt_debug_file); - printCandidate(candidate); - } -} -#endif - - -bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeBoolNode* cmpNode, - USHORT segment) const -{ -/************************************** - * - * v a l i d a t e S t a r t s - * - ************************************** - * - * Functional description - * Check if the boolean is valid for - * using it against the given index segment. - * - **************************************/ - fb_assert(cmpNode && cmpNode->blrOp == blr_starting); if (!cmpNode || cmpNode->blrOp != blr_starting) return false; @@ -2535,19 +2094,21 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB ValueExprNode* field = cmpNode->arg1; ValueExprNode* value = cmpNode->arg2; - if (indexScratch->idx->idx_flags & idx_expressn) + const auto idx = indexScratch->index; + + if (idx->idx_flags & idx_expressn) { // AB: What if the expression contains a number/float etc.. and // we use starting with against it? Is that allowed? - fb_assert(indexScratch->idx->idx_expression != NULL); + fb_assert(idx->idx_expression); - if (!(checkExpressionIndex(csb, indexScratch->idx, field, stream) || + if (!(checkExpressionIndex(csb, idx, field, stream) || (value && !value->computable(csb, stream, false)))) { // AB: Can we swap de left and right sides by a starting with? // X STARTING WITH 'a' that is never the same as 'a' STARTING WITH X if (value && - checkExpressionIndex(csb, indexScratch->idx, value, stream) && + checkExpressionIndex(csb, idx, value, stream) && field->computable(csb, stream, false)) { field = value; @@ -2559,7 +2120,7 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB } else { - FieldNode* fieldNode = nodeAs(field); + const auto fieldNode = nodeAs(field); if (!fieldNode) { @@ -2570,14 +2131,14 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB return false; /* if (!nodeIs(value)) - return NULL; + return nullptr; field = value; value = cmpNode->arg1; */ } // Every string starts with an empty string so don't bother using an index in that case. - LiteralNode* literal = nodeAs(value); + const auto literal = nodeAs(value); if (literal) { @@ -2592,11 +2153,11 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB // AB: Check if the index-segment is usable for using starts. // Thus it should be of type string, etc... if (fieldNode->fieldStream != stream || - fieldNode->fieldId != indexScratch->idx->idx_rpt[segment].idx_field || - !(indexScratch->idx->idx_rpt[segment].idx_itype == idx_string || - indexScratch->idx->idx_rpt[segment].idx_itype == idx_byte_array || - indexScratch->idx->idx_rpt[segment].idx_itype == idx_metadata || - indexScratch->idx->idx_rpt[segment].idx_itype >= idx_first_intl_string) || + fieldNode->fieldId != idx->idx_rpt[segment].idx_field || + !(idx->idx_rpt[segment].idx_itype == idx_string || + idx->idx_rpt[segment].idx_itype == idx_byte_array || + idx->idx_rpt[segment].idx_itype == idx_metadata || + idx->idx_rpt[segment].idx_itype >= idx_first_intl_string) || !value->computable(csb, stream, false)) { return false; @@ -2607,640 +2168,56 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB } -OptimizerInnerJoin::OptimizerInnerJoin(MemoryPool& p, OptimizerBlk* opt, const StreamList& streams, - SortNode* sort_clause, PlanNode* plan_clause) - : pool(p), innerStreams(p) +#ifdef OPT_DEBUG_RETRIEVAL +void Retrieval::printCandidate(const InversionCandidate* candidate) const { -/************************************** - * - * O p t i m i z e r I n n e r J o i n - * - ************************************** - * - * Initialize - * - **************************************/ - tdbb = NULL; - SET_TDBB(tdbb); - this->database = tdbb->getDatabase(); - this->optimizer = opt; - this->csb = this->optimizer->opt_csb; - this->sort = sort_clause; - this->plan = plan_clause; - this->remainingStreams = 0; - - innerStreams.grow(streams.getCount()); - InnerJoinStreamInfo** innerStream = innerStreams.begin(); - for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) + optimizer->printf(" cost (%1.2f), selectivity (%1.10f), indexes (%d), matched (%d, %d)", + candidate->cost, candidate->selectivity, candidate->indexes, candidate->matchedSegments, + candidate->nonFullMatchedSegments); + if (candidate->unique) + optimizer->printf(", unique"); + if (candidate->dependentFromStreams.hasData()) { - innerStream[i] = FB_NEW_POOL(p) InnerJoinStreamInfo(p); - innerStream[i]->stream = streams[i]; - } - - calculateStreamInfo(); -} - -OptimizerInnerJoin::~OptimizerInnerJoin() -{ -/************************************** - * - * ~O p t i m i z e r I n n e r J o i n - * - ************************************** - * - * Finish with giving back memory. - * - **************************************/ - - for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) - { - for (FB_SIZE_T j = 0; j < innerStreams[i]->indexedRelationships.getCount(); j++) - delete innerStreams[i]->indexedRelationships[j]; - - delete innerStreams[i]; - } -} - -void OptimizerInnerJoin::calculateStreamInfo() -{ -/************************************** - * - * c a l c u l a t e S t r e a m I n f o - * - ************************************** - * - * Calculate the needed information for - * all streams. - * - **************************************/ - StreamList streams; - - // First get the base cost without any relation to any other inner join stream - for (auto innerStream : innerStreams) - { - streams.add(innerStream->stream); - - const auto csb_tail = &csb->csb_rpt[innerStream->stream]; - csb_tail->activate(); - - OptimizerRetrieval optimizerRetrieval(pool, optimizer, innerStream->stream, - false, false, sort); - AutoPtr candidate(optimizerRetrieval.getCost()); - - innerStream->baseCost = candidate->cost; - innerStream->baseSelectivity = candidate->selectivity; - innerStream->baseIndexes = candidate->indexes; - innerStream->baseUnique = candidate->unique; - innerStream->baseNavigated = candidate->navigated; - - csb_tail->deactivate(); - } - - StreamStateHolder stateHolder(csb, streams); - stateHolder.activate(); - - // Collect stream inter-dependencies - for (const auto innerStream : innerStreams) - getIndexedRelationships(innerStream); - - // Unless PLAN is enforced, sort the streams based on independecy and cost - if (!plan && (innerStreams.getCount() > 1)) - { - StreamInfoList tempStreams(pool); - - for (const auto innerStream : innerStreams) + optimizer->printf(", dependent from streams: "); + const auto end = candidate->dependentFromStreams.end(); + for (auto iter = candidate->dependentFromStreams.begin(); iter != end; iter++) { - FB_SIZE_T index = 0; - for (; index < tempStreams.getCount(); index++) - { - // First those streams which can't be used by other streams - // or can't depend on a stream - if (innerStream->isIndependent() && !tempStreams[index]->isIndependent()) - break; - - // Next those with the lowest previous expected streams - const int compare = innerStream->previousExpectedStreams - - tempStreams[index]->previousExpectedStreams; - - if (compare < 0) - break; - - if (compare == 0) - { - // Next those with the cheapest base cost - if (innerStream->baseCost < tempStreams[index]->baseCost) - break; - } - } - tempStreams.insert(index, innerStream); - } - - // Finally update the innerStreams with the sorted streams - innerStreams.clear(); - innerStreams.join(tempStreams); - } -} - -bool OptimizerInnerJoin::cheaperRelationship(IndexRelationship* checkRelationship, - IndexRelationship* withRelationship) const -{ -/************************************** - * - * c h e a p e r R e l a t i o n s h i p - * - ************************************** - * - * Return true if checking relationship - * is cheaper as withRelationship. - * - **************************************/ - if (checkRelationship->cost == 0) - return true; - - if (withRelationship->cost == 0) - return false; - - const double compareValue = checkRelationship->cost / withRelationship->cost; - if (compareValue >= 0.98 && compareValue <= 1.02) - { - // cost is nearly the same, now check uniqueness and cardinality - - if (checkRelationship->unique == withRelationship->unique) - { - if (checkRelationship->cardinality < withRelationship->cardinality) - return true; - } - else if (checkRelationship->unique) - return true; - else if (withRelationship->unique) - return false; - } - else if (checkRelationship->cost < withRelationship->cost) - return true; - - return false; -} - -void OptimizerInnerJoin::estimateCost(StreamType stream, double* cost, - double* resulting_cardinality, bool start) const -{ -/************************************** - * - * e s t i m a t e C o s t - * - ************************************** - * - * Estimate the cost for the stream. - * - **************************************/ - // Create the optimizer retrieval generation class and calculate - // which indexes will be used and the total estimated selectivity will be returned - OptimizerRetrieval optimizerRetrieval(pool, optimizer, stream, false, false, - (start ? sort : NULL)); - AutoPtr candidate(optimizerRetrieval.getCost()); - - *cost = candidate->cost; - - // Calculate cardinality - const CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; - const double cardinality = csb_tail->csb_cardinality * candidate->selectivity; - - *resulting_cardinality = MAX(cardinality, MINIMUM_CARDINALITY); -} - -StreamType OptimizerInnerJoin::findJoinOrder() -{ -/************************************** - * - * f i n d J o i n O r d e r - * - ************************************** - * - * Find the best order out of the streams. - * First return a stream if it can't use - * an index based on a previous stream and - * it can't be used by another stream. - * Next loop through the remaining streams - * and find the best order. - * - **************************************/ - - optimizer->opt_best_count = 0; - -#ifdef OPT_DEBUG - // Debug - printStartOrder(); -#endif - - int filters = 0, navigations = 0; - - FB_SIZE_T i = 0; - remainingStreams = 0; - - for (i = 0; i < innerStreams.getCount(); i++) - { - if (!innerStreams[i]->used) - { - remainingStreams++; - - const int currentFilter = innerStreams[i]->isFiltered() ? 1 : 0; - - if (navigations && currentFilter) - navigations = 0; - - filters += currentFilter; - - if (innerStreams[i]->baseNavigated && currentFilter == filters) - navigations++; - - if (innerStreams[i]->isIndependent()) - { - if (!optimizer->opt_best_count || innerStreams[i]->baseCost < optimizer->opt_best_cost) - { - optimizer->opt_streams[0].opt_best_stream = innerStreams[i]->stream; - optimizer->opt_best_count = 1; - optimizer->opt_best_cost = innerStreams[i]->baseCost; - } - } + optimizer->printf("%u", *iter); + if (iter != end - 1) + optimizer->printf(", "); } } - - if (optimizer->opt_best_count == 0) - { - IndexedRelationships indexedRelationships(pool); - - for (i = 0; i < innerStreams.getCount(); i++) - { - if (!innerStreams[i]->used) - { - // If optimization for first rows has been requested and index navigations are - // possible, then consider only join orders starting with a navigational stream. - // Except cases when other streams have local predicates applied. - - const int currentFilter = innerStreams[i]->isFiltered() ? 1 : 0; - - if (!optimizer->favorFirstRows || !navigations || - (innerStreams[i]->baseNavigated && currentFilter == filters)) - { - indexedRelationships.clear(); - findBestOrder(0, innerStreams[i], &indexedRelationships, 0.0, 1.0); - - if (plan) - { - // If a explicit PLAN was specified we should be ready; - break; - } - } -#ifdef OPT_DEBUG - // Debug - printProcessList(&indexedRelationships, innerStreams[i]->stream); -#endif - } - } - } - - // Mark streams as used - for (StreamType stream = 0; stream < optimizer->opt_best_count; stream++) - { - InnerJoinStreamInfo* streamInfo = getStreamInfo(optimizer->opt_streams[stream].opt_best_stream); - streamInfo->used = true; - } - -#ifdef OPT_DEBUG - // Debug - printBestOrder(); -#endif - - return optimizer->opt_best_count; + optimizer->printf("\n"); } -void OptimizerInnerJoin::findBestOrder(StreamType position, InnerJoinStreamInfo* stream, - IndexedRelationships* processList, double cost, double cardinality) +void Retrieval::printCandidates(const InversionCandidateList& inversions) const { -/************************************** - * - * f i n d B e s t O r d e r - * - ************************************** - * Make different combinations to find - * out the join order. - * For every position we start with the - * stream that has the best selectivity - * for that position. If we've have - * used up all our streams after that - * we assume we're done. - * - **************************************/ + if (inversions.getCount() < 2) + return; - fb_assert(processList); + const auto tail = &csb->csb_rpt[stream]; - const bool start = (position == 0); + string relName(tail->csb_relation->rel_name.c_str()); + if (tail->csb_alias) + relName += " as " + *tail->csb_alias; + optimizer->printf(" retrieval candidates for stream %u (%s):\n", stream, relName.c_str()); - // do some initializations. - csb->csb_rpt[stream->stream].activate(); - optimizer->opt_streams[position].opt_stream_number = stream->stream; - position++; - const OptimizerBlk::opt_stream* order_end = optimizer->opt_streams.begin() + position; - - // Save the various flag bits from the optimizer block to reset its - // state after each test. - HalfStaticArray streamFlags(pool); - streamFlags.grow(innerStreams.getCount()); - for (FB_SIZE_T i = 0; i < streamFlags.getCount(); i++) - streamFlags[i] = innerStreams[i]->used; - - // Compute delta and total estimate cost to fetch this stream. - double position_cost, position_cardinality, new_cost = 0, new_cardinality = 0; - - if (!plan) - { - estimateCost(stream->stream, &position_cost, &position_cardinality, start); - new_cost = cost + cardinality * position_cost; - new_cardinality = position_cardinality * cardinality; - } - - // If the partial order is either longer than any previous partial order, - // or the same length and cheap, save order as "best". - if (position > optimizer->opt_best_count || - (position == optimizer->opt_best_count && new_cost < optimizer->opt_best_cost)) - { - optimizer->opt_best_count = position; - optimizer->opt_best_cost = new_cost; - for (OptimizerBlk::opt_stream* tail = optimizer->opt_streams.begin(); tail < order_end; tail++) - tail->opt_best_stream = tail->opt_stream_number; - } - -#ifdef OPT_DEBUG - // Debug information - printFoundOrder(position, position_cost, position_cardinality, new_cost, new_cardinality); -#endif - - // mark this stream as "used" in the sense that it is already included - // in this particular proposed stream ordering. - stream->used = true; - bool done = false; - - // if we've used up all the streams there's no reason to go any further. - if (position == remainingStreams) - done = true; - - // If we know a combination with all streams used and the - // current cost is higher as the one from the best we're done. - if ((optimizer->opt_best_count == remainingStreams) && (optimizer->opt_best_cost < new_cost)) - done = true; - - if (!done && !plan) - { - // Add these relations to the processing list - for (FB_SIZE_T j = 0; j < stream->indexedRelationships.getCount(); j++) - { - IndexRelationship* relationship = stream->indexedRelationships[j]; - InnerJoinStreamInfo* relationStreamInfo = getStreamInfo(relationship->stream); - if (!relationStreamInfo->used) - { - bool found = false; - IndexRelationship** processRelationship = processList->begin(); - FB_SIZE_T index; - for (index = 0; index < processList->getCount(); index++) - { - if (relationStreamInfo->stream == processRelationship[index]->stream) - { - // If the cost of this relationship is cheaper then remove the - // old relationship and add this one. - if (cheaperRelationship(relationship, processRelationship[index])) - { - processList->remove(index); - break; - } - - found = true; - break; - } - } - if (!found) - { - // Add relationship sorted on cost (cheapest as first) - IndexRelationship** relationships = processList->begin(); - for (index = 0; index < processList->getCount(); index++) - { - if (cheaperRelationship(relationship, relationships[index])) - break; - } - processList->insert(index, relationship); - } - } - } - - IndexRelationship** nextRelationship = processList->begin(); - for (FB_SIZE_T j = 0; j < processList->getCount(); j++) - { - InnerJoinStreamInfo* relationStreamInfo = getStreamInfo(nextRelationship[j]->stream); - if (!relationStreamInfo->used) - { - findBestOrder(position, relationStreamInfo, processList, new_cost, new_cardinality); - break; - } - } - } - - if (plan) - { - // If a explicit PLAN was specific pick the next relation. - // The order in innerStreams is expected to be exactly the order as - // specified in the explicit PLAN. - for (FB_SIZE_T j = 0; j < innerStreams.getCount(); j++) - { - InnerJoinStreamInfo* nextStream = innerStreams[j]; - if (!nextStream->used) - { - findBestOrder(position, nextStream, processList, new_cost, new_cardinality); - break; - } - } - } - - // Clean up from any changes made for compute the cost for this stream - csb->csb_rpt[stream->stream].deactivate(); - for (FB_SIZE_T i = 0; i < streamFlags.getCount(); i++) - innerStreams[i]->used = streamFlags[i]; + for (const auto candidate : inversions) + printCandidate(candidate); } -void OptimizerInnerJoin::getIndexedRelationships(InnerJoinStreamInfo* testStream) +void Retrieval::printFinalCandidate(const InversionCandidate* candidate) const { -/************************************** - * - * g e t I n d e x e d R e l a t i o n s h i p - * - ************************************** - * - * Check if the testStream can use a index - * when the baseStream is active. If so - * then we create a indexRelationship - * and fill it with the needed information. - * The reference is added to the baseStream - * and the baseStream is added as previous - * expected stream to the testStream. - * - **************************************/ - const auto csb_tail = &csb->csb_rpt[testStream->stream]; + if (!candidate) + return; - OptimizerRetrieval optimizerRetrieval(pool, optimizer, testStream->stream, false, false, NULL); - AutoPtr candidate(optimizerRetrieval.getCost()); + const auto tail = &csb->csb_rpt[stream]; - for (auto baseStream : innerStreams) - { - if (baseStream->stream != testStream->stream && - candidate->dependentFromStreams.exist(baseStream->stream)) - { - // If we could use more conjunctions on the testing stream - // with the base stream active as without the base stream - // then the test stream has a indexed relationship with the base stream. - IndexRelationship* indexRelationship = FB_NEW_POOL(pool) IndexRelationship(); - indexRelationship->stream = testStream->stream; - indexRelationship->unique = candidate->unique; - indexRelationship->cost = candidate->cost; - indexRelationship->cardinality = candidate->unique ? - csb_tail->csb_cardinality : csb_tail->csb_cardinality * candidate->selectivity; + string relName(tail->csb_relation->rel_name.c_str()); + if (tail->csb_alias) + relName += " as " + *tail->csb_alias; + optimizer->printf(" final candidate for stream %u (%s):\n", stream, relName.c_str()); - // indexRelationship are kept sorted on cost and unique in the indexRelations array. - // The unique and cheapest indexed relationships are on the first position. - FB_SIZE_T index = 0; - for (; index < baseStream->indexedRelationships.getCount(); index++) - { - if (cheaperRelationship(indexRelationship, baseStream->indexedRelationships[index])) - break; - } - baseStream->indexedRelationships.insert(index, indexRelationship); - testStream->previousExpectedStreams++; - } - } -} - -InnerJoinStreamInfo* OptimizerInnerJoin::getStreamInfo(StreamType stream) -{ -/************************************** - * - * g e t S t r e a m I n f o - * - ************************************** - * - * Return stream information based on - * the stream number. - * - **************************************/ - - for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) - { - if (innerStreams[i]->stream == stream) - return innerStreams[i]; - } - - // We should never come here - fb_assert(false); - return NULL; -} - -#ifdef OPT_DEBUG -void OptimizerInnerJoin::printBestOrder() const -{ -/************************************** - * - * p r i n t B e s t O r d e r - * - ************************************** - * - * Dump finally selected stream order. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " best order, streams: "); - for (StreamType i = 0; i < optimizer->opt_best_count; i++) - { - if (i == 0) - fprintf(opt_debug_file, "%d", optimizer->opt_streams[i].opt_best_stream); - else - fprintf(opt_debug_file, ", %d", optimizer->opt_streams[i].opt_best_stream); - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerInnerJoin::printFoundOrder(StreamType position, double positionCost, - double positionCardinality, double cost, double cardinality) const -{ -/************************************** - * - * p r i n t F o u n d O r d e r - * - ************************************** - * - * Dump currently passed streams to a - * debug file. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " position %2.2d:", position); - fprintf(opt_debug_file, " pos. cardinality(%10.2f) pos. cost(%10.2f)", positionCardinality, positionCost); - fprintf(opt_debug_file, " cardinality(%10.2f) cost(%10.2f)", cardinality, cost); - fprintf(opt_debug_file, ", streams: ", position); - const OptimizerBlk::opt_stream* tail = optimizer->opt_streams.begin(); - const OptimizerBlk::opt_stream* const order_end = tail + position; - for (; tail < order_end; tail++) - { - if (tail == optimizer->opt_streams.begin()) - fprintf(opt_debug_file, "%d", tail->opt_stream_number); - else - fprintf(opt_debug_file, ", %d", tail->opt_stream_number); - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerInnerJoin::printProcessList(const IndexedRelationships* processList, - StreamType stream) const -{ -/************************************** - * - * p r i n t P r o c e s s L i s t - * - ************************************** - * - * Dump the processlist to a debug file. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " basestream %d, relationships: stream(cost)", stream); - const IndexRelationship* const* relationships = processList->begin(); - for (int i = 0; i < processList->getCount(); i++) - fprintf(opt_debug_file, ", %d (%1.2f)", relationships[i]->stream, relationships[i]->cost); - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerInnerJoin::printStartOrder() const -{ -/************************************** - * - * p r i n t B e s t O r d e r - * - ************************************** - * - * Dump finally selected stream order. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, "Start join order: with stream(baseCost)"); - bool firstStream = true; - for (int i = 0; i < innerStreams.getCount(); i++) - { - if (!innerStreams[i]->used) - fprintf(opt_debug_file, ", %d (%1.2f)", innerStreams[i]->stream, innerStreams[i]->baseCost); - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); + printCandidate(candidate); } #endif diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 6b080c7289..836b4b1130 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -23,7 +23,6 @@ #include "firebird.h" #include "../jrd/jrd.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/ext_proto.h" #include "../jrd/met_proto.h" diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index f281cc0024..08f0a37ed5 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -22,7 +22,6 @@ #include "../jrd/exe.h" #include "../jrd/btr.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/btr_proto.h" #include "../jrd/cch_proto.h" #include "../jrd/cmp_proto.h" diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index dbfd555577..b9e72b89da 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -20,7 +20,6 @@ #include "firebird.h" #include "../jrd/jrd.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index 425255707d..3c6b5fb641 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -25,7 +25,6 @@ #include "../jrd/btr.h" #include "../jrd/intl.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/dpm_proto.h" #include "../jrd/err_proto.h" diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 4a28f85dda..20f2048e76 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -28,7 +28,7 @@ #include "../common/classes/NestConst.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/req.h" -#include "../jrd/rse.h" +#include "../jrd/RecordBuffer.h" #include "firebird/impl/inf_pub.h" #include "../jrd/evl_proto.h" @@ -42,7 +42,6 @@ namespace Jrd class DeclareLocalTableNode; class Sort; class CompilerScratch; - class RecordBuffer; class BtrPageGCLock; struct index_desc; struct record_param; @@ -880,7 +879,7 @@ namespace Jrd }; public: - WindowedStream(thread_db* tdbb, CompilerScratch* csb, + WindowedStream(thread_db* tdbb, Optimizer* opt, Firebird::ObjectsArray& windows, RecordSource* next); void open(thread_db* tdbb) const override; diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 0cc73fe9d2..3b56c467ae 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -23,7 +23,6 @@ #include "firebird.h" #include "../jrd/jrd.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/met_proto.h" #include "../jrd/vio_proto.h" diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index e4d041fe98..b715c34127 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -23,10 +23,11 @@ #include "firebird.h" #include "../dsql/Nodes.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/par_proto.h" +#include "../jrd/optimizer/Optimizer.h" + #include "RecordSource.h" using namespace Firebird; @@ -182,11 +183,13 @@ namespace // ------------------------------ -WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, +WindowedStream::WindowedStream(thread_db* tdbb, Optimizer* opt, ObjectsArray& windows, RecordSource* next) - : m_next(FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, next)), - m_joinedStream(NULL) + : m_joinedStream(nullptr) { + const auto csb = opt->getCompilerScratch(); + + m_next = FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, next); m_impure = csb->allocImpure(); // Process the unpartioned and unordered map, if existent. @@ -236,7 +239,7 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, nullptr, window.map, window.frameExtent, window.exclusion); } - OPT_gen_aggregate_distincts(tdbb, csb, window.map); + opt->generateAggregateDistincts(window.map); } } @@ -317,15 +320,15 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, streams.clear(); m_joinedStream->findUsedStreams(streams); - SortedStream* sortedStream = OPT_gen_sort(tdbb, csb, streams, nullptr, - m_joinedStream, windowOrder, false, false); + const auto sortedStream = + opt->generateSort(streams, nullptr, m_joinedStream, windowOrder, false, false); m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window.stream, (window.group ? &window.group->expressions : nullptr), FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, sortedStream), window.order, window.map, window.frameExtent, window.exclusion); - OPT_gen_aggregate_distincts(tdbb, csb, window.map); + opt->generateAggregateDistincts(window.map); } } } diff --git a/src/jrd/rse.h b/src/jrd/rse.h deleted file mode 100644 index c3f967dcd5..0000000000 --- a/src/jrd/rse.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: rse.h - * DESCRIPTION: Record source block definitions - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - * - * 2001.07.28: Added rsb_t.rsb_skip to support LIMIT functionality. - */ - -#ifndef JRD_RSE_H -#define JRD_RSE_H - -#include "../include/fb_blk.h" -#include "../common/classes/array.h" -#include "../jrd/constants.h" -#include "../common/dsc.h" -#include "../jrd/lls.h" -#include "../jrd/sbm.h" -#include "../jrd/ExtEngineManager.h" -#include "../jrd/RecordBuffer.h" -#include "../dsql/Nodes.h" - -struct dsc; - -namespace Jrd { - -struct sort_key_def; -class CompilerScratch; -class BoolExprNode; - -// Array which stores relative pointers to impure areas of invariant nodes -typedef Firebird::SortedArray VarInvariantArray; - -class RseNode; - -// General optimizer block - -class OptimizerBlk : public pool_alloc -{ -public: - OptimizerBlk(MemoryPool* pool, RseNode* aRse) - : opt_pool(pool), - opt_conjuncts(*pool), - opt_streams(*pool), - rse(aRse), - outerStreams(*pool), - subStreams(*pool), - compileStreams(*pool), - beds(*pool), - localStreams(*pool), - keyStreams(*pool) - {} - -private: - Firebird::MemoryPool* opt_pool; - -public: - CompilerScratch* opt_csb; // compiler scratch block - double opt_best_cost; // cost of best join order - StreamType opt_best_count; // longest length of indexable streams - USHORT opt_base_conjuncts; // number of conjuncts in our rse, next conjuncts are distributed parent - USHORT opt_base_parent_conjuncts; // number of conjuncts in our rse + distributed with parent, next are parent - USHORT opt_base_missing_conjuncts; // number of conjuncts in our and parent rse, but without missing - - struct opt_conjunct - { - // Conjunctions and their options - BoolExprNode* opt_conjunct_node; // conjunction - UCHAR opt_conjunct_flags; - }; - - struct opt_stream - { - // Streams and their options - StreamType opt_best_stream; // stream in best join order seen so far - StreamType opt_stream_number; // stream in position of join order - }; - - Firebird::MemoryPool& getPool() - { - return *opt_pool; - } - - Firebird::HalfStaticArray opt_conjuncts; - Firebird::HalfStaticArray opt_streams; - - RseNode* const rse; - StreamList outerStreams, subStreams; - StreamList compileStreams, beds, localStreams, keyStreams; - bool favorFirstRows; -}; - -// values for opt_conjunct_flags - -const USHORT opt_conjunct_used = 1; // conjunct is used -const USHORT opt_conjunct_matched = 2; // conjunct matches an index segment - -} //namespace Jrd - -#endif // JRD_RSE_H diff --git a/src/jrd/sort.cpp b/src/jrd/sort.cpp index 923dcc8461..6916672485 100644 --- a/src/jrd/sort.cpp +++ b/src/jrd/sort.cpp @@ -38,7 +38,6 @@ #include "../common/TimeZoneUtil.h" #include "../common/gdsassert.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/val.h" #include "../jrd/err_proto.h" #include "../yvalve/gds_proto.h" diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 580bc9c30b..5b35691892 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -37,7 +37,6 @@ #include "../jrd/req.h" #include "../jrd/exe.h" #include "../jrd/extds/ExtDS.h" -#include "../jrd/rse.h" #include "../jrd/intl_classes.h" #include "../common/ThreadStart.h" #include "../jrd/TimeZone.h" diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 56c5099477..d6f55e3290 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -42,8 +42,8 @@ #include "../../jrd/evl_proto.h" #include "../../jrd/intl_proto.h" #include "../../jrd/mov_proto.h" -#include "../../jrd/opt_proto.h" #include "../../jrd/pag_proto.h" +#include "../../jrd/optimizer/Optimizer.h" #include "../../common/os/path_utils.h" #include "../../dsql/dsql_proto.h" @@ -212,7 +212,7 @@ void TraceSQLStatementImpl::fillPlan(bool explained) { m_planExplained = explained; if (m_stmt->getJrdStatement()) - m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->getJrdStatement(), m_planExplained); + m_plan = Optimizer::getPlan(JRD_get_thread_data(), m_stmt->getJrdStatement(), m_planExplained); } } diff --git a/src/jrd/validation.cpp b/src/jrd/validation.cpp index c7a05417c4..da6929ea54 100644 --- a/src/jrd/validation.cpp +++ b/src/jrd/validation.cpp @@ -550,7 +550,6 @@ VI. ADDITIONAL NOTES #include "../jrd/btr.h" #include "../jrd/btn.h" #include "../jrd/cch.h" -#include "../jrd/rse.h" #include "../jrd/tra.h" #include "../jrd/svc.h" #include "../jrd/btr_proto.h" diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 5443e920b6..180b863055 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -58,7 +58,6 @@ #include "../jrd/os/pio.h" #include "../jrd/btr.h" #include "../jrd/exe.h" -#include "../jrd/rse.h" #include "../jrd/scl.h" #include "../common/classes/alloc.h" #include "../common/ThreadStart.h" From 2b2f66c0019935f8543984d64a0a8c31d39dda02 Mon Sep 17 00:00:00 2001 From: dyemanov Date: Wed, 9 Feb 2022 10:40:45 +0300 Subject: [PATCH 066/187] Fixed the filter for optimizer files. --- builds/win32/msvc15/engine.vcxproj.filters | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index 20a3ffafd3..7bf0680e84 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -43,6 +43,9 @@ {686cba10-1e4f-44f7-b044-972ef8327197} + + {4acc7c23-ac25-4311-a075-813ed730cb40} + @@ -480,15 +483,6 @@ JRD files - - Optimizer - - - Optimizer - - - Optimizer - Replication @@ -516,6 +510,15 @@ JRD files + + Optimizer + + + Optimizer + + + Optimizer + @@ -1106,4 +1109,4 @@ Resource files - + \ No newline at end of file From 5c2d87004cc2ee20e38aca6058bf3210789f69ef Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Wed, 9 Feb 2022 14:29:20 -0300 Subject: [PATCH 067/187] Misc. --- src/jrd/optimizer/Optimizer.cpp | 11 ++++------- src/jrd/optimizer/Retrieval.cpp | 4 +--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index a548b47258..ef46e476a0 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -580,7 +580,7 @@ Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse) Optimizer::~Optimizer() { - // Release memory allocated for index descriptions + // Release memory allocated for index descriptions for (const auto compileStream : compileStreams) { delete csb->csb_rpt[compileStream].csb_idx; @@ -625,7 +625,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) // AB: If we have limit our retrieval with FIRST / SKIP syntax then // we may not deliver above conditions (from higher rse's) to this // rse, because the results should be consistent. - if (rse->rse_skip || rse->rse_first) + if (rse->rse_skip || rse->rse_first) parentStack = nullptr; // Set base-point before the parent/distributed nodes begin. @@ -1792,10 +1792,7 @@ void Optimizer::checkSorts() unsigned Optimizer::decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack) { - const auto binaryNode = nodeAs(boolNode); - const auto cmpNode = nodeAs(boolNode); - - if (binaryNode) + if (const auto binaryNode = nodeAs(boolNode)) { if (binaryNode->blrOp == blr_and) { @@ -1840,7 +1837,7 @@ unsigned Optimizer::decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack) } } } - else if (cmpNode) + else if (const auto cmpNode = nodeAs(boolNode)) { // turn a between into (a greater than or equal) AND (a less than or equal) diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 192fd8b222..5db89d3c79 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -2138,9 +2138,7 @@ bool Retrieval::validateStarts(IndexScratch* indexScratch, } // Every string starts with an empty string so don't bother using an index in that case. - const auto literal = nodeAs(value); - - if (literal) + if (const auto literal = nodeAs(value)) { if ((literal->litDesc.dsc_dtype == dtype_text && literal->litDesc.dsc_length == 0) || (literal->litDesc.dsc_dtype == dtype_varying && From 68783f0971743183a5b9b7d65d83e0829128159e Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Wed, 9 Feb 2022 15:47:58 -0300 Subject: [PATCH 068/187] Rename src/jrd/JrdStatement.* files to Statement.* --- builds/win32/msvc15/engine.vcxproj | 4 ++-- builds/win32/msvc15/engine.vcxproj.filters | 14 +++++++------- src/dsql/DsqlRequests.cpp | 2 +- src/dsql/DsqlStatements.cpp | 2 +- src/jrd/Routine.cpp | 8 ++++---- src/jrd/{JrdStatement.cpp => Statement.cpp} | 2 +- src/jrd/{JrdStatement.h => Statement.h} | 0 src/jrd/exe.h | 2 +- src/jrd/req.h | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) rename src/jrd/{JrdStatement.cpp => Statement.cpp} (99%) rename src/jrd/{JrdStatement.h => Statement.h} (100%) diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj index dd0c9ca3d4..94e4e4222b 100644 --- a/builds/win32/msvc15/engine.vcxproj +++ b/builds/win32/msvc15/engine.vcxproj @@ -92,7 +92,6 @@ - @@ -155,6 +154,7 @@ + @@ -280,7 +280,6 @@ - @@ -330,6 +329,7 @@ + diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index 7bf0680e84..3356f663a6 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -297,9 +297,6 @@ JRD files - - JRD files - JRD files @@ -366,6 +363,9 @@ JRD files + + JRD files + JRD files @@ -845,9 +845,6 @@ Header files - - Header files - Header files @@ -974,6 +971,9 @@ Header files + + Header files + Header files @@ -1109,4 +1109,4 @@ Resource files - \ No newline at end of file + diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp index dec63074ab..19b61fec96 100644 --- a/src/dsql/DsqlRequests.cpp +++ b/src/dsql/DsqlRequests.cpp @@ -24,7 +24,7 @@ #include "../dsql/dsql.h" #include "../dsql/DsqlBatch.h" #include "../dsql/Nodes.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/req.h" #include "../jrd/tra.h" #include "../jrd/replication/Publisher.h" diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp index cb1e3fc139..e6ef7f120a 100644 --- a/src/dsql/DsqlStatements.cpp +++ b/src/dsql/DsqlStatements.cpp @@ -24,7 +24,7 @@ #include "../dsql/dsql.h" #include "../dsql/Nodes.h" #include "../dsql/DsqlCompilerScratch.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../dsql/errd_proto.h" #include "../dsql/gen_proto.h" #include "../jrd/cmp_proto.h" diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index 03d19d8e30..1d78121e20 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -21,7 +21,7 @@ #include "firebird.h" #include "../jrd/Routine.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/Function.h" #include "../jrd/jrd.h" #include "../jrd/exe.h" @@ -111,8 +111,8 @@ Format* Routine::createFormat(MemoryPool& pool, IMessageMetadata* params, bool a return format; } -void Routine::setStatement(JrdStatement* value) -{ +void Routine::setStatement(JrdStatement* value) +{ statement = value; if (statement) @@ -142,7 +142,7 @@ void Routine::checkReload(thread_db* tdbb) if (!reload(tdbb)) { string err; - err.printf("Recompile of %s \"%s\" failed", + err.printf("Recompile of %s \"%s\" failed", getObjectType() == obj_udf ? "FUNCTION" : "PROCEDURE", getName().toString().c_str()); diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/Statement.cpp similarity index 99% rename from src/jrd/JrdStatement.cpp rename to src/jrd/Statement.cpp index 8988213000..e2ed504aab 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/Statement.cpp @@ -19,7 +19,7 @@ */ #include "firebird.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/Attachment.h" #include "../jrd/intl_classes.h" #include "../jrd/acl.h" diff --git a/src/jrd/JrdStatement.h b/src/jrd/Statement.h similarity index 100% rename from src/jrd/JrdStatement.h rename to src/jrd/Statement.h diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 6bf78430b5..3d175c2a86 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -470,7 +470,7 @@ public: csb_dbg_info = FB_NEW_POOL(p) Firebird::DbgInfo(p); } - // Implemented in JrdStatement.cpp + // Implemented in Statement.cpp ULONG allocImpure(ULONG align, ULONG size); template diff --git a/src/jrd/req.h b/src/jrd/req.h index 08e5375543..688386fdfc 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -31,7 +31,7 @@ #include "../jrd/exe.h" #include "../jrd/sort.h" #include "../jrd/Attachment.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/Record.h" #include "../jrd/RecordNumber.h" #include "../common/classes/timestamp.h" From 7ff8855317e8a01853d2b71daa4382e874505e0d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Wed, 9 Feb 2022 15:47:58 -0300 Subject: [PATCH 069/187] Avoid prefix Jrd in Statement class and Statement/jrd_req accessors/variables. Change DSQL methods accordingly prefixing things with Dsql to avoid things as request->getRequest() and statement->getStatement(). --- src/dsql/DdlNodes.epp | 10 +- src/dsql/DdlNodes.h | 4 +- src/dsql/DsqlBatch.cpp | 40 ++++---- src/dsql/DsqlBatch.h | 2 +- src/dsql/DsqlCompilerScratch.h | 16 ++-- src/dsql/DsqlCursor.cpp | 36 ++++---- src/dsql/DsqlCursor.h | 2 +- src/dsql/DsqlRequests.cpp | 154 +++++++++++++++---------------- src/dsql/DsqlRequests.h | 26 +++--- src/dsql/DsqlStatements.cpp | 20 ++-- src/dsql/DsqlStatements.h | 10 +- src/dsql/ExprNodes.cpp | 4 +- src/dsql/Nodes.h | 4 +- src/dsql/StmtNodes.cpp | 70 +++++++------- src/dsql/StmtNodes.h | 4 +- src/dsql/dsql.cpp | 142 ++++++++++++++-------------- src/dsql/gen.cpp | 4 +- src/gpre/int_cxx.cpp | 2 +- src/jrd/Attachment.cpp | 6 +- src/jrd/Attachment.h | 8 +- src/jrd/EngineInterface.h | 8 +- src/jrd/ExtEngineManager.cpp | 4 +- src/jrd/Monitoring.cpp | 12 +-- src/jrd/Monitoring.h | 2 +- src/jrd/PreparedStatement.cpp | 36 ++++---- src/jrd/PreparedStatement.h | 6 +- src/jrd/ResultSet.cpp | 20 ++-- src/jrd/Routine.cpp | 4 +- src/jrd/Routine.h | 8 +- src/jrd/Statement.cpp | 50 +++++----- src/jrd/Statement.h | 10 +- src/jrd/btr.h | 6 +- src/jrd/cmp.cpp | 6 +- src/jrd/cmp_proto.h | 2 +- src/jrd/dfw.epp | 6 +- src/jrd/exe.cpp | 8 +- src/jrd/extds/InternalDS.cpp | 14 +-- src/jrd/jrd.cpp | 18 ++-- src/jrd/jrd.h | 6 +- src/jrd/jrd_proto.h | 2 +- src/jrd/met.epp | 22 ++--- src/jrd/met_proto.h | 6 +- src/jrd/optimizer/Optimizer.h | 2 +- src/jrd/par.cpp | 8 +- src/jrd/par_proto.h | 6 +- src/jrd/replication/Applier.cpp | 2 +- src/jrd/req.h | 14 +-- src/jrd/trace/TraceDSQLHelpers.h | 66 ++++++------- src/jrd/trace/TraceJrdHelpers.h | 8 +- src/jrd/trace/TraceObjects.cpp | 12 +-- src/jrd/trace/TraceObjects.h | 6 +- src/jrd/vio.cpp | 2 +- 52 files changed, 471 insertions(+), 475 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 2e8f65c333..9b2dfb3d7d 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -985,7 +985,7 @@ void DdlNode::executeDdlTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc const MetaName& oldNewObjectName) { executeDdlTrigger(tdbb, transaction, when, action, objectName, oldNewObjectName, - *dsqlScratch->getStatement()->getSqlText()); + *dsqlScratch->getDsqlStatement()->getSqlText()); } void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, MetaName& name, @@ -2354,7 +2354,7 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(false); dsqlScratch->appendUChar(blr_end); @@ -2879,7 +2879,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); bool modified = false; - DsqlStatement* statement = dsqlScratch->getStatement(); + DsqlStatement* statement = dsqlScratch->getDsqlStatement(); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) P IN RDB$PROCEDURES @@ -3249,7 +3249,7 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); dsqlScratch->appendUChar(blr_end); @@ -3780,7 +3780,7 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d // The statement type may have been set incorrectly when parsing // the trigger actions, so reset it to reflect the fact that this // is a data definition statement; also reset the ddl node. - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); } invalid = false; diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 9c874557ae..ff02b91ffc 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1129,7 +1129,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType( + dsqlScratch->getDsqlStatement()->setType( legacy ? DsqlStatement::TYPE_SET_GENERATOR : DsqlStatement::TYPE_DDL); return this; } @@ -2417,7 +2417,7 @@ public: public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType( + dsqlScratch->getDsqlStatement()->setType( create ? DsqlStatement::TYPE_CREATE_DB : DsqlStatement::TYPE_DDL); return this; } diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp index 0cdb773400..f5b249cc1b 100644 --- a/src/dsql/DsqlBatch.cpp +++ b/src/dsql/DsqlBatch.cpp @@ -61,14 +61,14 @@ namespace { } DsqlBatch::DsqlBatch(DsqlDmlRequest* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) - : m_request(req), + : m_dsqlRequest(req), m_batch(NULL), m_meta(inMeta), - m_messages(m_request->getPool()), - m_blobs(m_request->getPool()), - m_blobMap(m_request->getPool()), - m_blobMeta(m_request->getPool()), - m_defaultBpb(m_request->getPool()), + m_messages(m_dsqlRequest->getPool()), + m_blobs(m_dsqlRequest->getPool()), + m_blobMap(m_dsqlRequest->getPool()), + m_blobMeta(m_dsqlRequest->getPool()), + m_defaultBpb(m_dsqlRequest->getPool()), m_messageSize(0), m_alignedMessage(0), m_alignment(0), @@ -168,13 +168,13 @@ DsqlBatch::~DsqlBatch() { if (m_batch) m_batch->resetHandle(); - if (m_request) - m_request->req_batch = NULL; + if (m_dsqlRequest) + m_dsqlRequest->req_batch = NULL; } Attachment* DsqlBatch::getAttachment() const { - return m_request->req_dbb->dbb_attachment; + return m_dsqlRequest->req_dbb->dbb_attachment; } void DsqlBatch::setInterfacePtr(JBatch* interfacePtr) throw() @@ -205,13 +205,13 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadat // Sanity checks before creating batch - if (!req->getJrdRequest()) + if (!req->getRequest()) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << Arg::Gds(isc_unprepared_stmt)); } - const DsqlStatement* statement = req->getStatement(); + const auto statement = req->getDsqlStatement(); if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { @@ -451,7 +451,7 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb) jrd_tra* transaction = tdbb->getTransaction(); // execution timer - thread_db::TimerGuard timerGuard(tdbb, m_request->setupTimer(tdbb), true); + thread_db::TimerGuard timerGuard(tdbb, m_dsqlRequest->setupTimer(tdbb), true); // sync internal buffers m_messages.done(); @@ -651,20 +651,20 @@ private: } // execute request - m_request->req_transaction = transaction; - jrd_req* req = m_request->getJrdRequest(); + m_dsqlRequest->req_transaction = transaction; + jrd_req* req = m_dsqlRequest->getRequest(); fb_assert(req); // prepare completion interface AutoPtr completionState (FB_NEW BatchCompletionState(m_flags & (1 << IBatch::TAG_RECORD_COUNTS), m_detailed)); AutoSetRestore batchFlag(&req->req_batch_mode, true); - const dsql_msg* message = m_request->getStatement()->getSendMsg(); + const dsql_msg* message = m_dsqlRequest->getDsqlStatement()->getSendMsg(); bool startRequest = true; - bool isExecBlock = m_request->getStatement()->getType() == DsqlStatement::TYPE_EXEC_BLOCK; - const auto receiveMessage = isExecBlock ? m_request->getStatement()->getReceiveMsg() : nullptr; - auto receiveMsgBuffer = isExecBlock ? m_request->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr; + bool isExecBlock = m_dsqlRequest->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_BLOCK; + const auto receiveMessage = isExecBlock ? m_dsqlRequest->getDsqlStatement()->getReceiveMsg() : nullptr; + auto receiveMsgBuffer = isExecBlock ? m_dsqlRequest->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr; // process messages ULONG remains; @@ -720,11 +720,11 @@ private: } // map message to internal engine format - m_request->mapInOut(tdbb, false, message, m_meta, NULL, data); + m_dsqlRequest->mapInOut(tdbb, false, message, m_meta, NULL, data); data += m_messageSize; remains -= m_messageSize; - UCHAR* msgBuffer = m_request->req_msg_buffers[message->msg_buffer_number]; + UCHAR* msgBuffer = m_dsqlRequest->req_msg_buffers[message->msg_buffer_number]; try { // runsend data to request and collect stats diff --git a/src/dsql/DsqlBatch.h b/src/dsql/DsqlBatch.h index 06e9182b64..b3ef18d9d4 100644 --- a/src/dsql/DsqlBatch.h +++ b/src/dsql/DsqlBatch.h @@ -101,7 +101,7 @@ private: m_flags &= ~(1 << bit); } - DsqlDmlRequest* const m_request; + DsqlDmlRequest* const m_dsqlRequest; JBatch* m_batch; Firebird::IMessageMetadata* m_meta; diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index a29731e380..12e4fd76b3 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -74,11 +74,11 @@ public: public: DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction, - DsqlStatement* aStatement = nullptr, DsqlCompilerScratch* aMainScratch = nullptr) + DsqlStatement* aDsqlStatement = nullptr, DsqlCompilerScratch* aMainScratch = nullptr) : BlrDebugWriter(p), dbb(aDbb), transaction(aTransaction), - statement(aStatement), + dsqlStatement(aDsqlStatement), flags(0), nestingLevel(0), relation(NULL), @@ -144,7 +144,7 @@ public: public: virtual bool isVersion4() { - return statement->getBlrVersion() == 4; + return dsqlStatement->getBlrVersion() == 4; } MemoryPool& getPool() @@ -167,14 +167,14 @@ public: transaction = value; } - DsqlStatement* getStatement() const + DsqlStatement* getDsqlStatement() const { - return statement; + return dsqlStatement; } - void setStatement(DsqlStatement* aStatement) + void setDsqlStatement(DsqlStatement* aDsqlStatement) { - statement = aStatement; + dsqlStatement = aDsqlStatement; } void putBlrMarkers(ULONG marks); @@ -272,7 +272,7 @@ private: dsql_dbb* dbb; // DSQL attachment jrd_tra* transaction; // Transaction - DsqlStatement* statement; // Compiled statement + DsqlStatement* dsqlStatement; // DSQL statement public: unsigned flags; // flags diff --git a/src/dsql/DsqlCursor.cpp b/src/dsql/DsqlCursor.cpp index ba238e438b..e94e9451fe 100644 --- a/src/dsql/DsqlCursor.cpp +++ b/src/dsql/DsqlCursor.cpp @@ -34,12 +34,12 @@ static const char* const SCRATCH = "fb_cursor_"; static const ULONG PREFETCH_SIZE = 65536; // 64 KB DsqlCursor::DsqlCursor(DsqlDmlRequest* req, ULONG flags) - : m_request(req), m_message(req->getStatement()->getReceiveMsg()), + : m_dsqlRequest(req), m_message(req->getDsqlStatement()->getReceiveMsg()), m_resultSet(NULL), m_flags(flags), m_space(req->getPool(), SCRATCH), m_state(BOS), m_eof(false), m_position(0), m_cachedCount(0) { - TRA_link_cursor(m_request->req_transaction, this); + TRA_link_cursor(m_dsqlRequest->req_transaction, this); } DsqlCursor::~DsqlCursor() @@ -50,12 +50,12 @@ DsqlCursor::~DsqlCursor() jrd_tra* DsqlCursor::getTransaction() const { - return m_request->req_transaction; + return m_dsqlRequest->req_transaction; } Attachment* DsqlCursor::getAttachment() const { - return m_request->req_dbb->dbb_attachment; + return m_dsqlRequest->req_dbb->dbb_attachment; } void DsqlCursor::setInterfacePtr(JResultSet* interfacePtr) throw() @@ -70,34 +70,34 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) return; const auto attachment = cursor->getAttachment(); - const auto request = cursor->m_request; + const auto dsqlRequest = cursor->m_dsqlRequest; - if (request->getJrdRequest()) + if (dsqlRequest->getRequest()) { ThreadStatusGuard status_vector(tdbb); try { // Report some remaining fetches if any - if (request->req_fetch_baseline) + if (dsqlRequest->req_fetch_baseline) { - TraceDSQLFetch trace(attachment, request); + TraceDSQLFetch trace(attachment, dsqlRequest); trace.fetch(true, ITracePlugin::RESULT_SUCCESS); } - if (request->req_traced && TraceManager::need_dsql_free(attachment)) + if (dsqlRequest->req_traced && TraceManager::need_dsql_free(attachment)) { - TraceSQLStatementImpl stmt(request, NULL); + TraceSQLStatementImpl stmt(dsqlRequest, NULL); TraceManager::event_dsql_free(attachment, &stmt, DSQL_close); } - JRD_unwind_request(tdbb, request->getJrdRequest()); + JRD_unwind_request(tdbb, dsqlRequest->getRequest()); } catch (Firebird::Exception&) {} // no-op } - request->req_cursor = NULL; - TRA_unlink_cursor(request->req_transaction, cursor); + dsqlRequest->req_cursor = NULL; + TRA_unlink_cursor(dsqlRequest->req_transaction, cursor); delete cursor; } @@ -105,7 +105,7 @@ int DsqlCursor::fetchNext(thread_db* tdbb, UCHAR* buffer) { if (!(m_flags & IStatement::CURSOR_TYPE_SCROLLABLE)) { - m_eof = !m_request->fetch(tdbb, buffer); + m_eof = !m_dsqlRequest->fetch(tdbb, buffer); if (m_eof) { @@ -223,13 +223,13 @@ int DsqlCursor::fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 positio fb_assert(position < m_cachedCount); - UCHAR* const msgBuffer = m_request->req_msg_buffers[m_message->msg_buffer_number]; + UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number]; const FB_UINT64 offset = position * m_message->msg_length; const FB_UINT64 readBytes = m_space.read(offset, msgBuffer, m_message->msg_length); fb_assert(readBytes == m_message->msg_length); - m_request->mapInOut(tdbb, true, m_message, NULL, buffer); + m_dsqlRequest->mapInOut(tdbb, true, m_message, NULL, buffer); m_position = position; m_state = POSITIONED; @@ -241,13 +241,13 @@ bool DsqlCursor::cacheInput(thread_db* tdbb, FB_UINT64 position) fb_assert(!m_eof); const ULONG prefetchCount = MAX(PREFETCH_SIZE / m_message->msg_length, 1); - const UCHAR* const msgBuffer = m_request->req_msg_buffers[m_message->msg_buffer_number]; + const UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number]; while (position >= m_cachedCount) { for (ULONG count = 0; count < prefetchCount; count++) { - if (!m_request->fetch(tdbb, NULL)) + if (!m_dsqlRequest->fetch(tdbb, NULL)) { m_eof = true; break; diff --git a/src/dsql/DsqlCursor.h b/src/dsql/DsqlCursor.h index 75392a47f9..bf47e542c0 100644 --- a/src/dsql/DsqlCursor.h +++ b/src/dsql/DsqlCursor.h @@ -65,7 +65,7 @@ private: int fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position); bool cacheInput(thread_db* tdbb, FB_UINT64 position = MAX_UINT64); - DsqlDmlRequest* const m_request; + DsqlDmlRequest* const m_dsqlRequest; const dsql_msg* const m_message; JResultSet* m_resultSet; const ULONG m_flags; diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp index 19b61fec96..c5b34e8503 100644 --- a/src/dsql/DsqlRequests.cpp +++ b/src/dsql/DsqlRequests.cpp @@ -43,10 +43,10 @@ static void checkD(IStatus* st); // DsqlRequest -DsqlRequest::DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement) +DsqlRequest::DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aDsqlStatement) : PermanentStorage(pool), req_dbb(dbb), - statement(aStatement) + dsqlStatement(aDsqlStatement) { } @@ -100,17 +100,17 @@ void DsqlRequest::setTimeout(unsigned int timeOut) TimeoutTimer* DsqlRequest::setupTimer(thread_db* tdbb) { - auto jrdRequest = getJrdRequest(); + auto request = getRequest(); - if (jrdRequest) + if (request) { - if (jrdRequest->hasInternalStatement()) + if (request->hasInternalStatement()) return req_timer; - jrdRequest->req_timeout = this->req_timeout; + request->req_timeout = this->req_timeout; - fb_assert(!jrdRequest->req_caller); - if (jrdRequest->req_caller) + fb_assert(!request->req_caller); + if (request->req_caller) { if (req_timer) req_timer->setup(0, 0); @@ -146,8 +146,8 @@ TimeoutTimer* DsqlRequest::setupTimer(thread_db* tdbb) if (!req_timer && timeOut) { req_timer = FB_NEW TimeoutTimer(); - fb_assert(jrdRequest); - jrdRequest->req_timer = this->req_timer; + fb_assert(request); + request->req_timer = this->req_timer; } if (req_timer) @@ -160,19 +160,19 @@ TimeoutTimer* DsqlRequest::setupTimer(thread_db* tdbb) } // Release a dynamic request. -void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* request) +void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* dsqlRequest) { SET_TDBB(tdbb); - if (request->req_timer) + if (dsqlRequest->req_timer) { - request->req_timer->stop(); - request->req_timer = nullptr; + dsqlRequest->req_timer->stop(); + dsqlRequest->req_timer = nullptr; } // If request is parent, orphan the children and release a portion of their requests - for (auto childStatement : request->cursors) + for (auto childStatement : dsqlRequest->cursors) { childStatement->addFlags(DsqlStatement::FLAG_ORPHAN); childStatement->setParentRequest(nullptr); @@ -189,37 +189,37 @@ void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* request) // If the request had an open cursor, close it - if (request->req_cursor) - DsqlCursor::close(tdbb, request->req_cursor); + if (dsqlRequest->req_cursor) + DsqlCursor::close(tdbb, dsqlRequest->req_cursor); - if (request->req_batch) + if (dsqlRequest->req_batch) { - delete request->req_batch; - request->req_batch = nullptr; + delete dsqlRequest->req_batch; + dsqlRequest->req_batch = nullptr; } - Jrd::Attachment* att = request->req_dbb->dbb_attachment; - const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); + Jrd::Attachment* att = dsqlRequest->req_dbb->dbb_attachment; + const bool need_trace_free = dsqlRequest->req_traced && TraceManager::need_dsql_free(att); if (need_trace_free) { - TraceSQLStatementImpl stmt(request, NULL); + TraceSQLStatementImpl stmt(dsqlRequest, NULL); TraceManager::event_dsql_free(att, &stmt, DSQL_drop); } - if (request->req_cursor_name.hasData()) - request->req_dbb->dbb_cursors.remove(request->req_cursor_name); + if (dsqlRequest->req_cursor_name.hasData()) + dsqlRequest->req_dbb->dbb_cursors.remove(dsqlRequest->req_cursor_name); // If a request has been compiled, release it now - if (request->getJrdRequest()) - EXE_release(tdbb, request->getJrdRequest()); + if (dsqlRequest->getRequest()) + EXE_release(tdbb, dsqlRequest->getRequest()); // Increase the statement refCount so its pool is not destroyed before the request is gone. - auto statement = request->getStatement(); + auto dsqlStatement = dsqlRequest->getDsqlStatement(); // Release the entire request - delete request; + delete dsqlRequest; - statement = nullptr; + dsqlStatement = nullptr; } // Parse the message of a request. @@ -333,13 +333,13 @@ DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, req_msg_buffers.add(msgBuffer); } - jrdRequest = aStatement->getJrdStatement()->findRequest(tdbb); - tdbb->getAttachment()->att_requests.add(jrdRequest); + request = aStatement->getStatement()->findRequest(tdbb); + tdbb->getAttachment()->att_requests.add(request); } -JrdStatement* DsqlDmlRequest::getJrdStatement() const +Statement* DsqlDmlRequest::getStatement() const { - return jrdRequest ? jrdRequest->getStatement() : nullptr; + return request ? request->getStatement() : nullptr; } // Provide backward-compatibility @@ -364,10 +364,8 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) Jrd::ContextPoolHolder context(tdbb, &getPool()); - const auto statement = getStatement(); - // if the cursor isn't open, we've got a problem - if (statement->isCursorBased()) + if (dsqlStatement->isCursorBased()) { if (!req_cursor) { @@ -377,13 +375,13 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) } } - if (!jrdRequest) + if (!request) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << Arg::Gds(isc_unprepared_stmt)); } - dsql_msg* message = (dsql_msg*) statement->getReceiveMsg(); + dsql_msg* message = (dsql_msg*) dsqlStatement->getReceiveMsg(); if (delayedFormat && message) { @@ -410,11 +408,11 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) fb_assert(tra == req_transaction); } else - JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, dsqlMsgBuffer); + JRD_receive(tdbb, request, message->msg_number, message->msg_length, dsqlMsgBuffer); firstRowFetched = true; - const dsql_par* const eof = statement->getEof(); + const dsql_par* const eof = dsqlStatement->getEof(); const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; const bool eofReached = eof && !(*eofPtr); @@ -517,7 +515,7 @@ DsqlCursor* DsqlDmlRequest::openCursor(thread_db* tdbb, jrd_tra** traHandle, Jrd::ContextPoolHolder context(tdbb, &getPool()); - if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) + if (dsqlStatement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -533,7 +531,7 @@ DsqlCursor* DsqlDmlRequest::openCursor(thread_db* tdbb, jrd_tra** traHandle, // Validate statement type - if (!statement->isCursorBased()) + if (!dsqlStatement->isCursorBased()) Arg::Gds(isc_no_cursor).raise(); // Validate cursor or batch being not already open @@ -569,28 +567,28 @@ void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, bool singleton) { firstRowFetched = false; - const dsql_msg* message = statement->getSendMsg(); + const dsql_msg* message = dsqlStatement->getSendMsg(); if (!message) - JRD_start(tdbb, jrdRequest, req_transaction); + JRD_start(tdbb, request, req_transaction); else { UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_start_and_send(tdbb, jrdRequest, req_transaction, message->msg_number, + JRD_start_and_send(tdbb, request, req_transaction, message->msg_number, message->msg_length, msgBuffer); } // Selectable execute block should get the "proc fetch" flag assigned, // which ensures that the savepoint stack is preserved while suspending - if (statement->getType() == DsqlStatement::TYPE_SELECT_BLOCK) - jrdRequest->req_flags |= req_proc_fetch; + if (dsqlStatement->getType() == DsqlStatement::TYPE_SELECT_BLOCK) + request->req_flags |= req_proc_fetch; // TYPE_EXEC_BLOCK has no outputs so there are no out_msg // supplied from client side, but TYPE_EXEC_BLOCK requires // 2-byte message for EOS synchronization - const bool isBlock = (statement->getType() == DsqlStatement::TYPE_EXEC_BLOCK); + const bool isBlock = (dsqlStatement->getType() == DsqlStatement::TYPE_EXEC_BLOCK); - message = statement->getReceiveMsg(); + message = dsqlStatement->getReceiveMsg(); if (outMetadata == DELAYED_OUT_FORMAT) { @@ -619,7 +617,7 @@ void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); } - JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, msgBuffer); + JRD_receive(tdbb, request, message->msg_number, message->msg_length, msgBuffer); if (outMsg) mapInOut(tdbb, true, message, NULL, outMsg); @@ -647,7 +645,7 @@ void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, try { - JRD_receive(tdbb, jrdRequest, message->msg_number, + JRD_receive(tdbb, request, message->msg_number, message->msg_length, message_buffer); status = FB_SUCCESS; } @@ -672,10 +670,10 @@ void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, } } - switch (statement->getType()) + switch (dsqlStatement->getType()) { case DsqlStatement::TYPE_UPDATE_CURSOR: - if (!jrdRequest->req_records_updated) + if (!request->req_records_updated) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << Arg::Gds(isc_deadlock) << @@ -684,7 +682,7 @@ void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, break; case DsqlStatement::TYPE_DELETE_CURSOR: - if (!jrdRequest->req_records_deleted) + if (!request->req_records_deleted) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << Arg::Gds(isc_deadlock) << @@ -706,7 +704,7 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, IMessageMetadata* outMetadata, UCHAR* outMsg, bool singleton) { - if (!jrdRequest) + if (!request) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << Arg::Gds(isc_unprepared_stmt)); @@ -714,7 +712,7 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, // If there is no data required, just start the request - const dsql_msg* message = statement->getSendMsg(); + const dsql_msg* message = dsqlStatement->getSendMsg(); if (message) mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); @@ -723,7 +721,7 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, TraceDSQLExecute trace(req_dbb->dbb_attachment, this); // Setup and start timeout timer - const bool have_cursor = statement->isCursorBased() && !singleton; + const bool have_cursor = dsqlStatement->isCursorBased() && !singleton; setupTimer(tdbb); thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); @@ -741,7 +739,7 @@ void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHa IMessageMetadata* outMetadata, UCHAR* outMsg, bool singleton, bool exec, bool fetch) { - jrdRequest->req_flags &= ~req_update_conflict; + request->req_flags &= ~req_update_conflict; int numTries = 0; const int MAX_RESTARTS = 10; @@ -753,7 +751,7 @@ void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHa // It allows to raise update conflict error (if any) as usual and // handle error by PSQL handler. const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; - AutoSetRestoreFlag restartReady(&jrdRequest->req_flags, flag, true); + AutoSetRestoreFlag restartReady(&request->req_flags, flag, true); try { if (exec) @@ -761,24 +759,24 @@ void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHa if (fetch) { - fb_assert(statement->isCursorBased()); + fb_assert(dsqlStatement->isCursorBased()); - const dsql_msg* message = statement->getReceiveMsg(); + const dsql_msg* message = dsqlStatement->getReceiveMsg(); UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_receive(tdbb, jrdRequest, message->msg_number, message->msg_length, dsqlMsgBuffer); + JRD_receive(tdbb, request, message->msg_number, message->msg_length, dsqlMsgBuffer); } } catch (const status_exception&) { if (!(req_transaction->tra_flags & TRA_ex_restart)) { - jrdRequest->req_flags &= ~req_update_conflict; + request->req_flags &= ~req_update_conflict; throw; } } - if (!(jrdRequest->req_flags & req_update_conflict)) + if (!(request->req_flags & req_update_conflict)) { fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); req_transaction->tra_flags &= ~TRA_ex_restart; @@ -798,7 +796,7 @@ void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHa fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); - jrdRequest->req_flags &= ~req_update_conflict; + request->req_flags &= ~req_update_conflict; req_transaction->tra_flags &= ~TRA_ex_restart; fb_utils::init_status(tdbb->tdbb_status_vector); @@ -811,7 +809,7 @@ void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHa { gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" "\tafter %d attempts of restart.\n" - "\tQuery:\n%s\n", numTries, jrdRequest->getStatement()->sqlText->c_str() ); + "\tQuery:\n%s\n", numTries, request->getStatement()->sqlText->c_str() ); } // When restart we must execute query @@ -946,15 +944,15 @@ void DsqlDmlRequest::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && - (parameter = statement->getDbKey())) + if (!toExternal && (dbkey = dsqlStatement->getParentDbKey()) && + (parameter = dsqlStatement->getDbKey())) { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; + UCHAR* parentMsgBuffer = dsqlStatement->getParentRequest() ? + dsqlStatement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; fb_assert(parentMsgBuffer); @@ -979,11 +977,11 @@ void DsqlDmlRequest::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* } const dsql_par* rec_version; - if (!toExternal && (rec_version = statement->getParentRecVersion()) && - (parameter = statement->getRecVersion())) + if (!toExternal && (rec_version = dsqlStatement->getParentRecVersion()) && + (parameter = dsqlStatement->getRecVersion())) { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : + UCHAR* parentMsgBuffer = dsqlStatement->getParentRequest() ? + dsqlStatement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : NULL; UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; @@ -1013,7 +1011,7 @@ void DsqlDmlRequest::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* // DsqlDdlRequest DsqlDdlRequest::DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode) - : DsqlRequest(pool, dbb, aInternalScratch->getStatement()), + : DsqlRequest(pool, dbb, aInternalScratch->getDsqlStatement()), internalScratch(aInternalScratch), node(aNode) { @@ -1043,7 +1041,7 @@ void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); if (!isInternalRequest && node->mustBeReplicated()) - REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); + REPL_exec_sql(tdbb, req_transaction, getDsqlStatement()->getOrgText()); } catch (status_exception& ex) { diff --git a/src/dsql/DsqlRequests.h b/src/dsql/DsqlRequests.h index e08764922c..7b41152c11 100644 --- a/src/dsql/DsqlRequests.h +++ b/src/dsql/DsqlRequests.h @@ -43,7 +43,7 @@ class DsqlDmlStatement; class dsql_par; class jrd_req; class jrd_tra; -class JrdStatement; +class Statement; class SessionManagementNode; class TransactionNode; @@ -60,17 +60,17 @@ public: return req_transaction; } - Firebird::RefPtr getStatement() + Firebird::RefPtr getDsqlStatement() { - return statement; + return dsqlStatement; } - virtual JrdStatement* getJrdStatement() const + virtual Statement* getStatement() const { return nullptr; } - virtual jrd_req* getJrdRequest() const + virtual jrd_req* getRequest() const { return nullptr; } @@ -126,7 +126,7 @@ public: public: dsql_dbb* req_dbb; // DSQL attachment - Firebird::RefPtr statement; + Firebird::RefPtr dsqlStatement; Firebird::Array cursors{getPool()}; // Cursor update statements jrd_tra* req_transaction = nullptr; // JRD transaction @@ -150,19 +150,19 @@ protected: class DsqlDmlRequest final : public DsqlRequest { public: - DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement); + DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aDsqlStatement); // Reintroduce method to fake covariant return type with RefPtr. - auto getStatement() + auto getDsqlStatement() { - return Firebird::RefPtr((DsqlDmlStatement*) statement.getPtr()); + return Firebird::RefPtr((DsqlDmlStatement*) dsqlStatement.getPtr()); } - JrdStatement* getJrdStatement() const override; + Statement* getStatement() const override; - jrd_req* getJrdRequest() const override + jrd_req* getRequest() const override { - return jrdRequest; + return request; } bool isDml() const override @@ -209,7 +209,7 @@ public: private: Firebird::RefPtr delayedFormat; - jrd_req* jrdRequest = nullptr; + jrd_req* request = nullptr; bool needDelayedFormat = false; bool firstRowFetched = false; }; diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp index e6ef7f120a..9f51ce4153 100644 --- a/src/dsql/DsqlStatements.cpp +++ b/src/dsql/DsqlStatements.cpp @@ -103,14 +103,14 @@ void DsqlDmlStatement::doRelease() parent->cursors.remove(pos); } - if (jrdStatement) + if (statement) { thread_db* tdbb = JRD_get_thread_data(); ThreadStatusGuard status_vector(tdbb); try { - jrdStatement->release(tdbb); + statement->release(tdbb); } catch (Exception&) {} // no-op @@ -127,9 +127,9 @@ void DsqlDmlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, n } if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); + scratch->getDsqlStatement()->setBlrVersion(5); else - scratch->getStatement()->setBlrVersion(4); + scratch->getDsqlStatement()->setBlrVersion(4); GEN_statement(scratch, node); @@ -171,17 +171,17 @@ void DsqlDmlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, n const auto& blr = scratch->getBlrData(); const auto& debugData = scratch->getDebugData(); - jrdStatement = CMP_compile(tdbb, blr.begin(), blr.getCount(), + statement = CMP_compile(tdbb, blr.begin(), blr.getCount(), (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST), debugData.getCount(), debugData.begin()); if (getSqlText()) - jrdStatement->sqlText = getSqlText(); + statement->sqlText = getSqlText(); - fb_assert(jrdStatement->blr.isEmpty()); + fb_assert(statement->blr.isEmpty()); if (attachment->getDebugOptions().getDsqlKeepBlr()) - jrdStatement->blr.insert(0, blr.begin(), blr.getCount()); + statement->blr.insert(0, blr.begin(), blr.getCount()); } catch (const Exception&) { @@ -264,9 +264,9 @@ void DsqlDdlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, n } if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); + scratch->getDsqlStatement()->setBlrVersion(5); else - scratch->getStatement()->setBlrVersion(4); + scratch->getDsqlStatement()->setBlrVersion(4); this->scratch = scratch; } diff --git a/src/dsql/DsqlStatements.h b/src/dsql/DsqlStatements.h index 1918e3f82b..fc88b53662 100644 --- a/src/dsql/DsqlStatements.h +++ b/src/dsql/DsqlStatements.h @@ -39,7 +39,7 @@ class dsql_msg; class dsql_par; class DsqlRequest; class DsqlCompilerScratch; -class JrdStatement; +class Statement; class SessionManagementNode; class TransactionNode; @@ -134,7 +134,7 @@ public: void setEof(dsql_par* value) { eof = value; } public: - virtual JrdStatement* getJrdStatement() const + virtual Statement* getStatement() const { return nullptr; } @@ -182,9 +182,9 @@ public: } public: - JrdStatement* getJrdStatement() const override + Statement* getStatement() const override { - return jrdStatement; + return statement; } void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; @@ -214,7 +214,7 @@ protected: private: NestConst node; - JrdStatement* jrdStatement = nullptr; + Statement* statement = nullptr; dsql_par* dbKey = nullptr; // Database key for current of dsql_par* recVersion = nullptr; // Record Version for current of dsql_par* parentRecVersion = nullptr; // parent record version diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 66d3ba7d5f..45224c19fb 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -9484,7 +9484,7 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) auto msg = dsqlMessage ? dsqlMessage : dsqlParameter ? dsqlParameter->par_message : - dsqlScratch->getStatement()->getSendMsg(); + dsqlScratch->getDsqlStatement()->getSendMsg(); auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool()); node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr); @@ -9556,7 +9556,7 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, if (!dsqlParameter) { - dsqlParameter = MAKE_parameter(dsqlScratch->getStatement()->getSendMsg(), true, true, + dsqlParameter = MAKE_parameter(dsqlScratch->getDsqlStatement()->getSendMsg(), true, true, dsqlParameterIndex, NULL); dsqlParameterIndex = dsqlParameter->par_index; } diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 129b39cde6..65b20f4e15 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -213,7 +213,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); return this; } @@ -300,7 +300,7 @@ public: { Node::dsqlPass(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SESSION_MANAGEMENT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_SESSION_MANAGEMENT); return this; } diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 49a97caa22..a000c7199f 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -2076,7 +2076,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(SUB_ROUTINE_TYPE_PSQL); dsqlScratch->appendUChar( - blockScratch->getStatement()->getFlags() & DsqlStatement::FLAG_SELECTABLE ? 1 : 0); + blockScratch->getDsqlStatement()->getFlags() & DsqlStatement::FLAG_SELECTABLE ? 1 : 0); genParameters(dsqlScratch, dsqlBlock->parameters); genParameters(dsqlScratch, dsqlBlock->returns); @@ -2290,7 +2290,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return SavepointEncloseNode::make(dsqlScratch->getPool(), dsqlScratch, node); } - dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? + dsqlScratch->getDsqlStatement()->setType(dsqlCursorName.hasData() ? DsqlStatement::TYPE_DELETE_CURSOR : DsqlStatement::TYPE_DELETE); // Generate record selection expression. @@ -2377,7 +2377,7 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) else { dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); } } @@ -2990,7 +2990,7 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } if (!dsqlScratch->isPsql()) - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE); ExecProcedureNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName); node->dsqlProcedure = procedure; @@ -3081,7 +3081,7 @@ ValueListNode* ExecProcedureNode::explodeOutputs(DsqlCompilerScratch* dsqlScratc *ptr = paramNode; dsql_par* parameter = paramNode->dsqlParameter = MAKE_parameter( - dsqlScratch->getStatement()->getReceiveMsg(), true, true, 0, NULL); + dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, NULL); paramNode->dsqlParameterIndex = parameter->par_index; DsqlDescMaker::fromField(¶meter->par_desc, field); @@ -3112,9 +3112,9 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) { const dsql_msg* message = NULL; - if (dsqlScratch->getStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE) + if (dsqlScratch->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE) { - if ((message = dsqlScratch->getStatement()->getReceiveMsg())) + if ((message = dsqlScratch->getDsqlStatement()->getReceiveMsg())) { dsqlScratch->appendUChar(blr_begin); dsqlScratch->appendUChar(blr_send); @@ -4267,7 +4267,7 @@ const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, Exe ExecBlockNode* ExecBlockNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); if (returns.hasData()) statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); @@ -4404,7 +4404,7 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } - DsqlStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); dsqlScratch->appendUChar(blr_begin); @@ -5968,7 +5968,7 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (!dsqlScratch->isPsql()) { // Describe it as TYPE_RETURNING_CURSOR if RETURNING is present or as INSERT otherwise. - dsqlScratch->getStatement()->setType(returning ? + dsqlScratch->getDsqlStatement()->setType(returning ? DsqlStatement::TYPE_RETURNING_CURSOR : DsqlStatement::TYPE_INSERT); } @@ -6529,7 +6529,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up return node; } - dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? + dsqlScratch->getDsqlStatement()->setType(dsqlCursorName.hasData() ? DsqlStatement::TYPE_UPDATE_CURSOR : DsqlStatement::TYPE_UPDATE); doDsqlPass(dsqlScratch, node->dsqlRelation, relation, false); @@ -6656,7 +6656,7 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) else { dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); } } @@ -7461,7 +7461,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, { DsqlContextStack::AutoRestore autoContext(*dsqlScratch->context); - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_INSERT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_INSERT); const auto node = FB_NEW_POOL(dsqlScratch->getPool()) StoreNode(dsqlScratch->getPool()); node->overrideClause = overrideClause; @@ -7671,7 +7671,7 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) else if (!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT)) { dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); } } @@ -8164,8 +8164,8 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (dsqlForUpdate) { - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SELECT_UPD); - dsqlScratch->getStatement()->addFlags(DsqlStatement::FLAG_NO_BATCH); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_SELECT_UPD); + dsqlScratch->getDsqlStatement()->addFlags(DsqlStatement::FLAG_NO_BATCH); } else { @@ -8177,8 +8177,8 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (rseNode->dsqlOrder || rseNode->dsqlDistinct) { - dsqlScratch->getStatement()->setFlags( - dsqlScratch->getStatement()->getFlags() & ~DsqlStatement::FLAG_NO_BATCH); + dsqlScratch->getDsqlStatement()->setFlags( + dsqlScratch->getDsqlStatement()->getFlags() & ~DsqlStatement::FLAG_NO_BATCH); } } @@ -8204,7 +8204,7 @@ void SelectNode::genBlr(DsqlCompilerScratch* dsqlScratch) RseNode* const rse = nodeAs(dsqlRse); fb_assert(rse); - DsqlStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); // Set up parameter for things in the select list. ValueListNode* list = rse->dsqlSelectList; @@ -8532,7 +8532,7 @@ DmlNode* SuspendNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); if (dsqlScratch->flags & (DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)) { @@ -8790,7 +8790,7 @@ const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_START_TRANS); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_START_TRANS); // Generate tpb for set transaction. Use blr string of dsqlScratch. // If a value is not specified, default is not stuffed, let the engine handle it. @@ -9365,7 +9365,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // If RETURNING is present, type is already DsqlStatement::TYPE_EXEC_PROCEDURE. if (!returning) - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_INSERT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_INSERT); return SavepointEncloseNode::make(dsqlScratch->getPool(), dsqlScratch, node); } @@ -9438,12 +9438,12 @@ CommitRollbackNode* CommitRollbackNode::dsqlPass(DsqlCompilerScratch* dsqlScratc switch (command) { case CMD_COMMIT: - dsqlScratch->getStatement()->setType(retain ? + dsqlScratch->getDsqlStatement()->setType(retain ? DsqlStatement::TYPE_COMMIT_RETAIN : DsqlStatement::TYPE_COMMIT); break; case CMD_ROLLBACK: - dsqlScratch->getStatement()->setType(retain ? + dsqlScratch->getDsqlStatement()->setType(retain ? DsqlStatement::TYPE_ROLLBACK_RETAIN : DsqlStatement::TYPE_ROLLBACK); break; } @@ -9499,7 +9499,7 @@ Firebird::string UserSavepointNode::internalPrint(NodePrinter& printer) const UserSavepointNode* UserSavepointNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlStatement::TYPE_SAVEPOINT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_SAVEPOINT); return this; } @@ -9666,7 +9666,7 @@ static void dsqlGenEofAssignment(DsqlCompilerScratch* dsqlScratch, SSHORT value) dsqlScratch->appendUChar(blr_assignment); LiteralNode::genConstant(dsqlScratch, &valueDesc, false); - GEN_parameter(dsqlScratch, dsqlScratch->getStatement()->getEof()); + GEN_parameter(dsqlScratch, dsqlScratch->getDsqlStatement()->getEof()); } static void dsqlGenReturning(DsqlCompilerScratch* dsqlScratch, ReturningClause* returning, @@ -9734,7 +9734,7 @@ static void dsqlGenReturningLocalTableCursor(DsqlCompilerScratch* dsqlScratch, R dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); dsqlScratch->appendUChar(blr_begin); @@ -9752,7 +9752,7 @@ static void dsqlGenReturningLocalTableCursor(DsqlCompilerScratch* dsqlScratch, R dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); dsqlGenEofAssignment(dsqlScratch, 0); } @@ -10004,8 +10004,8 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const // Verify that the cursor is appropriate and updatable - dsql_par* source = dsqlFindDbKey(parent->getStatement(), relation_name); - dsql_par* rv_source = dsqlFindRecordVersion(parent->getStatement(), relation_name); + dsql_par* source = dsqlFindDbKey(parent->getDsqlStatement(), relation_name); + dsql_par* rv_source = dsqlFindRecordVersion(parent->getDsqlStatement(), relation_name); if (!source || !rv_source) { @@ -10014,7 +10014,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_update_err) << cursor); } - const auto statement = static_cast(dsqlScratch->getStatement()); + const auto statement = static_cast(dsqlScratch->getDsqlStatement()); statement->setParentRequest(parent); statement->setParentDbKey(source); @@ -10261,7 +10261,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d for (auto& src : node->first->items) { - auto parameter = MAKE_parameter(dsqlScratch->getStatement()->getReceiveMsg(), + auto parameter = MAKE_parameter(dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, src); parameter->par_node = src; DsqlDescMaker::fromNode(dsqlScratch, ¶meter->par_desc, src, true); @@ -10278,8 +10278,8 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d if (!singleton) { // Set up parameter to handle EOF - auto parameter = MAKE_parameter(dsqlScratch->getStatement()->getReceiveMsg(), false, false, 0, nullptr); - dsqlScratch->getStatement()->setEof(parameter); + auto parameter = MAKE_parameter(dsqlScratch->getDsqlStatement()->getReceiveMsg(), false, false, 0, nullptr); + dsqlScratch->getDsqlStatement()->setEof(parameter); parameter->par_desc.dsc_dtype = dtype_short; parameter->par_desc.dsc_scale = 0; parameter->par_desc.dsc_length = sizeof(SSHORT); @@ -10289,7 +10289,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d if (!dsqlScratch->isPsql()) { - dsqlScratch->getStatement()->setType(singleton ? + dsqlScratch->getDsqlStatement()->setType(singleton ? DsqlStatement::TYPE_EXEC_PROCEDURE : DsqlStatement::TYPE_RETURNING_CURSOR); } diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index e8b3e8250f..9d356297f2 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -1473,9 +1473,9 @@ public: // Save and reset the statement type, as SessionManagementNode sets it to TYPE_SESSION_MANAGEMENT but // we are a DML statement. - DsqlStatement::Type statementType = dsqlScratch->getStatement()->getType(); + DsqlStatement::Type statementType = dsqlScratch->getDsqlStatement()->getType(); wrapped->dsqlPass(dsqlScratch); - dsqlScratch->getStatement()->setType(statementType); + dsqlScratch->getDsqlStatement()->setType(statementType); return this; } diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index f91f271c66..1f77edb1b8 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -117,15 +117,15 @@ dsql_dbb::~dsql_dbb() // Execute a dynamic SQL statement. void DSQL_execute(thread_db* tdbb, jrd_tra** tra_handle, - DsqlRequest* request, + DsqlRequest* dsqlRequest, IMessageMetadata* in_meta, const UCHAR* in_msg, IMessageMetadata* out_meta, UCHAR* out_msg) { SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); - const DsqlStatement* statement = request->getStatement(); + const auto statement = dsqlRequest->getDsqlStatement(); if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { @@ -151,7 +151,7 @@ void DSQL_execute(thread_db* tdbb, if (statement->isCursorBased()) { - if (request->req_cursor) + if (dsqlRequest->req_cursor) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << Arg::Gds(isc_dsql_cursor_open_err)); @@ -161,8 +161,8 @@ void DSQL_execute(thread_db* tdbb, (Arg::Gds(isc_random) << "Cannot execute SELECT statement").raise(); } - request->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); + dsqlRequest->req_transaction = *tra_handle; + dsqlRequest->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); } @@ -178,33 +178,33 @@ void DSQL_execute(thread_db* tdbb, @param option **/ -void DSQL_free_statement(thread_db* tdbb, DsqlRequest* request, USHORT option) +void DSQL_free_statement(thread_db* tdbb, DsqlRequest* dsqlRequest, USHORT option) { SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); - const DsqlStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); fb_assert(!(option & DSQL_unprepare)); // handled in y-valve if (option & DSQL_drop) { // Release everything associated with the request - DsqlRequest::destroy(tdbb, request); + DsqlRequest::destroy(tdbb, dsqlRequest); } else if (option & DSQL_close) { // Just close the cursor associated with the request - if (statement->isCursorBased()) + if (dsqlStatement->isCursorBased()) { - if (!request->req_cursor) + if (!dsqlRequest->req_cursor) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-501) << Arg::Gds(isc_dsql_cursor_close_err)); } - DsqlCursor::close(tdbb, request->req_cursor); + DsqlCursor::close(tdbb, dsqlRequest->req_cursor); } } } @@ -238,18 +238,18 @@ DsqlRequest* DSQL_prepare(thread_db* tdbb, SET_TDBB(tdbb); dsql_dbb* database = init(tdbb, attachment); - DsqlRequest* request = NULL; + DsqlRequest* dsqlRequest = NULL; try { // Allocate a new request block and then prepare the request. - request = prepareRequest(tdbb, database, transaction, length, string, dialect, + dsqlRequest = prepareRequest(tdbb, database, transaction, length, string, dialect, isInternalRequest); // Can not prepare a CREATE DATABASE/SCHEMA statement - const DsqlStatement* statement = request->getStatement(); + const auto statement = dsqlRequest->getDsqlStatement(); if (statement->getType() == DsqlStatement::TYPE_CREATE_DB) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-530) << @@ -258,19 +258,19 @@ DsqlRequest* DSQL_prepare(thread_db* tdbb, if (items && buffer) { - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - sql_info(tdbb, request, items->getCount(), items->begin(), + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); + sql_info(tdbb, dsqlRequest, items->getCount(), items->begin(), buffer->getCount(), buffer->begin()); } - return request; + return dsqlRequest; } catch (const Exception&) { - if (request) + if (dsqlRequest) { - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - DsqlRequest::destroy(tdbb, request); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); + DsqlRequest::destroy(tdbb, dsqlRequest); } throw; } @@ -293,15 +293,15 @@ DsqlRequest* DSQL_prepare(thread_db* tdbb, **/ void DSQL_sql_info(thread_db* tdbb, - DsqlRequest* request, + DsqlRequest* dsqlRequest, ULONG item_length, const UCHAR* items, ULONG info_length, UCHAR* info) { SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); - sql_info(tdbb, request, item_length, items, info_length, info); + sql_info(tdbb, dsqlRequest, item_length, items, info_length, info); } @@ -315,47 +315,47 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr SET_TDBB(tdbb); dsql_dbb* const database = init(tdbb, attachment); - DsqlRequest* request = NULL; + DsqlRequest* dsqlRequest = NULL; try { - request = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, + dsqlRequest = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, isInternalRequest); - const DsqlStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlStatement::TYPE_START_TRANS && - statement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) + dsqlStatement->getType() != DsqlStatement::TYPE_START_TRANS && + dsqlStatement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); } - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); // A select having cursor is a singleton select when executed immediate - const bool singleton = statement->isCursorBased(); + const bool singleton = dsqlStatement->isCursorBased(); if (singleton && !(out_msg && out_meta)) { ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << Arg::Gds(isc_dsql_no_output_sqlda)); } - request->req_transaction = *tra_handle; + dsqlRequest->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); + dsqlRequest->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); - DsqlRequest::destroy(tdbb, request); + DsqlRequest::destroy(tdbb, dsqlRequest); } catch (const Exception&) { - if (request) + if (dsqlRequest) { - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - DsqlRequest::destroy(tdbb, request); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); + DsqlRequest::destroy(tdbb, dsqlRequest); } throw; } @@ -374,16 +374,16 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr @param buffer **/ -static ULONG get_request_info(thread_db* tdbb, DsqlRequest* request, ULONG buffer_length, UCHAR* buffer) +static ULONG get_request_info(thread_db* tdbb, DsqlRequest* dsqlRequest, ULONG buffer_length, UCHAR* buffer) { - if (!request->getJrdRequest()) // DDL + if (!dsqlRequest->getRequest()) // DDL return 0; // get the info for the request from the engine try { - return INF_request_info(request->getJrdRequest(), sizeof(record_info), record_info, + return INF_request_info(dsqlRequest->getRequest(), sizeof(record_info), record_info, buffer_length, buffer); } catch (Exception&) @@ -438,13 +438,13 @@ static DsqlRequest* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* auto statement = prepareStatement(tdbb, database, transaction, textLength, text, clientDialect, isInternalRequest, &traceResult); - auto request = statement->createRequest(tdbb, database); + auto dsqlRequest = statement->createRequest(tdbb, database); - request->req_traced = true; - trace.setStatement(request); + dsqlRequest->req_traced = true; + trace.setStatement(dsqlRequest); trace.prepare(traceResult); - return request; + return dsqlRequest; } catch (const Exception&) { @@ -502,7 +502,7 @@ static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* databas MemoryPool* scratchPool = nullptr; DsqlCompilerScratch* scratch = nullptr; MemoryPool* statementPool = database->createPool(); - RefPtr statement; + RefPtr dsqlStatement; Jrd::ContextPoolHolder statementContext(tdbb, statementPool); try @@ -532,9 +532,9 @@ static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* databas dbDialect, text, textLength, charSetId); // Parse the SQL statement. If it croaks, return - statement = parser.parse(); + dsqlStatement = parser.parse(); - scratch->setStatement(statement); + scratch->setDsqlStatement(dsqlStatement); if (parser.isStmtAmbiguous()) scratch->flags |= DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT; @@ -573,34 +573,34 @@ static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* databas transformedText.assign(temp.begin(), temp.getCount()); } - statement->setSqlText(FB_NEW_POOL(*statementPool) RefString(*statementPool, transformedText)); + dsqlStatement->setSqlText(FB_NEW_POOL(*statementPool) RefString(*statementPool, transformedText)); // allocate the send and receive messages - statement->setSendMsg(FB_NEW_POOL(*statementPool) dsql_msg(*statementPool)); + dsqlStatement->setSendMsg(FB_NEW_POOL(*statementPool) dsql_msg(*statementPool)); dsql_msg* message = FB_NEW_POOL(*statementPool) dsql_msg(*statementPool); - statement->setReceiveMsg(message); + dsqlStatement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlStatement::TYPE_SELECT); - statement->dsqlPass(tdbb, scratch, traceResult); + dsqlStatement->setType(DsqlStatement::TYPE_SELECT); + dsqlStatement->dsqlPass(tdbb, scratch, traceResult); - if (!statement->shouldPreserveScratch()) + if (!dsqlStatement->shouldPreserveScratch()) database->deletePool(scratchPool); scratchPool = nullptr; - if (!isInternalRequest && statement->mustBeReplicated()) - statement->setOrgText(text, textLength); + if (!isInternalRequest && dsqlStatement->mustBeReplicated()) + dsqlStatement->setOrgText(text, textLength); - return statement; + return dsqlStatement; } catch (const Exception&) { if (scratchPool) database->deletePool(scratchPool); - if (!statement) + if (!dsqlStatement) database->deletePool(statementPool); throw; @@ -678,7 +678,7 @@ string IntlString::toUtf8(jrd_tra* transaction) const @brief Return DSQL information buffer. - @param request + @param dsqlRequest @param item_length @param items @param info_length @@ -687,7 +687,7 @@ string IntlString::toUtf8(jrd_tra* transaction) const **/ static void sql_info(thread_db* tdbb, - DsqlRequest* request, + DsqlRequest* dsqlRequest, ULONG item_length, const UCHAR* items, ULONG info_length, @@ -720,7 +720,7 @@ static void sql_info(thread_db* tdbb, bool messageFound = false; USHORT first_index = 0; - const DsqlStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); while (items < end_items && *items != isc_info_end && info < end_info) { @@ -734,7 +734,7 @@ static void sql_info(thread_db* tdbb, case isc_info_sql_select: case isc_info_sql_bind: message = (item == isc_info_sql_select) ? - statement->getReceiveMsg() : statement->getSendMsg(); + dsqlStatement->getReceiveMsg() : dsqlStatement->getSendMsg(); messageFound = true; if (info + 1 >= end_info) { @@ -746,7 +746,7 @@ static void sql_info(thread_db* tdbb, case isc_info_sql_stmt_flags: value = IStatement::FLAG_REPEAT_EXECUTE; - switch (statement->getType()) + switch (dsqlStatement->getType()) { case DsqlStatement::TYPE_CREATE_DB: case DsqlStatement::TYPE_DDL: @@ -766,7 +766,7 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_stmt_type: - switch (statement->getType()) + switch (dsqlStatement->getType()) { case DsqlStatement::TYPE_SELECT: case DsqlStatement::TYPE_RETURNING_CURSOR: @@ -852,7 +852,7 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_batch_fetch: - if (statement->getFlags() & DsqlStatement::FLAG_NO_BATCH) + if (dsqlStatement->getFlags() & DsqlStatement::FLAG_NO_BATCH) number = 0; else number = 1; @@ -862,14 +862,14 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_records: - length = get_request_info(tdbb, request, sizeof(buffer), buffer); + length = get_request_info(tdbb, dsqlRequest, sizeof(buffer), buffer); if (length && !(info = put_item(item, length, buffer, info, end_info))) return; break; case isc_info_sql_stmt_timeout_user: case isc_info_sql_stmt_timeout_run: - value = (item == isc_info_sql_stmt_timeout_user) ? request->getTimeout() : request->getActualTimeout(); + value = (item == isc_info_sql_stmt_timeout_user) ? dsqlRequest->getTimeout() : dsqlRequest->getActualTimeout(); length = put_vax_long(buffer, value); if (!(info = put_item(item, length, buffer, info, end_info))) @@ -889,7 +889,7 @@ static void sql_info(thread_db* tdbb, { const bool detailed = (item == isc_info_sql_explain_plan); string plan = tdbb->getAttachment()->stringToUserCharSet(tdbb, - Optimizer::getPlan(tdbb, request->getJrdStatement(), detailed)); + Optimizer::getPlan(tdbb, dsqlRequest->getStatement(), detailed)); if (plan.hasData()) { @@ -939,9 +939,9 @@ static void sql_info(thread_db* tdbb, { HalfStaticArray path; - if (request->getJrdStatement()) + if (dsqlRequest->getStatement()) { - const auto& blr = request->getJrdStatement()->blr; + const auto& blr = dsqlRequest->getStatement()->blr; if (blr.hasData()) { @@ -1011,7 +1011,7 @@ static void sql_info(thread_db* tdbb, } info = var_info(message, items, end_describe, info, end_info, first_index, - message == statement->getSendMsg()); + message == dsqlStatement->getSendMsg()); if (!info) return; diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index 89aa4c554b..63536cff5d 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -220,14 +220,14 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) message->msg_length = offset; - dsqlScratch->getStatement()->getPorts().add(message); + dsqlScratch->getDsqlStatement()->getPorts().add(message); } // Generate complete blr for a dsqlScratch. void GEN_statement(DsqlCompilerScratch* scratch, DmlNode* node) { - DsqlStatement* statement = scratch->getStatement(); + DsqlStatement* statement = scratch->getDsqlStatement(); if (statement->getBlrVersion() == 4) scratch->appendUChar(blr_version4); diff --git a/src/gpre/int_cxx.cpp b/src/gpre/int_cxx.cpp index 87d8345c03..fabd63c477 100644 --- a/src/gpre/int_cxx.cpp +++ b/src/gpre/int_cxx.cpp @@ -583,7 +583,7 @@ static void gen_send( const gpre_req* request, const gpre_port* port, int column align(column); fprintf(gpreGlob.out_file, "if (ignore_perm)"); align(column); - fprintf(gpreGlob.out_file, "\trequest->getStatement()->flags |= JrdStatement::FLAG_IGNORE_PERM;"); + fprintf(gpreGlob.out_file, "\trequest->getStatement()->flags |= Statement::FLAG_IGNORE_PERM;"); } align(column); diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index 2cfbfb0c62..8b15f37c1a 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -669,7 +669,7 @@ jrd_req* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT w fb_assert(which == IRQ_REQUESTS || which == DYN_REQUESTS); - JrdStatement* statement = (which == IRQ_REQUESTS ? att_internal[id] : att_dyn_req[id]); + Statement* statement = (which == IRQ_REQUESTS ? att_internal[id] : att_dyn_req[id]); if (!statement) return NULL; @@ -846,13 +846,13 @@ void Jrd::Attachment::releaseLocks(thread_db* tdbb) // And release the system requests - for (JrdStatement** itr = att_internal.begin(); itr != att_internal.end(); ++itr) + for (Statement** itr = att_internal.begin(); itr != att_internal.end(); ++itr) { if (*itr) (*itr)->release(tdbb); } - for (JrdStatement** itr = att_dyn_req.begin(); itr != att_dyn_req.end(); ++itr) + for (Statement** itr = att_dyn_req.begin(); itr != att_dyn_req.end(); ++itr) { if (*itr) (*itr)->release(tdbb); diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 1e42f2a263..11294625a3 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -94,7 +94,7 @@ namespace Jrd class Trigger; class TrigVector; class Function; - class JrdStatement; + class Statement; class Validation; class Applier; @@ -545,7 +545,7 @@ private: StableAttachmentPart* att_stable; public: - Firebird::SortedArray att_statements; // Statements belonging to attachment + Firebird::SortedArray att_statements; // Statements belonging to attachment Firebird::SortedArray att_requests; // Requests belonging to attachment Lock* att_id_lock; // Attachment lock (if any) AttNumber att_attachment_id; // Attachment ID @@ -620,8 +620,8 @@ public: Firebird::Array att_functions; // User defined functions GeneratorFinder att_generators; - Firebird::Array att_internal; // internal statements - Firebird::Array att_dyn_req; // internal dyn statements + Firebird::Array att_internal; // internal statements + Firebird::Array att_dyn_req; // internal dyn statements Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt Firebird::DecimalStatus att_dec_status; // error handling and rounding diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 4bfd0c0308..5a9fa1bef5 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -36,7 +36,7 @@ class jrd_tra; class DsqlCursor; class DsqlBatch; class DsqlRequest; -class JrdStatement; +class Statement; class StableAttachmentPart; class Attachment; class Service; @@ -346,20 +346,20 @@ public: void deprecatedFree(Firebird::CheckStatusWrapper* status) override; public: - JRequest(JrdStatement* handle, StableAttachmentPart* sa); + JRequest(Statement* handle, StableAttachmentPart* sa); StableAttachmentPart* getAttachment() { return sAtt; } - JrdStatement* getHandle() throw() + Statement* getHandle() throw() { return rq; } private: - JrdStatement* rq; + Statement* rq; Firebird::RefPtr sAtt; void freeEngineData(Firebird::CheckStatusWrapper* status); diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 9016d5f90b..b8158020d3 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -1387,7 +1387,7 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: extFunctionNode->statement = FB_NEW_POOL(csbPool) MessageMoverNode( csbPool, extOutMessageNode, intOutMessageNode); - JrdStatement* statement = udf->getStatement(); + Statement* statement = udf->getStatement(); PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); udf->setStatement(statement); } @@ -1519,7 +1519,7 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ extInMessageNode, extOutMessageNode, intOutMessageNode, prc->getExternal()); mainNode->statements.add(extProcedureNode); - JrdStatement* statement = prc->getStatement(); + Statement* statement = prc->getStatement(); PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); prc->setStatement(statement); } diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index bfec859023..c9e0d61fe3 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -1140,7 +1140,7 @@ void Monitoring::putTransaction(SnapshotData::DumpRecord& record, const jrd_tra* } -void Monitoring::putStatement(SnapshotData::DumpRecord& record, const JrdStatement* statement, const string& plan) +void Monitoring::putStatement(SnapshotData::DumpRecord& record, const Statement* statement, const string& plan) { fb_assert(statement); @@ -1209,7 +1209,7 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req else record.storeInteger(f_mon_stmt_state, mon_state_idle); - const JrdStatement* const statement = request->getStatement(); + const Statement* const statement = request->getStatement(); // sql text if (statement->sqlText) @@ -1259,7 +1259,7 @@ void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* reques if (initialRequest != request->req_caller) record.storeInteger(f_mon_call_caller_id, request->req_caller->getRequestId()); - const JrdStatement* statement = request->getStatement(); + const Statement* statement = request->getStatement(); const Routine* routine = statement->getRoutine(); // object name/type @@ -1477,7 +1477,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) request->adjustCallerStats(); if (!(request->getStatement()->flags & - (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER)) && + (Statement::FLAG_INTERNAL | Statement::FLAG_SYS_TRIGGER)) && request->req_caller) { putCall(record, request); @@ -1491,7 +1491,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) for (const auto statement : attachment->att_statements) { - if (!(statement->flags & (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) + if (!(statement->flags & (Statement::FLAG_INTERNAL | Statement::FLAG_SYS_TRIGGER))) { const string plan = Optimizer::getPlan(tdbb, statement, true); putStatement(record, statement, plan); @@ -1505,7 +1505,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) { const auto statement = request->getStatement(); - if (!(statement->flags & (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) + if (!(statement->flags & (Statement::FLAG_INTERNAL | Statement::FLAG_SYS_TRIGGER))) { const string plan = Optimizer::getPlan(tdbb, statement, true); putRequest(record, request, plan); diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index 5fb0fa2dea..f77b7ef983 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -389,7 +389,7 @@ private: static void putAttachment(SnapshotData::DumpRecord&, const Attachment*); static void putTransaction(SnapshotData::DumpRecord&, const jrd_tra*); - static void putStatement(SnapshotData::DumpRecord&, const JrdStatement*, const Firebird::string&); + static void putStatement(SnapshotData::DumpRecord&, const Statement*, const Firebird::string&); static void putRequest(SnapshotData::DumpRecord&, const jrd_req*, const Firebird::string&); static void putCall(SnapshotData::DumpRecord&, const jrd_req*); static void putStatistics(SnapshotData::DumpRecord&, const RuntimeStatistics&, int, int); diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index 9f39d76ff0..62ebcf1ab4 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -302,7 +302,7 @@ PreparedStatement::~PreparedStatement() { thread_db* tdbb = JRD_get_thread_data(); - DSQL_free_statement(tdbb, request, DSQL_drop); + DSQL_free_statement(tdbb, dsqlRequest, DSQL_drop); if (resultSet) resultSet->stmt = NULL; @@ -315,30 +315,28 @@ void PreparedStatement::init(thread_db* tdbb, Attachment* attachment, jrd_tra* t AutoSetRestore autoAttCharset(&attachment->att_charset, (isInternalRequest ? CS_METADATA : attachment->att_charset)); - request = NULL; + dsqlRequest = NULL; try { const Database& dbb = *tdbb->getDatabase(); const int dialect = isInternalRequest || (dbb.dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; - request = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, 0, + dsqlRequest = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, 0, NULL, NULL, isInternalRequest); - const DsqlStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); - if (statement->getSendMsg()) - parseDsqlMessage(statement->getSendMsg(), inValues, inMetadata, inMessage); + if (dsqlStatement->getSendMsg()) + parseDsqlMessage(dsqlStatement->getSendMsg(), inValues, inMetadata, inMessage); - if (statement->getReceiveMsg()) - parseDsqlMessage(statement->getReceiveMsg(), outValues, outMetadata, outMessage); + if (dsqlStatement->getReceiveMsg()) + parseDsqlMessage(dsqlStatement->getReceiveMsg(), outValues, outMetadata, outMessage); } catch (const Exception&) { - if (request) - { - DSQL_free_statement(tdbb, request, DSQL_drop); - } + if (dsqlRequest) + DSQL_free_statement(tdbb, dsqlRequest, DSQL_drop); throw; } } @@ -348,12 +346,12 @@ void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& valu { fb_assert(param > 0); - jrd_req* jrdRequest = getRequest()->getJrdRequest(); + jrd_req* request = getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( - tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); - AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); MOV_move(tdbb, const_cast(&value), &inValues[(param - 1) * 2]); @@ -371,7 +369,7 @@ void PreparedStatement::execute(thread_db* tdbb, jrd_tra* transaction) if (builder) builder->moveToStatement(tdbb, this); - DSQL_execute(tdbb, &transaction, request, inMetadata, inMessage.begin(), NULL, NULL); + DSQL_execute(tdbb, &transaction, dsqlRequest, inMetadata, inMessage.begin(), NULL, NULL); } @@ -382,13 +380,13 @@ void PreparedStatement::open(thread_db* tdbb, jrd_tra* transaction) if (builder) builder->moveToStatement(tdbb, this); - request->openCursor(tdbb, &transaction, inMetadata, inMessage.begin(), outMetadata, 0); + dsqlRequest->openCursor(tdbb, &transaction, inMetadata, inMessage.begin(), outMetadata, 0); } ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction) { - fb_assert(resultSet == NULL && request->getStatement()->getReceiveMsg()); + fb_assert(resultSet == NULL && dsqlRequest->getDsqlStatement()->getReceiveMsg()); if (builder) builder->moveToStatement(tdbb, this); @@ -400,7 +398,7 @@ ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction unsigned PreparedStatement::executeUpdate(thread_db* tdbb, jrd_tra* transaction) { execute(tdbb, transaction); - return getRequest()->getJrdRequest()->req_records_updated; + return getDsqlRequest()->getRequest()->req_records_updated; } diff --git a/src/jrd/PreparedStatement.h b/src/jrd/PreparedStatement.h index 4206b41370..703c1d42b6 100644 --- a/src/jrd/PreparedStatement.h +++ b/src/jrd/PreparedStatement.h @@ -359,9 +359,9 @@ public: int getResultCount() const; - DsqlRequest* getRequest() + DsqlRequest* getDsqlRequest() { - return request; + return dsqlRequest; } static void parseDsqlMessage(const dsql_msg* dsqlMsg, Firebird::Array& values, @@ -369,7 +369,7 @@ public: private: const Builder* builder; - DsqlRequest* request; + DsqlRequest* dsqlRequest; Firebird::Array inValues, outValues; Firebird::RefPtr inMetadata, outMetadata; Firebird::UCharBuffer inMessage, outMessage; diff --git a/src/jrd/ResultSet.cpp b/src/jrd/ResultSet.cpp index bd112fa6bb..01bf321647 100644 --- a/src/jrd/ResultSet.cpp +++ b/src/jrd/ResultSet.cpp @@ -54,14 +54,14 @@ ResultSet::~ResultSet() stmt->resultSet = NULL; - if (stmt->request->getStatement()->getType() != DsqlStatement::TYPE_EXEC_PROCEDURE) - DSQL_free_statement(tdbb, stmt->request, DSQL_close); + if (stmt->dsqlRequest->getDsqlStatement()->getType() != DsqlStatement::TYPE_EXEC_PROCEDURE) + DSQL_free_statement(tdbb, stmt->dsqlRequest, DSQL_close); } bool ResultSet::fetch(thread_db* tdbb) { - if (stmt->request->getStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE && + if (stmt->dsqlRequest->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE && firstFetchDone) { return false; @@ -69,7 +69,7 @@ bool ResultSet::fetch(thread_db* tdbb) memset(stmt->outMessage.begin(), 0, stmt->outMessage.getCount()); - if (!stmt->request->fetch(tdbb, stmt->outMessage.begin())) + if (!stmt->dsqlRequest->fetch(tdbb, stmt->outMessage.begin())) return false; if (stmt->builder) @@ -103,12 +103,12 @@ Firebird::string ResultSet::getString(thread_db* tdbb, unsigned param) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->getJrdRequest(); + jrd_req* request = stmt->getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( - tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); - AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); return MOV_make_string2(tdbb, &getDesc(param), CS_NONE); @@ -131,12 +131,12 @@ void ResultSet::moveDesc(thread_db* tdbb, unsigned param, dsc& desc) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->getJrdRequest(); + jrd_req* request = stmt->getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( - tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); - AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); MOV_move(tdbb, &getDesc(param), &desc); diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index 1d78121e20..c0fb46955d 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -111,7 +111,7 @@ Format* Routine::createFormat(MemoryPool& pool, IMessageMetadata* params, bool a return format; } -void Routine::setStatement(JrdStatement* value) +void Routine::setStatement(Statement* value) { statement = value; @@ -173,7 +173,7 @@ void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* flags &= ~Routine::FLAG_RELOAD; - JrdStatement* statement = getStatement(); + Statement* statement = getStatement(); PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &statement, false, 0); setStatement(statement); diff --git a/src/jrd/Routine.h b/src/jrd/Routine.h index 1de4ef5463..9002453ef9 100644 --- a/src/jrd/Routine.h +++ b/src/jrd/Routine.h @@ -34,7 +34,7 @@ namespace Jrd { class thread_db; class CompilerScratch; - class JrdStatement; + class Statement; class Lock; class Format; class Parameter; @@ -105,8 +105,8 @@ namespace Jrd const MetaName& getSecurityName() const { return securityName; } void setSecurityName(const MetaName& value) { securityName = value; } - /*const*/ JrdStatement* getStatement() const { return statement; } - void setStatement(JrdStatement* value); + /*const*/ Statement* getStatement() const { return statement; } + void setStatement(Statement* value); bool isSubRoutine() const { return subRoutine; } void setSubRoutine(bool value) { subRoutine = value; } @@ -166,7 +166,7 @@ namespace Jrd USHORT id; // routine ID QualifiedName name; // routine name MetaName securityName; // security class name - JrdStatement* statement; // compiled routine statement + Statement* statement; // compiled routine statement bool subRoutine; // Is this a subroutine? bool implemented; // Is the packaged routine missing the body/entrypoint? bool defined; // UDF has its implementation module available diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index e2ed504aab..7026093c9d 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -41,7 +41,7 @@ using namespace Firebird; using namespace Jrd; -template static void makeSubRoutines(thread_db* tdbb, JrdStatement* statement, +template static void makeSubRoutines(thread_db* tdbb, Statement* statement, CompilerScratch* csb, T& subs); @@ -49,7 +49,7 @@ ULONG CompilerScratch::allocImpure(ULONG align, ULONG size) { const ULONG offset = FB_ALIGN(csb_impure, align); - if (offset + size > JrdStatement::MAX_REQUEST_SIZE) + if (offset + size > Statement::MAX_REQUEST_SIZE) IBERROR(226); // msg 226: request size limit exceeded csb_impure = offset + size; @@ -59,7 +59,7 @@ ULONG CompilerScratch::allocImpure(ULONG align, ULONG size) // Start to turn a parsed scratch into a statement. This is completed by makeStatement. -JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) +Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) : pool(p), rpbsSetup(*p), requests(*p), @@ -133,7 +133,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) #ifdef DEBUG_PROCS string buffer; buffer.printf( - "Called from JrdStatement::makeRequest:\n\t Incrementing use count of %s\n", + "Called from Statement::makeRequest:\n\t Incrementing use count of %s\n", routine->getName()->toString().c_str()); JRD_print_procedure_info(tdbb, buffer.c_str()); #endif @@ -189,7 +189,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) } catch (Exception&) { - for (JrdStatement** subStatement = subStatements.begin(); + for (Statement** subStatement = subStatements.begin(); subStatement != subStatements.end(); ++subStatement) { @@ -201,7 +201,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) } // Turn a parsed scratch into a statement. -JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) { DEV_BLKCHK(csb, type_csb); SET_TDBB(tdbb); @@ -212,7 +212,7 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, jrd_req* const old_request = tdbb->getRequest(); tdbb->setRequest(NULL); - JrdStatement* statement = NULL; + Statement* statement = NULL; try { @@ -279,7 +279,7 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, MemoryPool* const pool = tdbb->getDefaultPool(); - statement = FB_NEW_POOL(*pool) JrdStatement(tdbb, pool, csb); + statement = FB_NEW_POOL(*pool) Statement(tdbb, pool, csb); tdbb->setRequest(old_request); } // try @@ -288,7 +288,7 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, if (statement) { // Release sub statements. - for (JrdStatement** subStatement = statement->subStatements.begin(); + for (Statement** subStatement = statement->subStatements.begin(); subStatement != statement->subStatements.end(); ++subStatement) { @@ -310,14 +310,14 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, } // Turn a parsed scratch into an executable request. -jrd_req* JrdStatement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +jrd_req* Statement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) { - JrdStatement* statement = makeStatement(tdbb, csb, internalFlag); + Statement* statement = makeStatement(tdbb, csb, internalFlag); return statement->getRequest(tdbb, 0); } // Returns function or procedure routine. -const Routine* JrdStatement::getRoutine() const +const Routine* Statement::getRoutine() const { fb_assert(!(procedure && function)); @@ -328,7 +328,7 @@ const Routine* JrdStatement::getRoutine() const } // Determine if any request of this statement are active. -bool JrdStatement::isActive() const +bool Statement::isActive() const { for (const jrd_req* const* request = requests.begin(); request != requests.end(); ++request) { @@ -339,12 +339,12 @@ bool JrdStatement::isActive() const return false; } -jrd_req* JrdStatement::findRequest(thread_db* tdbb, bool unique) +jrd_req* Statement::findRequest(thread_db* tdbb, bool unique) { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); - const JrdStatement* const thisPointer = this; // avoid warning + const Statement* const thisPointer = this; // avoid warning if (!thisPointer) BUGCHECK(167); /* msg 167 invalid SEND request */ @@ -391,7 +391,7 @@ jrd_req* JrdStatement::findRequest(thread_db* tdbb, bool unique) return clone; } -jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) +jrd_req* Statement::getRequest(thread_db* tdbb, USHORT level) { SET_TDBB(tdbb); @@ -420,7 +420,7 @@ jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) // Check that we have enough rights to access all resources this request touches including // resources it used indirectly via procedures or triggers. -void JrdStatement::verifyAccess(thread_db* tdbb) +void Statement::verifyAccess(thread_db* tdbb) { SET_TDBB(tdbb); @@ -590,12 +590,12 @@ void JrdStatement::verifyAccess(thread_db* tdbb) } // Release a statement. -void JrdStatement::release(thread_db* tdbb) +void Statement::release(thread_db* tdbb) { SET_TDBB(tdbb); // Release sub statements. - for (JrdStatement** subStatement = subStatements.begin(); + for (Statement** subStatement = subStatements.begin(); subStatement != subStatements.end(); ++subStatement) { @@ -665,7 +665,7 @@ void JrdStatement::release(thread_db* tdbb) } // Returns a formatted textual plan for all RseNode's in the specified request -string JrdStatement::getPlan(thread_db* tdbb, bool detailed) const +string Statement::getPlan(thread_db* tdbb, bool detailed) const { string plan; @@ -679,7 +679,7 @@ string JrdStatement::getPlan(thread_db* tdbb, bool detailed) const } // Check that we have enough rights to access all resources this list of triggers touches. -void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, +void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers, MetaName userName) { if (!triggers) @@ -743,7 +743,7 @@ void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, } // Invoke buildExternalAccess for triggers in vector -inline void JrdStatement::triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, +inline void Statement::triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, TrigVector* tvec, const MetaName& user) { if (!tvec) @@ -764,7 +764,7 @@ inline void JrdStatement::triggersExternalAccess(thread_db* tdbb, ExternalAccess // Recursively walk external dependencies (procedures, triggers) for request to assemble full // list of requests it depends on. -void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const MetaName &user) +void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const MetaName &user) { for (ExternalAccess* item = externalList.begin(); item != externalList.end(); ++item) { @@ -834,7 +834,7 @@ void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list // Make sub routines. -template static void makeSubRoutines(thread_db* tdbb, JrdStatement* statement, +template static void makeSubRoutines(thread_db* tdbb, Statement* statement, CompilerScratch* csb, T& subs) { typename T::Accessor subAccessor(&subs); @@ -845,7 +845,7 @@ template static void makeSubRoutines(thread_db* tdbb, JrdStatement* Routine* subRoutine = subNode->routine; CompilerScratch*& subCsb = subNode->subCsb; - JrdStatement* subStatement = JrdStatement::makeStatement(tdbb, subCsb, false); + Statement* subStatement = Statement::makeStatement(tdbb, subCsb, false); subStatement->parentStatement = statement; subRoutine->setStatement(subStatement); diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index 643c784a70..318e6d1d2b 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -28,7 +28,7 @@ namespace Jrd { // Compiled statement. -class JrdStatement : public pool_alloc +class Statement : public pool_alloc { public: static const unsigned FLAG_SYS_TRIGGER = 0x01; @@ -42,10 +42,10 @@ public: static const unsigned MAX_REQUEST_SIZE = 50 * 1048576; // 50 MB - just to be safe private: - JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); + Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); public: - static JrdStatement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); static jrd_req* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); StmtNumber getStatementId() const @@ -87,8 +87,8 @@ public: const Function* function; // function, if any MetaName triggerName; // name of request (trigger), if any Jrd::UserId* triggerInvoker; // user name if trigger run with SQL SECURITY DEFINER - JrdStatement* parentStatement; // Sub routine's parent statement - Firebird::Array subStatements; // Array of subroutines' statements + Statement* parentStatement; // Sub routine's parent statement + Firebird::Array subStatements; // Array of subroutines' statements const StmtNode* topNode; // top of execution tree Firebird::Array fors; // record sources Firebird::Array localTables; // local tables diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 96fa19494d..36cb89ab15 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -43,7 +43,7 @@ namespace Jrd { class jrd_rel; class jrd_tra; template class vec; -class JrdStatement; +class Statement; struct temporary_key; class jrd_tra; class BtrPageGCLock; @@ -66,7 +66,7 @@ struct index_desc vec* idx_foreign_indexes; // ids for foreign key partner indexes ValueExprNode* idx_expression; // node tree for indexed expresssion dsc idx_expression_desc; // descriptor for expression result - JrdStatement* idx_expression_statement; // stored statement for expression evaluation + Statement* idx_expression_statement; // stored statement for expression evaluation // This structure should exactly match IRTD structure for current ODS struct idx_repeat { @@ -246,7 +246,7 @@ public: { } - static bool checkPool(const Lock* lock, Firebird::MemoryPool* pool) + static bool checkPool(const Lock* lock, Firebird::MemoryPool* pool) { if (!pool || !lock) return false; diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index dab8c7ffe3..c957d5b4c8 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -142,10 +142,10 @@ BoolExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, BoolExpr } // Compile a statement. -JrdStatement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, +Statement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, ULONG dbginfoLength, const UCHAR* dbginfo) { - JrdStatement* statement = nullptr; + Statement* statement = nullptr; SET_TDBB(tdbb); const auto att = tdbb->getAttachment(); @@ -160,7 +160,7 @@ JrdStatement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bo const auto csb = PAR_parse(tdbb, blr, blrLength, internalFlag, dbginfoLength, dbginfo); - statement = JrdStatement::makeStatement(tdbb, csb, internalFlag); + statement = Statement::makeStatement(tdbb, csb, internalFlag); #ifdef CMP_DEBUG if (csb->csb_dump.hasData()) diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index a5f3a9b630..eb85916e88 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -37,7 +37,7 @@ StreamType* CMP_alloc_map(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType str Jrd::ValueExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); Jrd::BoolExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::BoolExprNode*); Jrd::ValueExprNode* CMP_clone_node(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); -Jrd::JrdStatement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, +Jrd::Statement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, ULONG dbginfoLength, const UCHAR* dbginfo); Jrd::jrd_req* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 6e232a698d..7b8e783533 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -983,7 +983,7 @@ namespace if (routine && !blobId.isEmpty()) { - JrdStatement* statement = NULL; + Statement* statement = NULL; // Nickolay Samofatov: allocate statement memory pool... MemoryPool* new_pool = attachment->createPool(); // block is used to ensure MET_verify_cache @@ -5433,7 +5433,7 @@ static void get_trigger_dependencies(DeferredWork* work, bool compile, jrd_tra* if ((relation || (type & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DML) && !blob_id.isEmpty()) { - JrdStatement* statement = NULL; + Statement* statement = NULL; // Nickolay Samofatov: allocate statement memory pool... MemoryPool* new_pool = attachment->createPool(); USHORT par_flags; @@ -5905,7 +5905,7 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ if (notNull && !defaultValue->isEmpty()) { Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - JrdStatement* defaultStatement = NULL; + Statement* defaultStatement = NULL; try { ValueExprNode* defaultNode = static_cast(MET_parse_blob( diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 1ab416e332..2c2313f2d9 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -930,7 +930,7 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) if (request->req_flags & req_active) { - const JrdStatement* statement = request->getStatement(); + const Statement* statement = request->getStatement(); if (statement->fors.getCount() || request->req_ext_resultset || request->req_ext_stmt) { @@ -1199,7 +1199,7 @@ void EXE_execute_triggers(thread_db* tdbb, TraceTrigExecute trace(tdbb, trigger, which_trig); { // Scope to replace att_ss_user - const JrdStatement* s = trigger->getStatement(); + const Statement* s = trigger->getStatement(); UserId* invoker = s->triggerInvoker ? s->triggerInvoker : tdbb->getAttachment()->att_ss_user; AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); @@ -1252,7 +1252,7 @@ static void stuff_stack_trace(const jrd_req* request) for (const jrd_req* req = request; req; req = req->req_caller) { - const JrdStatement* const statement = req->getStatement(); + const Statement* const statement = req->getStatement(); string context, name; @@ -1572,7 +1572,7 @@ static void trigger_failure(thread_db* tdbb, jrd_req* trigger) MET_trigger_msg(tdbb, msg, trigger->getStatement()->triggerName, trigger->req_label); if (msg.hasData()) { - if (trigger->getStatement()->flags & JrdStatement::FLAG_SYS_TRIGGER) + if (trigger->getStatement()->flags & Statement::FLAG_SYS_TRIGGER) { ISC_STATUS code = PAR_symbol_to_gdscode(msg); if (code) diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 590e2fa931..1ecd53d20c 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -460,7 +460,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (m_callerPrivileges) { jrd_req* request = tdbb->getRequest(); - JrdStatement* statement = request ? request->getStatement() : NULL; + auto statement = request ? request->getStatement() : NULL; CallerName callerName; const Routine* routine; @@ -504,13 +504,13 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (status->getState() & IStatus::STATE_ERRORS) raise(&status, tdbb, "JAttachment::prepare", &sql); - const DsqlStatement* statement = m_request->getHandle()->getStatement(); + const auto dsqlStatement = m_request->getHandle()->getDsqlStatement(); - if (statement->getSendMsg()) + if (dsqlStatement->getSendMsg()) { try { - PreparedStatement::parseDsqlMessage(statement->getSendMsg(), m_inDescs, + PreparedStatement::parseDsqlMessage(dsqlStatement->getSendMsg(), m_inDescs, m_inMetadata, m_in_buffer); m_inputs = m_inMetadata->getCount(); } @@ -522,11 +522,11 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) else m_inputs = 0; - if (statement->getReceiveMsg()) + if (dsqlStatement->getReceiveMsg()) { try { - PreparedStatement::parseDsqlMessage(statement->getReceiveMsg(), m_outDescs, + PreparedStatement::parseDsqlMessage(dsqlStatement->getReceiveMsg(), m_outDescs, m_outMetadata, m_out_buffer); m_outputs = m_outMetadata->getCount(); } @@ -540,7 +540,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) m_stmt_selectable = false; - switch (statement->getType()) + switch (dsqlStatement->getType()) { case DsqlStatement::TYPE_SELECT: case DsqlStatement::TYPE_RETURNING_CURSOR: diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 3cd5cd2f62..b1bfa3d2b9 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -333,7 +333,7 @@ JResultSet::JResultSet(DsqlCursor* handle, JStatement* aStatement) { } -JRequest::JRequest(JrdStatement* handle, StableAttachmentPart* sa) +JRequest::JRequest(Statement* handle, StableAttachmentPart* sa) : rq(handle), sAtt(sa) { } @@ -647,7 +647,7 @@ namespace tdbb->setTransaction(transaction); } - inline void validateHandle(thread_db* tdbb, JrdStatement* const statement) + inline void validateHandle(thread_db* tdbb, Statement* const statement) { if (!statement) status_exception::raise(Arg::Gds(isc_bad_req_handle)); @@ -942,10 +942,10 @@ void Trigger::compile(thread_db* tdbb) statement->triggerInvoker = att->getUserId(owner); if (sysTrigger) - statement->flags |= JrdStatement::FLAG_SYS_TRIGGER; + statement->flags |= Statement::FLAG_SYS_TRIGGER; if (flags & TRG_ignore_perm) - statement->flags |= JrdStatement::FLAG_IGNORE_PERM; + statement->flags |= Statement::FLAG_IGNORE_PERM; } } @@ -1311,7 +1311,7 @@ static bool drop_files(const jrd_file*); static void find_intl_charset(thread_db*, Jrd::Attachment*, const DatabaseOptions*); static void init_database_lock(thread_db*); static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction); -static jrd_req* verify_request_synchronization(JrdStatement* statement, USHORT level); +static jrd_req* verify_request_synchronization(Statement* statement, USHORT level); static void purge_transactions(thread_db*, Jrd::Attachment*, const bool); static void check_single_maintenance(thread_db* tdbb); @@ -2681,7 +2681,7 @@ JRequest* JAttachment::compileRequest(CheckStatusWrapper* user_status, * Functional description * **************************************/ - JrdStatement* stmt = NULL; + Statement* stmt = NULL; try { @@ -4713,7 +4713,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* CompilerScratch* csb = PAR_parse(tdbb, reinterpret_cast(blr), blr_length, false); - request = JrdStatement::makeRequest(tdbb, csb, false); + request = Statement::makeRequest(tdbb, csb, false); request->getStatement()->verifyAccess(tdbb); for (FB_SIZE_T i = 0; i < csb->csb_rpt.getCount(); i++) @@ -8335,7 +8335,7 @@ static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction) // // @param request The incoming, parent request to be replaced. // @param level The level of the sub-request we need to find. -static jrd_req* verify_request_synchronization(JrdStatement* statement, USHORT level) +static jrd_req* verify_request_synchronization(Statement* statement, USHORT level) { if (level) { @@ -8945,7 +8945,7 @@ ISC_STATUS thread_db::getCancelState(ISC_STATUS* secondary) if ((!request || !(request->getStatement()->flags & // temporary change to fix shutdown - (/*JrdStatement::FLAG_INTERNAL | */JrdStatement::FLAG_SYS_TRIGGER))) && + (/*Statement::FLAG_INTERNAL | */Statement::FLAG_SYS_TRIGGER))) && (!transaction || !(transaction->tra_flags & TRA_system))) { return isc_cancelled; diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 593fac66ac..78ddc4ca54 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -107,7 +107,7 @@ class thread_db; class Attachment; class jrd_tra; class jrd_req; -class JrdStatement; +class Statement; class jrd_file; class Format; class BufferDesc; @@ -136,7 +136,7 @@ class Trigger public: Firebird::HalfStaticArray blr; // BLR code Firebird::HalfStaticArray debugInfo; // Debug info - JrdStatement* statement; // Compiled statement + Statement* statement; // Compiled statement bool releaseInProgress; bool sysTrigger; FB_UINT64 type; // Trigger type @@ -305,7 +305,7 @@ class IndexBlock : public pool_alloc public: IndexBlock* idb_next; ValueExprNode* idb_expression; // node tree for index expression - JrdStatement* idb_expression_statement; // statement for index expression evaluation + Statement* idb_expression_statement; // statement for index expression evaluation dsc idb_expression_desc; // descriptor for expression result Lock* idb_lock; // lock to synchronize changes to index USHORT idb_id; diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index 7f39152a17..3784ba6224 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -36,7 +36,7 @@ namespace Jrd { class blb; struct bid; class jrd_req; - class JrdStatement; + class Statement; class Service; class thread_db; struct teb; diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 662a2aa3d8..ad3117557f 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -122,7 +122,7 @@ static void make_relation_scope_name(const TEXT*, const USHORT, string& str); static ValueExprNode* parse_field_default_blr(thread_db* tdbb, bid* blob_id); static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, const MetaName name); static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*); -static void save_trigger_data(thread_db*, TrigVector**, jrd_rel*, JrdStatement*, blb*, blb*, +static void save_trigger_data(thread_db*, TrigVector**, jrd_rel*, Statement*, blb*, blb*, const TEXT*, FB_UINT64, bool, USHORT, const MetaName&, const string&, const bid*, Nullable ssDefiner); static void scan_partners(thread_db*, jrd_rel*); @@ -131,10 +131,10 @@ static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*, static bool verify_TRG_ignore_perm(thread_db*, const MetaName&); -static void inc_int_use_count(JrdStatement* statement) +static void inc_int_use_count(Statement* statement) { // Handle sub-statements - for (JrdStatement** subStatement = statement->subStatements.begin(); + for (Statement** subStatement = statement->subStatements.begin(); subStatement != statement->subStatements.end(); ++subStatement) { @@ -175,7 +175,7 @@ static void post_used_procedures(TrigVector* vector) for (FB_SIZE_T i = 0; i < vector->getCount(); i++) { - JrdStatement* stmt = (*vector)[i].statement; + Statement* stmt = (*vector)[i].statement; if (stmt && !stmt->isActive()) inc_int_use_count(stmt); } @@ -1648,7 +1648,7 @@ DmlNode* MET_get_dependencies(thread_db* tdbb, const ULONG blob_length, CompilerScratch* view_csb, bid* blob_id, - JrdStatement** statementPtr, + Statement** statementPtr, CompilerScratch** csb_ptr, const MetaName& object_name, int type, @@ -3073,7 +3073,7 @@ DmlNode* MET_parse_blob(thread_db* tdbb, jrd_rel* relation, bid* blob_id, CompilerScratch** csb_ptr, - JrdStatement** statementPtr, + Statement** statementPtr, const bool trigger, bool validationExpr) { @@ -3198,7 +3198,7 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) else par_flags |= csb_post_trigger; - JrdStatement* statement = NULL; + Statement* statement = NULL; { Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); @@ -3207,9 +3207,9 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) statement->triggerName = name; - statement->flags |= JrdStatement::FLAG_SYS_TRIGGER; + statement->flags |= Statement::FLAG_SYS_TRIGGER; if (trig_flags & TRG_ignore_perm) - statement->flags |= JrdStatement::FLAG_IGNORE_PERM; + statement->flags |= Statement::FLAG_IGNORE_PERM; save_trigger_data(tdbb, ptr, relation, statement, NULL, NULL, NULL, type, true, 0, "", "", NULL, Nullable()); @@ -4771,7 +4771,7 @@ void MET_release_trigger(thread_db* tdbb, TrigVector** vector_ptr, const MetaNam { if (vector[i].name == name) { - JrdStatement* stmt = vector[i].statement; + Statement* stmt = vector[i].statement; if (stmt) { if (stmt->isActive()) @@ -4933,7 +4933,7 @@ static bool resolve_charset_and_collation(thread_db* tdbb, static void save_trigger_data(thread_db* tdbb, TrigVector** ptr, jrd_rel* relation, - JrdStatement* statement, blb* blrBlob, blb* debugInfoBlob, + Statement* statement, blb* blrBlob, blb* debugInfoBlob, const TEXT* name, FB_UINT64 type, bool sys_trigger, USHORT flags, const MetaName& engine, const string& entryPoint, diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index 1c87a5274a..018338db70 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -32,7 +32,7 @@ namespace Jrd { class jrd_tra; class jrd_req; - class JrdStatement; + class Statement; class jrd_prc; class Format; class jrd_rel; @@ -86,7 +86,7 @@ Jrd::Format* MET_format(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); bool MET_get_char_coll_subtype(Jrd::thread_db*, USHORT*, const UCHAR*, USHORT); bool MET_get_char_coll_subtype_info(Jrd::thread_db*, USHORT, SubtypeInfo* info); Jrd::DmlNode* MET_get_dependencies(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, const ULONG, - Jrd::CompilerScratch*, Jrd::bid*, Jrd::JrdStatement**, + Jrd::CompilerScratch*, Jrd::bid*, Jrd::Statement**, Jrd::CompilerScratch**, const Jrd::MetaName&, int, USHORT, Jrd::jrd_tra*, const Jrd::MetaName& = Jrd::MetaName()); Jrd::jrd_fld* MET_get_field(const Jrd::jrd_rel*, USHORT); @@ -115,7 +115,7 @@ Jrd::jrd_prc* MET_lookup_procedure_id(Jrd::thread_db*, USHORT, bool, bool, USHOR Jrd::jrd_rel* MET_lookup_relation(Jrd::thread_db*, const Jrd::MetaName&); Jrd::jrd_rel* MET_lookup_relation_id(Jrd::thread_db*, SLONG, bool); Jrd::DmlNode* MET_parse_blob(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::bid*, Jrd::CompilerScratch**, - Jrd::JrdStatement**, bool, bool); + Jrd::Statement**, bool, bool); void MET_parse_sys_trigger(Jrd::thread_db*, Jrd::jrd_rel*); void MET_post_existence(Jrd::thread_db*, Jrd::jrd_rel*); void MET_prepare(Jrd::thread_db*, Jrd::jrd_tra*, USHORT, const UCHAR*); diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index dafe816e10..61855e52b0 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -330,7 +330,7 @@ public: return ConjunctIterator(begin, end); } - static Firebird::string getPlan(thread_db* tdbb, const JrdStatement* statement, bool detailed) + static Firebird::string getPlan(thread_db* tdbb, const Statement* statement, bool detailed) { return statement ? statement->getPlan(tdbb, detailed) : ""; } diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 3c958d9627..890496eec0 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -167,7 +167,7 @@ namespace // Parse blr, returning a compiler scratch block with the results. // Caller must do pool handling. DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length, - CompilerScratch* view_csb, CompilerScratch** csb_ptr, JrdStatement** statementPtr, + CompilerScratch* view_csb, CompilerScratch** csb_ptr, Statement** statementPtr, const bool trigger, USHORT flags) { #ifdef CMP_DEBUG @@ -188,7 +188,7 @@ DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr PAR_syntax_error(csb, "end_of_command"); if (statementPtr) - *statementPtr = JrdStatement::makeStatement(tdbb, csb, false); + *statementPtr = Statement::makeStatement(tdbb, csb, false); return csb->csb_node; } @@ -197,7 +197,7 @@ DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr // Finish parse of memory nodes, returning a compiler scratch block with the results. // Caller must do pool handling. void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node, - CompilerScratch* view_csb, CompilerScratch** csb_ptr, JrdStatement** statementPtr, + CompilerScratch* view_csb, CompilerScratch** csb_ptr, Statement** statementPtr, const bool trigger, USHORT flags) { BlrParseWrapper csb(*tdbb->getDefaultPool(), relation, view_csb, csb_ptr, trigger, flags); @@ -206,7 +206,7 @@ void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node, csb->csb_node = node; if (statementPtr) - *statementPtr = JrdStatement::makeStatement(tdbb, csb, false); + *statementPtr = Statement::makeStatement(tdbb, csb, false); } diff --git a/src/jrd/par_proto.h b/src/jrd/par_proto.h index 02f0012e95..c8b23789d9 100644 --- a/src/jrd/par_proto.h +++ b/src/jrd/par_proto.h @@ -28,7 +28,7 @@ namespace Jrd { class CompilerScratch; class jrd_rel; class jrd_req; - class JrdStatement; + class Statement; class thread_db; class ItemInfo; class BoolExprNode; @@ -46,9 +46,9 @@ struct dsc; Jrd::ValueListNode* PAR_args(Jrd::thread_db*, Jrd::CompilerScratch*, USHORT, USHORT); Jrd::ValueListNode* PAR_args(Jrd::thread_db*, Jrd::CompilerScratch*); Jrd::DmlNode* PAR_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, ULONG blr_length, - Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::JrdStatement**, const bool, USHORT); + Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::Statement**, const bool, USHORT); void PAR_preparsed_node(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::DmlNode*, - Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::JrdStatement**, const bool, USHORT); + Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::Statement**, const bool, USHORT); Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR* blr, ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, USHORT); StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*); diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index 8565a4638b..e2d08454bc 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -240,7 +240,7 @@ Applier* Applier::create(thread_db* tdbb) Jrd::ContextPoolHolder context(tdbb, req_pool); AutoPtr csb(FB_NEW_POOL(*req_pool) CompilerScratch(*req_pool)); - const auto request = JrdStatement::makeRequest(tdbb, csb, true); + const auto request = Statement::makeRequest(tdbb, csb, true); request->validateTimeStamp(); request->req_attachment = attachment; diff --git a/src/jrd/req.h b/src/jrd/req.h index 688386fdfc..79299f3f92 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -266,7 +266,7 @@ private: }; public: - jrd_req(Attachment* attachment, /*const*/ JrdStatement* aStatement, + jrd_req(Attachment* attachment, /*const*/ Statement* aStatement, Firebird::MemoryStats* parent_stats) : statement(aStatement), req_pool(statement->pool), @@ -290,30 +290,30 @@ public: impureArea.grow(statement->impureSize); } - JrdStatement* getStatement() + Statement* getStatement() { return statement; } - const JrdStatement* getStatement() const + const Statement* getStatement() const { return statement; } bool hasInternalStatement() const { - return statement->flags & JrdStatement::FLAG_INTERNAL; + return statement->flags & Statement::FLAG_INTERNAL; } bool hasPowerfulStatement() const { - return statement->flags & JrdStatement::FLAG_POWERFUL; + return statement->flags & Statement::FLAG_POWERFUL; } void setAttachment(Attachment* newAttachment) { req_attachment = newAttachment; - charSetId = statement->flags & JrdStatement::FLAG_INTERNAL ? + charSetId = statement->flags & Statement::FLAG_INTERNAL ? CS_METADATA : req_attachment->att_charset; } @@ -345,7 +345,7 @@ public: } private: - JrdStatement* const statement; + Statement* const statement; mutable StmtNumber req_id; // request identifier TimeStampCache req_timeStampCache; // time stamp cache diff --git a/src/jrd/trace/TraceDSQLHelpers.h b/src/jrd/trace/TraceDSQLHelpers.h index 7d7570dac2..7c1f110f71 100644 --- a/src/jrd/trace/TraceDSQLHelpers.h +++ b/src/jrd/trace/TraceDSQLHelpers.h @@ -111,31 +111,31 @@ private: class TraceDSQLExecute { public: - TraceDSQLExecute(Attachment* attachment, DsqlRequest* request) : + TraceDSQLExecute(Attachment* attachment, DsqlRequest* dsqlRequest) : m_attachment(attachment), - m_request(request) + m_dsqlRequest(dsqlRequest) { - m_need_trace = m_request->req_traced && TraceManager::need_dsql_execute(m_attachment); + m_need_trace = m_dsqlRequest->req_traced && TraceManager::need_dsql_execute(m_attachment); if (!m_need_trace) return; { // scope - TraceSQLStatementImpl stmt(request, NULL); - TraceManager::event_dsql_execute(m_attachment, request->req_transaction, &stmt, true, + TraceSQLStatementImpl stmt(dsqlRequest, NULL); + TraceManager::event_dsql_execute(m_attachment, dsqlRequest->req_transaction, &stmt, true, ITracePlugin::RESULT_SUCCESS); } m_start_clock = fb_utils::query_performance_counter(); - m_request->req_fetch_elapsed = 0; - m_request->req_fetch_rowcount = 0; - fb_assert(!m_request->req_fetch_baseline); - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_elapsed = 0; + m_dsqlRequest->req_fetch_rowcount = 0; + fb_assert(!m_dsqlRequest->req_fetch_baseline); + m_dsqlRequest->req_fetch_baseline = NULL; - if (auto jrdRequest = m_request->getJrdRequest()) + if (auto request = m_dsqlRequest->getRequest()) { MemoryPool* pool = MemoryPool::getContextPool(); - m_request->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, jrdRequest->req_stats); + m_dsqlRequest->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, request->req_stats); } } @@ -147,19 +147,19 @@ public: m_need_trace = false; if (have_cursor) { - m_request->req_fetch_elapsed = fb_utils::query_performance_counter() - m_start_clock; + m_dsqlRequest->req_fetch_elapsed = fb_utils::query_performance_counter() - m_start_clock; return; } - TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - m_request->getJrdRequest() ? &m_request->getJrdRequest()->req_stats : NULL, + TraceRuntimeStats stats(m_attachment, m_dsqlRequest->req_fetch_baseline, + m_dsqlRequest->getRequest() ? &m_dsqlRequest->getRequest()->req_stats : NULL, fb_utils::query_performance_counter() - m_start_clock, - m_request->req_fetch_rowcount); + m_dsqlRequest->req_fetch_rowcount); - TraceSQLStatementImpl stmt(m_request, stats.getPerf()); - TraceManager::event_dsql_execute(m_attachment, m_request->req_transaction, &stmt, false, result); + TraceSQLStatementImpl stmt(m_dsqlRequest, stats.getPerf()); + TraceManager::event_dsql_execute(m_attachment, m_dsqlRequest->req_transaction, &stmt, false, result); - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_baseline = NULL; } ~TraceDSQLExecute() @@ -170,7 +170,7 @@ public: private: bool m_need_trace; Attachment* const m_attachment; - DsqlRequest* const m_request; + DsqlRequest* const m_dsqlRequest; SINT64 m_start_clock; }; @@ -179,14 +179,14 @@ class TraceDSQLFetch public: TraceDSQLFetch(Attachment* attachment, DsqlRequest* request) : m_attachment(attachment), - m_request(request) + m_dsqlRequest(request) { - m_need_trace = m_request->req_traced && TraceManager::need_dsql_execute(m_attachment) && - m_request->getJrdRequest() && (m_request->getJrdRequest()->req_flags & req_active); + m_need_trace = m_dsqlRequest->req_traced && TraceManager::need_dsql_execute(m_attachment) && + m_dsqlRequest->getRequest() && (m_dsqlRequest->getRequest()->req_flags & req_active); if (!m_need_trace) { - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_baseline = NULL; return; } @@ -204,30 +204,30 @@ public: return; m_need_trace = false; - m_request->req_fetch_elapsed += fb_utils::query_performance_counter() - m_start_clock; + m_dsqlRequest->req_fetch_elapsed += fb_utils::query_performance_counter() - m_start_clock; if (!eof) { - m_request->req_fetch_rowcount++; + m_dsqlRequest->req_fetch_rowcount++; return; } - TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - &m_request->getJrdRequest()->req_stats, m_request->req_fetch_elapsed, - m_request->req_fetch_rowcount); + TraceRuntimeStats stats(m_attachment, m_dsqlRequest->req_fetch_baseline, + &m_dsqlRequest->getRequest()->req_stats, m_dsqlRequest->req_fetch_elapsed, + m_dsqlRequest->req_fetch_rowcount); - TraceSQLStatementImpl stmt(m_request, stats.getPerf()); + TraceSQLStatementImpl stmt(m_dsqlRequest, stats.getPerf()); - TraceManager::event_dsql_execute(m_attachment, m_request->req_transaction, + TraceManager::event_dsql_execute(m_attachment, m_dsqlRequest->req_transaction, &stmt, false, result); - m_request->req_fetch_elapsed = 0; - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_elapsed = 0; + m_dsqlRequest->req_fetch_baseline = NULL; } private: bool m_need_trace; Attachment* const m_attachment; - DsqlRequest* const m_request; + DsqlRequest* const m_dsqlRequest; SINT64 m_start_clock; }; diff --git a/src/jrd/trace/TraceJrdHelpers.h b/src/jrd/trace/TraceJrdHelpers.h index 5643a48e1d..882b6f5d6c 100644 --- a/src/jrd/trace/TraceJrdHelpers.h +++ b/src/jrd/trace/TraceJrdHelpers.h @@ -317,7 +317,7 @@ public: m_which_trig(which_trig) { TraceManager* trace_mgr = m_tdbb->getAttachment()->att_trace_manager; - m_need_trace = !(m_request->getStatement()->flags & JrdStatement::FLAG_SYS_TRIGGER) && + m_need_trace = !(m_request->getStatement()->flags & Statement::FLAG_SYS_TRIGGER) && trace_mgr->needs(ITraceFactory::TRACE_EVENT_TRIGGER_EXECUTE); if (!m_need_trace) @@ -392,7 +392,7 @@ public: m_start_clock = fb_utils::query_performance_counter(); } - void finish(JrdStatement* statement, ntrace_result_t result) + void finish(Statement* statement, ntrace_result_t result) { if (!m_need_trace) return; @@ -442,11 +442,11 @@ public: m_request(request) { Attachment* attachment = m_tdbb->getAttachment(); - JrdStatement* statement = m_request->getStatement(); + Statement* statement = m_request->getStatement(); m_need_trace = attachment->att_trace_manager->needs(ITraceFactory::TRACE_EVENT_BLR_EXECUTE) && !statement->sqlText && - !(statement->flags & JrdStatement::FLAG_INTERNAL) && + !(statement->flags & Statement::FLAG_INTERNAL) && !attachment->isUtility(); if (!m_need_trace) diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index d6f55e3290..039b1cc7b7 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -169,21 +169,21 @@ ISC_INT64 TraceTransactionImpl::getInitialID() ISC_INT64 TraceSQLStatementImpl::getStmtID() { - if (m_stmt->getJrdRequest()) - return m_stmt->getJrdRequest()->getRequestId(); + if (m_stmt->getRequest()) + return m_stmt->getRequest()->getRequestId(); return 0; } const char* TraceSQLStatementImpl::getText() { - const string* stmtText = m_stmt->getStatement()->getSqlText(); + const string* stmtText = m_stmt->getDsqlStatement()->getSqlText(); return stmtText ? stmtText->c_str() : ""; } const char* TraceSQLStatementImpl::getTextUTF8() { - const string* stmtText = m_stmt->getStatement()->getSqlText(); + const string* stmtText = m_stmt->getDsqlStatement()->getSqlText(); if (m_textUTF8.isEmpty() && stmtText && !stmtText->isEmpty()) { @@ -211,8 +211,8 @@ void TraceSQLStatementImpl::fillPlan(bool explained) if (m_plan.isEmpty() || m_planExplained != explained) { m_planExplained = explained; - if (m_stmt->getJrdStatement()) - m_plan = Optimizer::getPlan(JRD_get_thread_data(), m_stmt->getJrdStatement(), m_planExplained); + if (m_stmt->getStatement()) + m_plan = Optimizer::getPlan(JRD_get_thread_data(), m_stmt->getStatement(), m_planExplained); } } diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index 2536d74613..ec47e03038 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -146,7 +146,7 @@ private: class TraceBLRStatementImpl : public BLRPrinter { public: - TraceBLRStatementImpl(const JrdStatement* stmt, Firebird::PerformanceInfo* perf) : + TraceBLRStatementImpl(const Statement* stmt, Firebird::PerformanceInfo* perf) : BLRPrinter(stmt->blr.begin(), stmt->blr.getCount()), m_stmt(stmt), m_perf(perf) @@ -156,7 +156,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; } private: - const JrdStatement* const m_stmt; + const Statement* const m_stmt; Firebird::PerformanceInfo* const m_perf; }; @@ -203,7 +203,7 @@ private: m_params(NULL), m_descs(pool) { - const dsql_msg* msg = m_stmt->getStatement()->getSendMsg(); + const dsql_msg* msg = m_stmt->getDsqlStatement()->getSendMsg(); if (msg) m_params = &msg->msg_parameters; } diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 180b863055..e01e08971c 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -3713,7 +3713,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_trg_rname, &desc); // check if this request go through without checking permissions - if (!(request->getStatement()->flags & (JrdStatement::FLAG_IGNORE_PERM | JrdStatement::FLAG_INTERNAL))) + if (!(request->getStatement()->flags & (Statement::FLAG_IGNORE_PERM | Statement::FLAG_INTERNAL))) SCL_check_relation(tdbb, &desc, SCL_control | SCL_alter); if (EVL_field(0, rpb->rpb_record, f_trg_rname, &desc2)) From 13bda0c1b61ce223d057aaf786570b624a8415ea Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 10 Feb 2022 00:05:46 +0000 Subject: [PATCH 070/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index a7b2a7084d..93ccfd914d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:393 + FORMAL BUILD NUMBER:399 */ -#define PRODUCT_VER_STRING "5.0.0.393" -#define FILE_VER_STRING "WI-T5.0.0.393" -#define LICENSE_VER_STRING "WI-T5.0.0.393" -#define FILE_VER_NUMBER 5, 0, 0, 393 +#define PRODUCT_VER_STRING "5.0.0.399" +#define FILE_VER_STRING "WI-T5.0.0.399" +#define LICENSE_VER_STRING "WI-T5.0.0.399" +#define FILE_VER_NUMBER 5, 0, 0, 399 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "393" +#define FB_BUILD_NO "399" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index a6addf4fa1..05e9e897ea 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=393 +BuildNum=399 NowAt=`pwd` cd `dirname $0` From b4be5b9ae2eccfb3097c11b82f78c8c41b668e83 Mon Sep 17 00:00:00 2001 From: Roman Simakov Date: Thu, 10 Feb 2022 12:27:43 +0300 Subject: [PATCH 071/187] Make DDL object type constants stable and extendable (#7125) Add shift to DDL object type while restoring backup of prior version. Rework DDL access checks. Remove unneeded constants, duplicating code. Rename some functions to suit their purpose. Provide a way to extent object type constants in future not changing existing ones. --- src/burp/restore.epp | 5 +- src/dsql/DdlNodes.epp | 43 ++++----- src/dsql/DdlNodes.h | 2 +- src/dsql/ExprNodes.cpp | 10 +-- src/dsql/PackageNodes.epp | 4 +- src/dsql/StmtNodes.cpp | 6 +- src/dsql/parse.y | 30 +++---- src/isql/extract.epp | 7 +- src/isql/show.epp | 83 ++++------------- src/isql/show_proto.h | 5 +- src/jrd/Function.h | 2 +- src/jrd/RecordSourceNodes.cpp | 2 +- src/jrd/Statement.cpp | 4 +- src/jrd/cmp.cpp | 8 +- src/jrd/cmp_proto.h | 2 +- src/jrd/exe.h | 9 +- src/jrd/grant.epp | 10 +-- src/jrd/grant_proto.h | 3 +- src/jrd/idx.cpp | 4 +- src/jrd/ini.epp | 5 +- src/jrd/jrd.h | 2 +- src/jrd/obj.h | 154 +++++++++++++++++++++++--------- src/jrd/optimizer/Optimizer.cpp | 2 +- src/jrd/scl.epp | 135 +++++++--------------------- src/jrd/scl.h | 18 ---- src/jrd/scl_proto.h | 6 +- src/jrd/tra.cpp | 4 +- 27 files changed, 251 insertions(+), 314 deletions(-) diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 178058a4cd..b43140d076 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -10239,6 +10239,8 @@ bool get_user_privilege(BurpGlobals* tdgbl) case att_priv_obj_type: flags |= USER_PRIV_OBJECT_TYPE; object_type = (USHORT) get_int32(tdgbl); + if ( (tdgbl->RESTORE_format < 11) && (object_type > 19) ) // FB 4 has a shift :( + object_type++; break; default: @@ -10310,9 +10312,6 @@ bool get_user_privilege(BurpGlobals* tdgbl) } break; - case obj_database: - break; - default: exists = true; break; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 9b2dfb3d7d..eb05fd8375 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1354,7 +1354,6 @@ void CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) switch (objType) { - case obj_schema: case obj_database: SCL_check_database(tdbb, SCL_alter); break; @@ -1577,12 +1576,6 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objNameStr); break; - case obj_schema: - tableClause = "rdb$schemas"; - columnClause = "rdb$schema_name"; - status << Arg::Gds(isc_dyn_schema_not_found) << Arg::Str(objNameStr); - break; - default: fb_assert(false); return; @@ -1741,7 +1734,7 @@ void CreateAlterFunctionNode::checkPermission(thread_db* tdbb, jrd_tra* transact return; } - SCL_check_create_access(tdbb, SCL_object_function); + SCL_check_create_access(tdbb, obj_functions); } void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -2749,7 +2742,7 @@ void CreateAlterProcedureNode::checkPermission(thread_db* tdbb, jrd_tra* transac return; } - SCL_check_create_access(tdbb, SCL_object_procedure); + SCL_check_create_access(tdbb, obj_procedures); } void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -3950,7 +3943,7 @@ string CreateCollationNode::internalPrint(NodePrinter& printer) const void CreateCollationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_collation); + SCL_check_create_access(tdbb, obj_collations); } void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -4318,7 +4311,7 @@ string CreateDomainNode::internalPrint(NodePrinter& printer) const void CreateDomainNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_domain); + SCL_check_create_access(tdbb, obj_domains); } void CreateDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -5456,7 +5449,7 @@ void CreateAlterExceptionNode::checkPermission(thread_db* tdbb, jrd_tra* transac return; } - SCL_check_create_access(tdbb, SCL_object_exception); + SCL_check_create_access(tdbb, obj_exceptions); } void CreateAlterExceptionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -5663,7 +5656,7 @@ void CreateAlterSequenceNode::checkPermission(thread_db* tdbb, jrd_tra* transact return; } - SCL_check_create_access(tdbb, SCL_object_generator); + SCL_check_create_access(tdbb, obj_generators); } void CreateAlterSequenceNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -7396,7 +7389,7 @@ string CreateRelationNode::internalPrint(NodePrinter& printer) const void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_table); + SCL_check_create_access(tdbb, obj_relations); } void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -8676,7 +8669,7 @@ void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) return; } - SCL_check_create_access(tdbb, SCL_object_view); + SCL_check_create_access(tdbb, obj_views); } void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -10098,7 +10091,7 @@ string CreateFilterNode::internalPrint(NodePrinter& printer) const void CreateFilterNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_filter); + SCL_check_create_access(tdbb, obj_filters); } // Define a blob filter. @@ -10354,7 +10347,7 @@ string CreateAlterRoleNode::internalPrint(NodePrinter& printer) const void CreateAlterRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { if (createFlag) - SCL_check_create_access(tdbb, SCL_object_role); + SCL_check_create_access(tdbb, obj_roles); else SCL_check_role(tdbb, name, SCL_alter); } @@ -11520,10 +11513,10 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G const GranteeClause* userNod, const char* privs, MetaName field, int options) { - SSHORT userType = userNod->first; + ObjectType userType = userNod->first; MetaName user(userNod->second); MetaName dummyName; - const SSHORT objType = object ? object->first : obj_type_MAX; + const ObjectType objType = object ? object->first : obj_type_MAX; const MetaName objName(object ? object->second : ""); bool crdb = false; @@ -11675,7 +11668,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G break; default: - fb_assert(object == NULL || objType >= obj_database); + fb_assert(object == NULL || isDdlObject(objType)); } if (options == 1) // with grant option @@ -11879,10 +11872,8 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G } default: - if (objType >= obj_database) - { + if (isDdlObject(objType)) checkGrantorCanGrantDdl(tdbb, transaction, currentUser.c_str(), priv, objName); - } else fb_assert(false); } @@ -12304,12 +12295,16 @@ void GrantRevokeNode::checkGrantorCanGrantDdl(thread_db* tdbb, jrd_tra* transact WITH ((PRV.RDB$USER = UPPERCASE(grantor.c_str()) AND PRV.RDB$USER_TYPE = obj_user) OR (PRV.RDB$USER_TYPE = obj_sql_role)) AND PRV.RDB$RELATION_NAME EQ objName.c_str() AND - PRV.RDB$OBJECT_TYPE >= obj_database AND + PRV.RDB$OBJECT_TYPE >= obj_database AND // it might be deleted but I believe it's more efficient PRV.RDB$PRIVILEGE EQ privilege { if ( (PRV.RDB$USER_TYPE == obj_sql_role) && !attachment->att_user->roleInUse(tdbb, PRV.RDB$USER)) continue; + // Double check if the object is DDL one. + if (!isDdlObject(PRV.RDB$OBJECT_TYPE)) + continue; + if (PRV.RDB$GRANT_OPTION == WITH_GRANT_OPTION) { grantable = true; diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index ff02b91ffc..4686f94eab 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -2279,7 +2279,7 @@ typedef RecreateNode > PrivilegeClause; -typedef Firebird::Pair > GranteeClause; +typedef Firebird::Pair > GranteeClause; class GrantRevokeNode : public PrivilegesNode, private ExecInSecurityDb { diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 45224c19fb..f103370450 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6624,14 +6624,14 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) tail->csb_view->rel_id : (csb->csb_view ? csb->csb_view->rel_id : 0); CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId, - privilege, SCL_object_table, relation->rel_name); + privilege, obj_relations, relation->rel_name); // Field-level privilege access is posted for every operation except DELETE if (privilege != SCL_delete) { CMP_post_access(tdbb, csb, field->fld_security_name, ssRelationId, - privilege, SCL_object_column, field->fld_name, relation->rel_name); + privilege, obj_column, field->fld_name, relation->rel_name); } } @@ -7039,7 +7039,7 @@ ValueExprNode* GenIdNode::pass1(thread_db* tdbb, CompilerScratch* csb) if (!identity) { CMP_post_access(tdbb, csb, generator.secName, 0, - SCL_usage, SCL_object_generator, generator.name); + SCL_usage, obj_generators, generator.name); } return this; @@ -12919,13 +12919,13 @@ ValueExprNode* UdfCallNode::pass1(thread_db* tdbb, CompilerScratch* csb) } CMP_post_access(tdbb, csb, function->getSecurityName(), ssRelationId, - SCL_execute, SCL_object_function, function->getName().identifier); + SCL_execute, obj_functions, function->getName().identifier); } else { CMP_post_access(tdbb, csb, function->getSecurityName(), (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, SCL_object_package, function->getName().package); + SCL_execute, obj_packages, function->getName().package); } ExternalAccess temp(ExternalAccess::exa_function, function->getId()); diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index be7eed2a61..0c50cc49c7 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -297,7 +297,7 @@ void CreateAlterPackageNode::checkPermission(thread_db* tdbb, jrd_tra* transacti return; } - SCL_check_create_access(tdbb, SCL_object_package); + SCL_check_create_access(tdbb, obj_packages); } @@ -699,7 +699,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) void CreatePackageBodyNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_package); + SCL_check_create_access(tdbb, obj_packages); } diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index a000c7199f..ee65d9b15f 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -4671,7 +4671,7 @@ ExceptionNode* ExceptionNode::pass1(thread_db* tdbb, CompilerScratch* csb) if (exception) { CMP_post_access(tdbb, csb, exception->secName, 0, - SCL_usage, SCL_object_exception, exception->name); + SCL_usage, obj_exceptions, exception->name); } return this; @@ -8421,7 +8421,7 @@ SetGeneratorNode* SetGeneratorNode::pass1(thread_db* tdbb, CompilerScratch* csb) doPass1(tdbb, csb, value.getAddress()); CMP_post_access(tdbb, csb, generator.secName, 0, - SCL_usage, SCL_object_generator, generator.name); + SCL_usage, obj_generators, generator.name); return this; } @@ -10625,7 +10625,7 @@ static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jr // unless this is an internal request, check access permission CMP_post_access(tdbb, csb, relation->rel_security_name, (view ? view->rel_id : 0), - priv, SCL_object_table, relation->rel_name); + priv, obj_relations, relation->rel_name); // ensure that the view is set for the input streams, // so that access to views can be checked at the field level diff --git a/src/dsql/parse.y b/src/dsql/parse.y index e9840aa749..55e590a41f 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -1009,7 +1009,7 @@ grant0($node) | db_ddl_privileges(NOTRIAL(&$node->privileges)) DATABASE TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by { - $node->object = newNode(obj_database, get_object_name(obj_database)); + $node->object = newNode(obj_database, getSecurityClassName(obj_database)); $node->grantAdminOption = $5; $node->grantor = $6; $node->isDdl = true; @@ -1025,31 +1025,31 @@ grant0($node) %type object object : TABLE - { $$ = newNode(obj_relations, get_object_name(obj_relations)); } + { $$ = newNode(obj_relations, getSecurityClassName(obj_relations)); } | VIEW - { $$ = newNode(obj_views, get_object_name(obj_views)); } + { $$ = newNode(obj_views, getSecurityClassName(obj_views)); } | PROCEDURE - { $$ = newNode(obj_procedures, get_object_name(obj_procedures)); } + { $$ = newNode(obj_procedures, getSecurityClassName(obj_procedures)); } | FUNCTION - { $$ = newNode(obj_functions, get_object_name(obj_functions)); } + { $$ = newNode(obj_functions, getSecurityClassName(obj_functions)); } | PACKAGE - { $$ = newNode(obj_packages, get_object_name(obj_packages)); } + { $$ = newNode(obj_packages, getSecurityClassName(obj_packages)); } | GENERATOR - { $$ = newNode(obj_generators, get_object_name(obj_generators)); } + { $$ = newNode(obj_generators, getSecurityClassName(obj_generators)); } | SEQUENCE - { $$ = newNode(obj_generators, get_object_name(obj_generators)); } + { $$ = newNode(obj_generators, getSecurityClassName(obj_generators)); } | DOMAIN - { $$ = newNode(obj_domains, get_object_name(obj_domains)); } + { $$ = newNode(obj_domains, getSecurityClassName(obj_domains)); } | EXCEPTION - { $$ = newNode(obj_exceptions, get_object_name(obj_exceptions)); } + { $$ = newNode(obj_exceptions, getSecurityClassName(obj_exceptions)); } | ROLE - { $$ = newNode(obj_roles, get_object_name(obj_roles)); } + { $$ = newNode(obj_roles, getSecurityClassName(obj_roles)); } | CHARACTER SET - { $$ = newNode(obj_charsets, get_object_name(obj_charsets)); } + { $$ = newNode(obj_charsets, getSecurityClassName(obj_charsets)); } | COLLATION - { $$ = newNode(obj_collations, get_object_name(obj_collations)); } + { $$ = newNode(obj_collations, getSecurityClassName(obj_collations)); } | FILTER - { $$ = newNode(obj_filters, get_object_name(obj_filters)); } + { $$ = newNode(obj_filters, getSecurityClassName(obj_filters)); } ; table_noise @@ -1266,7 +1266,7 @@ revoke0($node) | rev_grant_option db_ddl_privileges(NOTRIAL(&$node->privileges)) DATABASE FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by { - $node->object = newNode(obj_database, get_object_name(obj_database)); + $node->object = newNode(obj_database, getSecurityClassName(obj_database)); $node->grantAdminOption = $1; $node->grantor = $6; $node->isDdl = true; diff --git a/src/isql/extract.epp b/src/isql/extract.epp index af51723ab9..24863068f3 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -1404,10 +1404,13 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi ***/ // Process DDL permissions - for (int i = obj_database; i < obj_type_MAX; i++) + for (SSHORT obj = obj_database; obj < obj_type_MAX; obj++) { + if (!isDdlObject(obj)) + continue; + const processing_state rc = - SHOW_grants2(get_object_name(i), terminator, i, first ? banner : 0, mangle); + SHOW_grants2(getSecurityClassName(obj), terminator, obj, first ? banner : 0, mangle); if (rc == SKIP) first = false; } diff --git a/src/isql/show.epp b/src/isql/show.epp index c4511d11ed..85a6a76168 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -655,7 +655,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle, } -processing_state SHOW_grants(const SCHAR* object, const SCHAR* terminator, USHORT obj_type) +processing_state SHOW_grants(const SCHAR* object, const SCHAR* terminator, ObjectType obj_type) { /************************************** * @@ -786,7 +786,7 @@ static void set_grantee(int user_type, const char* SQL_identifier, char* user_st processing_state SHOW_grants2 (const SCHAR* object, const SCHAR* terminator, - USHORT obj_type, + ObjectType obj_type, const TEXT* optional_msg, bool mangle) { @@ -837,7 +837,7 @@ processing_state SHOW_grants2 (const SCHAR* object, USHORT priv_flags = 0; SSHORT prev_field_null = -1; - if (obj_type == obj_relation || obj_type == 255) + if (obj_type == obj_relation || obj_type == obj_any) { // Find the user specified relation and show its privileges @@ -1020,7 +1020,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } // No relation called "object" was found, try procedure "object" - if (obj_type == obj_procedure || obj_type == 255) + if (obj_type == obj_procedure || obj_type == obj_any) { FOR FIRST 1 P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ object AND @@ -1099,7 +1099,7 @@ processing_state SHOW_grants2 (const SCHAR* object, // No procedure called "object" was found, try role "object" SCHAR role_name[BUFFER_LENGTH256]; - if (obj_type == obj_sql_role || obj_type == 255) + if (obj_type == obj_sql_role || obj_type == obj_any) { // No procedure called "object" was found, try role "object" // CVC: This code could be superseded by SHOW_grant_roles() below @@ -1164,7 +1164,7 @@ processing_state SHOW_grants2 (const SCHAR* object, return (SKIP); } - if (obj_type == obj_package_header || obj_type == 255) + if (obj_type == obj_package_header || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1238,7 +1238,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } } - if (obj_type == obj_udf || obj_type == 255) + if (obj_type == obj_udf || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1316,7 +1316,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } } - if (obj_type == obj_generator || obj_type == 255) + if (obj_type == obj_generator || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1390,7 +1390,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } } - if (obj_type == obj_exception || obj_type == 255) + if (obj_type == obj_exception || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1688,7 +1688,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } ***/ - if (obj_type >= obj_database || obj_type == 255) + if (isDdlObject(obj_type) || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1698,6 +1698,10 @@ processing_state SHOW_grants2 (const SCHAR* object, PRV.RDB$RELATION_NAME EQ object SORTED BY PRV.RDB$USER, PRV.RDB$GRANT_OPTION + // Double check if the object is DDL one. + if (!isDdlObject(PRV.RDB$OBJECT_TYPE)) + continue; + if (first && optional_msg) isqlGlob.prints(optional_msg); @@ -1753,60 +1757,7 @@ processing_state SHOW_grants2 (const SCHAR* object, set_grantee(PRV.RDB$USER_TYPE, SQL_identifier, user_string); - switch (PRV.RDB$OBJECT_TYPE) - { - case obj_database: - strcpy(obj_string, "DATABASE"); - break; - - case obj_relations: - strcpy(obj_string, "TABLE"); - break; - - case obj_views: - strcpy(obj_string, "VIEW"); - break; - - case obj_procedures: - strcpy(obj_string, "PROCEDURE"); - break; - - case obj_functions: - strcpy(obj_string, "FUNCTION"); - break; - - case obj_packages: - strcpy(obj_string, "PACKAGE"); - break; - - case obj_generators: - strcpy(obj_string, "GENERATOR"); - break; - - case obj_domains: - strcpy(obj_string, "DOMAIN"); - break; - - case obj_exceptions: - strcpy(obj_string, "EXCEPTION"); - break; - - case obj_roles: - strcpy(obj_string, "ROLE"); - break; - - case obj_charsets: - strcpy(obj_string, "CHARACTER SET"); - break; - - case obj_collations: - strcpy(obj_string, "COLLATION"); - break; - - case obj_filters: - strcpy(obj_string, "FILTER"); - break; - } + strcpy(obj_string, getDdlObjectName(PRV.RDB$OBJECT_TYPE)); if (PRV.RDB$GRANT_OPTION) strcpy(with_option, " WITH GRANT OPTION"); @@ -1824,7 +1775,7 @@ processing_state SHOW_grants2 (const SCHAR* object, return ps_ERR; END_ERROR - if (obj_type == obj_database || obj_type == 255) + if (obj_type == obj_database || obj_type == obj_any) { FOR PRV IN SEC$DB_CREATORS SORTED BY PRV.SEC$USER_TYPE, PRV.SEC$USER @@ -2448,7 +2399,7 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) } else strcpy(SQL_id_for_grant, cmd[2]); - ret = SHOW_grants(SQL_id_for_grant, "", 255); + ret = SHOW_grants(SQL_id_for_grant, "", obj_any); } else { diff --git a/src/isql/show_proto.h b/src/isql/show_proto.h index 0184db3b39..8a03884be2 100644 --- a/src/isql/show_proto.h +++ b/src/isql/show_proto.h @@ -26,11 +26,12 @@ #include "../common/classes/fb_string.h" #include +#include "../jrd/obj.h" void SHOW_comments(bool force); bool SHOW_dbb_parameters (Firebird::IAttachment*, SCHAR*, const UCHAR*, unsigned, bool, const char*); -processing_state SHOW_grants (const SCHAR*, const SCHAR*, USHORT); -processing_state SHOW_grants2 (const SCHAR*, const SCHAR*, USHORT, const TEXT*, bool); +processing_state SHOW_grants (const SCHAR*, const SCHAR*, ObjectType); +processing_state SHOW_grants2 (const SCHAR*, const SCHAR*, ObjectType, const TEXT*, bool); void SHOW_grant_roles (const SCHAR*, bool*); void SHOW_grant_roles2 (const SCHAR*, bool*, const TEXT*, bool); void SHOW_print_metadata_text_blob(FILE*, ISC_QUAD*, bool escape_squote = false, diff --git a/src/jrd/Function.h b/src/jrd/Function.h index 8f746628e8..5ddb9acd40 100644 --- a/src/jrd/Function.h +++ b/src/jrd/Function.h @@ -65,7 +65,7 @@ namespace Jrd virtual SLONG getSclType() const { - return SCL_object_function; + return obj_functions; } virtual bool checkCache(thread_db* tdbb) const; diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 1f5239e75e..881d2a6d49 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -724,7 +724,7 @@ RecordSourceNode* RelationSourceNode::pass1(thread_db* tdbb, CompilerScratch* cs const SLONG ssRelationId = tail->csb_view ? tail->csb_view->rel_id : view ? view->rel_id : csb->csb_view ? csb->csb_view->rel_id : 0; CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId, - SCL_select, SCL_object_table, relation->rel_name); + SCL_select, obj_relations, relation->rel_name); } return this; diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 7026093c9d..51a4870152 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -708,12 +708,12 @@ void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, if (!(ownerRelation->rel_flags & REL_system)) { - if (access->acc_type == SCL_object_table && + if (access->acc_type == obj_relations && (ownerRelation->rel_name == access->acc_name)) { continue; } - if (access->acc_type == SCL_object_column && + if (access->acc_type == obj_column && (ownerRelation->rel_name == access->acc_r_name)) { continue; diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index c957d5b4c8..07d143ee91 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -327,7 +327,7 @@ void CMP_post_access(thread_db* tdbb, const MetaName& security_name, SLONG ssRelationId, // SQL SECURITY relation in which context permissions should be check SecurityClass::flags_t mask, - SLONG type_name, + ObjectType obj_type, const MetaName& name, const MetaName& r_name) { @@ -353,7 +353,7 @@ void CMP_post_access(thread_db* tdbb, SET_TDBB(tdbb); - AccessItem access(security_name, ssRelationId, name, type_name, mask, r_name); + AccessItem access(security_name, ssRelationId, name, obj_type, mask, r_name); FB_SIZE_T i; @@ -487,13 +487,13 @@ void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* p { CMP_post_access(tdbb, csb, procedure->getSecurityName(), (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, SCL_object_procedure, procedure->getName().identifier); + SCL_execute, obj_procedures, procedure->getName().identifier); } else { CMP_post_access(tdbb, csb, procedure->getSecurityName(), (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, SCL_object_package, procedure->getName().package); + SCL_execute, obj_packages, procedure->getName().package); } // Add the procedure to list of external objects accessed diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index eb85916e88..4312fe3f21 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -47,7 +47,7 @@ Jrd::jrd_req* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&); void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName&, SLONG ssRelationId, - Jrd::SecurityClass::flags_t, SLONG type_name, const Jrd::MetaName&, + Jrd::SecurityClass::flags_t, ObjectType obj_type, const Jrd::MetaName&, const Jrd::MetaName& = ""); void CMP_post_procedure_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*); diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 3d175c2a86..244acbd4a2 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -187,9 +187,10 @@ typedef Firebird::SortedArray, struct AccessItem { MetaName acc_security_name; - SLONG acc_ss_rel_id; // Relation Id which owner will be used to check permissions - MetaName acc_name, acc_r_name; - SLONG acc_type; + SLONG acc_ss_rel_id; // Relation Id which owner will be used to check permissions + MetaName acc_name; + MetaName acc_r_name; + ObjectType acc_type; SecurityClass::flags_t acc_mask; static bool greaterThan(const AccessItem& i1, const AccessItem& i2) @@ -224,7 +225,7 @@ struct AccessItem } AccessItem(const MetaName& security_name, SLONG view_id, - const MetaName& name, SLONG type, + const MetaName& name, ObjectType type, SecurityClass::flags_t mask, const MetaName& relName) : acc_security_name(security_name), acc_ss_rel_id(view_id), acc_name(name), acc_r_name(relName), acc_type(type), acc_mask(mask) diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index 6106c09872..aabe8453cf 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -71,7 +71,7 @@ DATABASE DB = STATIC "yachts.lnk"; static void define_default_class(thread_db*, const TEXT*, MetaName&, const Acl&, jrd_tra*); static void finish_security_class(Acl&, SecurityClass::flags_t); -static void get_object_info(thread_db*, const TEXT*, SSHORT, +static void get_object_info(thread_db*, const TEXT*, ObjectType, MetaName&, MetaName&, MetaName&, bool&); static SecurityClass::flags_t get_public_privs(thread_db*, const TEXT*, SSHORT); static void get_user_privs(thread_db*, Acl&, const TEXT*, SSHORT, const MetaName&, @@ -85,7 +85,7 @@ static SecurityClass::flags_t squeeze_acl(Acl&, const MetaName&, SSHORT); static bool check_string(const UCHAR*, const MetaName&); -void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, jrd_tra* transaction) +void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, ObjectType id, jrd_tra* transaction) { /************************************** * @@ -142,7 +142,7 @@ void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, break; default: - if (id >= obj_database && id < obj_type_MAX) + if (isDdlObject(id)) priv = OWNER_PRIVS; break; } @@ -314,7 +314,7 @@ static SecurityClass::flags_t get_public_privs(thread_db* tdbb, static void get_object_info(thread_db* tdbb, const TEXT* object_name, - SSHORT obj_type, + ObjectType obj_type, MetaName& owner, MetaName& s_class, MetaName& default_class, @@ -521,7 +521,7 @@ static void get_object_info(thread_db* tdbb, } else { - s_class = get_object_name(obj_type); + s_class = getSecurityClassName(obj_type); default_class = ""; owner = tdbb->getDatabase()->dbb_owner; view = false; diff --git a/src/jrd/grant_proto.h b/src/jrd/grant_proto.h index 3609fba2bd..b34db32028 100644 --- a/src/jrd/grant_proto.h +++ b/src/jrd/grant_proto.h @@ -25,12 +25,13 @@ #define JRD_GRANT_PROTO_H #include "../common/classes/fb_string.h" +#include "../jrd/obj.h" namespace Jrd { class DeferredWork; } -void GRANT_privileges(Jrd::thread_db*, const Firebird::string&, USHORT, Jrd::jrd_tra*); +void GRANT_privileges(Jrd::thread_db*, const Firebird::string&, ObjectType, Jrd::jrd_tra*); #endif // JRD_GRANT_PROTO_H diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index e6997714c4..8cdfe811a3 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -167,11 +167,11 @@ void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_ CMP_post_access(tdbb, csb, referenced_relation->rel_security_name, (view ? view->rel_id : 0), - SCL_references, SCL_object_table, + SCL_references, obj_relations, referenced_relation->rel_name); CMP_post_access(tdbb, csb, referenced_field->fld_security_name, 0, - SCL_references, SCL_object_column, + SCL_references, obj_column, referenced_field->fld_name, referenced_relation->rel_name); } diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 2054ad85f5..da745683b5 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -524,9 +524,10 @@ void INI_format(const char* owner, const char* charset) length = acl - buffer; - for (int ddl_obj = obj_database + 1; ddl_obj < obj_type_MAX; ++ddl_obj) + for (int obj = obj_database + 1; obj < obj_type_MAX; obj++) { - add_security_class(tdbb, reqAddSC, get_object_name(ddl_obj), length, buffer); + if (isDdlObject(obj)) + add_security_class(tdbb, reqAddSC, getSecurityClassName(obj), length, buffer); } { // scope diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 78ddc4ca54..0cc93f82a5 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -248,7 +248,7 @@ public: virtual SLONG getSclType() const { - return SCL_object_procedure; + return obj_procedures; } virtual void releaseFormat() diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 1bb0bf9b7e..f836d592b1 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -24,56 +24,88 @@ #ifndef JRD_OBJ_H #define JRD_OBJ_H -// Object types used in RDB$DEPENDENCIES and RDB$USER_PRIVILEGES +// Object types used in RDB$DEPENDENCIES and RDB$USER_PRIVILEGES and stored in backup. // Note: some values are hard coded in grant.gdl +// Keep existing constants unchanged. -const int obj_relation = 0; -const int obj_view = 1; -const int obj_trigger = 2; -const int obj_computed = 3; -const int obj_validation = 4; -const int obj_procedure = 5; -const int obj_expression_index = 6; -const int obj_exception = 7; -const int obj_user = 8; -const int obj_field = 9; -const int obj_index = 10; -const int obj_charset = 11; -const int obj_user_group = 12; -const int obj_sql_role = 13; -const int obj_generator = 14; -const int obj_udf = 15; -const int obj_blob_filter = 16; -const int obj_collation = 17; -const int obj_package_header = 18; -const int obj_package_body = 19; -const int obj_privilege = 20; +typedef SSHORT ObjectType; -const int obj_last_non_ddl = 20; // keep in sync!!! +const ObjectType obj_relation = 0; +const ObjectType obj_view = 1; +const ObjectType obj_trigger = 2; +const ObjectType obj_computed = 3; +const ObjectType obj_validation = 4; +const ObjectType obj_procedure = 5; +const ObjectType obj_expression_index = 6; +const ObjectType obj_exception = 7; +const ObjectType obj_user = 8; +const ObjectType obj_field = 9; +const ObjectType obj_index = 10; +const ObjectType obj_charset = 11; +const ObjectType obj_user_group = 12; +const ObjectType obj_sql_role = 13; +const ObjectType obj_generator = 14; +const ObjectType obj_udf = 15; +const ObjectType obj_blob_filter = 16; +const ObjectType obj_collation = 17; +const ObjectType obj_package_header = 18; +const ObjectType obj_package_body = 19; +const ObjectType obj_privilege = 20; // objects types for ddl operations -const int obj_database = obj_last_non_ddl + 1; -const int obj_relations = obj_last_non_ddl + 2; -const int obj_views = obj_last_non_ddl + 3; -const int obj_procedures = obj_last_non_ddl + 4; -const int obj_functions = obj_last_non_ddl + 5; -const int obj_packages = obj_last_non_ddl + 6; -const int obj_generators = obj_last_non_ddl + 7; -const int obj_domains = obj_last_non_ddl + 8; -const int obj_exceptions = obj_last_non_ddl + 9; -const int obj_roles = obj_last_non_ddl + 10; -const int obj_charsets = obj_last_non_ddl + 11; -const int obj_collations = obj_last_non_ddl + 12; -const int obj_filters = obj_last_non_ddl + 13; +const ObjectType obj_database = 21; +const ObjectType obj_relations = 22; +const ObjectType obj_views = 23; +const ObjectType obj_procedures = 24; +const ObjectType obj_functions = 25; +const ObjectType obj_packages = 26; +const ObjectType obj_generators = 27; +const ObjectType obj_domains = 28; +const ObjectType obj_exceptions = 29; +const ObjectType obj_roles = 30; +const ObjectType obj_charsets = 31; +const ObjectType obj_collations = 32; +const ObjectType obj_filters = 33; -const int obj_type_MAX = obj_last_non_ddl + 14; // keep this last! +// Add new codes here if they are used in RDB$DEPENDENCIES or RDB$USER_PRIVILEGES or stored in backup +// Codes for DDL operations add in isDdlObject function as well (find it below). +// etc. obj_tablespaces, + +const ObjectType obj_type_MAX = 34; // used in the parser only / no relation with obj_type_MAX (should be greater) -const int obj_user_or_role = 100; -const int obj_schema = 101; -const int obj_parameter = 102; +const ObjectType obj_user_or_role= 100; +const ObjectType obj_parameter = 101; +const ObjectType obj_column = 102; -inline const char* get_object_name(int object_type) +const ObjectType obj_any = 255; + + +inline bool isDdlObject(ObjectType object_type) +{ + switch (object_type) + { + case obj_database: + case obj_relations: + case obj_views: + case obj_procedures: + case obj_functions: + case obj_packages: + case obj_generators: + case obj_filters: + case obj_domains: + case obj_exceptions: + case obj_roles: + case obj_charsets: + case obj_collations: + return true; + default: + return false; + } +} + + +inline const char* getSecurityClassName(ObjectType object_type) { switch (object_type) { @@ -108,4 +140,44 @@ inline const char* get_object_name(int object_type) } } + +inline const char* getDdlObjectName(ObjectType object_type) +{ + switch (object_type) + { + case obj_database: + return "DATABASE"; + case obj_relations: + return "TABLE"; + case obj_packages: + return "PACKAGE"; + case obj_procedures: + return "PROCEDURE"; + case obj_functions: + return "FUNCTION"; + case obj_column: + return "COLUMN"; + case obj_charsets: + return "CHARACTER SET"; + case obj_collations: + return "COLLATION"; + case obj_domains: + return "DOMAIN"; + case obj_exceptions: + return "EXCEPTION"; + case obj_generators: + return "GENERATOR"; + case obj_views: + return "VIEW"; + case obj_roles: + return "ROLE"; + case obj_filters: + return "FILTER"; + default: + fb_assert(false); + return ""; + } +} + + #endif // JRD_OBJ_H diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index ef46e476a0..0dda7f9993 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -970,7 +970,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) CMP_post_access(tdbb, csb, tail->csb_relation->rel_security_name, tail->csb_view ? tail->csb_view->rel_id : 0, - SCL_update, SCL_object_table, tail->csb_relation->rel_name); + SCL_update, obj_relations, tail->csb_relation->rel_name); } rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) LockedStream(csb, rsb); diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index b0bed3b8f3..e64307c62e 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -75,10 +75,10 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClas static SecurityClass::flags_t get_sys_privileges(thread_db* tdbb); static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl&, const MetaName&, SLONG, const MetaName&); -static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, const MetaName& name, +static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, ObjectType type, const MetaName& name, const MetaName& r_name, const MetaName &invoker); static bool check_object(thread_db* tdbb, bool found, const SecurityClass* s_class, SLONG obj_type, - const MetaName& obj_name, SecurityClass::flags_t mask, SLONG type, const MetaName& name); + const MetaName& obj_name, SecurityClass::flags_t mask, ObjectType type, const MetaName& name); namespace @@ -105,79 +105,10 @@ namespace { SCL_create, priv_create, "CREATE" }, { 0, 0, "" } }; - - - // Database is never requested through CMP_post_access. - const char* const object_str_database = "DATABASE"; - //const char* const object_str_schema = "SCHEMA"; - const char* const object_str_table = "TABLE"; - const char* const object_str_package = "PACKAGE"; - const char* const object_str_procedure = "PROCEDURE"; - const char* const object_str_function = "FUNCTION"; - const char* const object_str_column = "COLUMN"; - const char* const object_str_charset = "CHARACTER SET"; - const char* const object_str_collation = "COLLATION"; - const char* const object_str_domain = "DOMAIN"; - const char* const object_str_exception = "EXCEPTION"; - const char* const object_str_generator = "GENERATOR"; - const char* const object_str_view = "VIEW"; - const char* const object_str_role = "ROLE"; - const char* const object_str_filter = "FILTER"; - - - struct SecObjectNamePriority - { - const char* name; - SLONG num; - }; - - const SecObjectNamePriority priorities[] = - { - {object_str_database, SCL_object_database}, - //{object_str_schema, SCL_object_schema}, - {object_str_table, SCL_object_table}, - {object_str_package, SCL_object_package}, - {object_str_procedure, SCL_object_procedure}, - {object_str_function, SCL_object_function}, - {object_str_column, SCL_object_column}, - {object_str_charset, SCL_object_charset}, - {object_str_collation, SCL_object_collation}, - {object_str_domain, SCL_object_domain}, - {object_str_exception, SCL_object_exception}, - {object_str_generator, SCL_object_generator}, - {object_str_view, SCL_object_view}, - {object_str_role, SCL_object_role}, - {object_str_filter, SCL_object_filter}, - {"", 0} - }; - - /* Unused, may be needed - SLONG accTypeStrToNum(const char* name) - { - for (const SecObjectNamePriority* p = priorities; p->num != 0; ++p) - { - if (strcmp(p->name, name) == 0) - return p->num; - } - fb_assert(false); - return 0; - } - */ - - const char* accTypeNumToStr(const SLONG num) - { - for (const SecObjectNamePriority* p = priorities; p->num != 0; ++p) - { - if (p->num == num) - return p->name; - } - fb_assert(false); - return ""; - } } // anonymous namespace -static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, const MetaName& name, +static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, ObjectType type, const MetaName& name, const MetaName& r_name, const MetaName& invoker) { const P_NAMES* names; @@ -187,13 +118,13 @@ static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, break; } - const char* const typeAsStr = accTypeNumToStr(type); + const char* const ddlObjectName = getDdlObjectName(type); const Firebird::string fullName = r_name.hasData() ? r_name.c_str() + Firebird::string(".") + name.c_str() : name.c_str(); Arg::StatusVector status; status << Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(typeAsStr) << + Arg::Str(ddlObjectName) << Arg::Str(fullName); if (invoker.hasData()) { @@ -207,10 +138,10 @@ static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, void SCL_check_access(thread_db* tdbb, const SecurityClass* s_class, - SLONG obj_type, + SLONG obj_id, const MetaName& obj_name, SecurityClass::flags_t mask, - SLONG type, + ObjectType type, bool recursive, const MetaName& name, const MetaName& r_name) @@ -252,13 +183,13 @@ void SCL_check_access(thread_db* tdbb, return; // Check global DDL permissions with ANY option which allow user to make changes non owned objects - if ((type > obj_last_non_ddl) && (mask & SCL_get_object_mask(type))) + if (isDdlObject(type) && (mask & SCL_get_object_mask(type))) return; if (!s_class || (mask & s_class->scl_flags) ) return; - if (obj_name.hasData() && (compute_access(tdbb, s_class, obj_type, obj_name) & mask) ) + if (obj_name.hasData() && (compute_access(tdbb, s_class, obj_id, obj_name) & mask) ) { return; } @@ -266,8 +197,8 @@ void SCL_check_access(thread_db* tdbb, // Allow recursive procedure/function call if (recursive && - ((type == SCL_object_procedure && obj_type == id_procedure) || - (type == SCL_object_function && obj_type == id_function)) && + ((type == obj_procedures && obj_id == id_procedure) || + (type == obj_functions && obj_id == id_function)) && obj_name == name) { return; @@ -277,7 +208,7 @@ void SCL_check_access(thread_db* tdbb, } -void SCL_check_create_access(thread_db* tdbb, int type) +void SCL_check_create_access(thread_db* tdbb, ObjectType type) { /************************************** * @@ -305,7 +236,7 @@ void SCL_check_create_access(thread_db* tdbb, int type) if (!(obj_mask & SCL_create)) { - const char* name = accTypeNumToStr(type); + const char* name = getDdlObjectName(type); ERR_post(Arg::Gds(isc_dyn_no_create_priv) << name); } } @@ -338,7 +269,7 @@ void SCL_check_charset(thread_db* tdbb, const MetaName& name, SecurityClass::fla } END_FOR - SCL_check_access(tdbb, s_class, 0, name, mask, SCL_object_charset, false, name); + SCL_check_access(tdbb, s_class, 0, name, mask, obj_charsets, false, name); } @@ -369,7 +300,7 @@ void SCL_check_collation(thread_db* tdbb, const MetaName& name, SecurityClass::f } END_FOR - SCL_check_access(tdbb, s_class, 0, name, mask, SCL_object_collation, false, name); + SCL_check_access(tdbb, s_class, 0, name, mask, obj_collations, false, name); } @@ -406,7 +337,7 @@ void SCL_check_database(thread_db* tdbb, SecurityClass::flags_t mask) } ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(object_str_database) << + Arg::Str(getDdlObjectName(obj_database)) << Arg::Str("")); } @@ -438,7 +369,7 @@ void SCL_check_domain(thread_db* tdbb, const MetaName& name, SecurityClass::flag } END_FOR - SCL_check_access(tdbb, s_class, 0, name, mask, SCL_object_domain, false, name); + SCL_check_access(tdbb, s_class, 0, name, mask, obj_domains, false, name); } @@ -471,7 +402,7 @@ bool SCL_check_exception(thread_db* tdbb, const MetaName& name, SecurityClass::f } END_FOR - return check_object(tdbb, found, s_class, 0, name, mask, SCL_object_exception, name); + return check_object(tdbb, found, s_class, 0, name, mask, obj_exceptions, name); } @@ -504,7 +435,7 @@ bool SCL_check_generator(thread_db* tdbb, const MetaName& name, SecurityClass::f } END_FOR - return check_object(tdbb, found, s_class, 0, name, mask, SCL_object_generator, name); + return check_object(tdbb, found, s_class, 0, name, mask, obj_generators, name); } @@ -590,7 +521,7 @@ void SCL_check_index(thread_db* tdbb, const MetaName& index_name, UCHAR index_id { // Someone is going to reference system table in FK // Usually it's not good idea - raiseError(tdbb, mask, SCL_object_table, reln_name, "", NULL); + raiseError(tdbb, mask, obj_relations, reln_name, "", NULL); } // Check if the relation exists. It may not have been created yet. @@ -599,7 +530,7 @@ void SCL_check_index(thread_db* tdbb, const MetaName& index_name, UCHAR index_id if (reln_name.isEmpty()) return; - SCL_check_access(tdbb, s_class, 0, NULL, mask, SCL_object_table, false, reln_name); + SCL_check_access(tdbb, s_class, 0, NULL, mask, obj_relations, false, reln_name); request.reset(); @@ -620,7 +551,7 @@ void SCL_check_index(thread_db* tdbb, const MetaName& index_name, UCHAR index_id s_class = (!RF.RDB$SECURITY_CLASS.NULL) ? SCL_get_class(tdbb, RF.RDB$SECURITY_CLASS) : default_s_class; SCL_check_access(tdbb, s_class, 0, NULL, mask, - SCL_object_column, false, RF.RDB$FIELD_NAME, reln_name); + obj_column, false, RF.RDB$FIELD_NAME, reln_name); } END_FOR } @@ -663,7 +594,7 @@ bool SCL_check_package(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flag } END_FOR - return check_object(tdbb, found, s_class, id_package, name, mask, SCL_object_package, name); + return check_object(tdbb, found, s_class, id_package, name, mask, obj_packages, name); } @@ -705,7 +636,7 @@ bool SCL_check_procedure(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fl } END_FOR - return check_object(tdbb, found, s_class, id_procedure, name, mask, SCL_object_procedure, name); + return check_object(tdbb, found, s_class, id_procedure, name, mask, obj_procedures, name); } @@ -747,7 +678,7 @@ bool SCL_check_function(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla } END_FOR - return check_object(tdbb, found, s_class, id_function, name, mask, SCL_object_function, name); + return check_object(tdbb, found, s_class, id_function, name, mask, obj_functions, name); } @@ -781,7 +712,7 @@ void SCL_check_filter(thread_db* tdbb, const MetaName &name, SecurityClass::flag } END_FOR - SCL_check_access(tdbb, s_class, id_filter, name, mask, SCL_object_filter, false, name); + SCL_check_access(tdbb, s_class, id_filter, name, mask, obj_filters, false, name); } @@ -819,7 +750,7 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla { // Someone is going to modify system table layout // Usually it's not good idea - raiseError(tdbb, mask, SCL_object_table, name, "", NULL); + raiseError(tdbb, mask, obj_relations, name, "", NULL); } if (!REL.RDB$SECURITY_CLASS.NULL) @@ -827,7 +758,7 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla } END_FOR - SCL_check_access(tdbb, s_class, 0, NULL, mask, SCL_object_table, false, name); + SCL_check_access(tdbb, s_class, 0, NULL, mask, obj_relations, false, name); } bool SCL_check_view(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t mask) @@ -866,7 +797,7 @@ bool SCL_check_view(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t } END_FOR - return check_object(tdbb, found, s_class, 0, NULL, mask, SCL_object_view, name); + return check_object(tdbb, found, s_class, 0, NULL, mask, obj_views, name); } void SCL_check_role(thread_db* tdbb, const MetaName& name, SecurityClass::flags_t mask) @@ -896,7 +827,7 @@ void SCL_check_role(thread_db* tdbb, const MetaName& name, SecurityClass::flags_ } END_FOR - SCL_check_access(tdbb, s_class, 0, NULL, mask, SCL_object_role, false, name); + SCL_check_access(tdbb, s_class, 0, NULL, mask, obj_roles, false, name); } SecurityClass* SCL_get_class(thread_db* tdbb, const TEXT* par_string) @@ -1292,7 +1223,7 @@ void SCL_release_all(SecurityClassList*& list) } -SecurityClass::flags_t SCL_get_object_mask(const int object_type) +SecurityClass::flags_t SCL_get_object_mask(ObjectType object_type) { /************************************** * @@ -1307,7 +1238,7 @@ SecurityClass::flags_t SCL_get_object_mask(const int object_type) thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); - const TEXT* object_name = get_object_name(object_type); + const TEXT* object_name = getSecurityClassName(object_type); if (!*object_name) return 0; @@ -1836,7 +1767,7 @@ static bool check_object(thread_db* tdbb, SLONG obj_type, const MetaName& obj_name, SecurityClass::flags_t mask, - SLONG type, + ObjectType type, const MetaName& name) { if (s_class) diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 191dae62ff..d6b9a8a4b0 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -389,24 +389,6 @@ private: void findGrantedRoles(thread_db* tdbb) const; }; -// These numbers are arbitrary and only used at run-time. Can be changed if necessary at any moment. -// We need to include here the new objects that accept ACLs. -const SLONG SCL_object_database = obj_database; -const SLONG SCL_object_table = obj_relations; -const SLONG SCL_object_package = obj_packages; -const SLONG SCL_object_procedure = obj_procedures; -const SLONG SCL_object_function = obj_functions; -const SLONG SCL_object_collation = obj_collations; -const SLONG SCL_object_exception = obj_exceptions; -const SLONG SCL_object_generator = obj_generators; -const SLONG SCL_object_charset = obj_charsets; -const SLONG SCL_object_domain = obj_domains; -const SLONG SCL_object_view = obj_views; -const SLONG SCL_object_role = obj_roles; -const SLONG SCL_object_filter = obj_filters; -// Please keep it with code more than other objects -// - relations and procedures should be sorted before columns. -const SLONG SCL_object_column = obj_type_MAX + 1; } //namespace Jrd diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index 9a9524df83..ab5fb5429d 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -35,9 +35,9 @@ struct dsc; void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, SLONG, const Jrd::MetaName&, - Jrd::SecurityClass::flags_t, SLONG type, bool recursive, const Jrd::MetaName&, + Jrd::SecurityClass::flags_t, ObjectType type, bool recursive, const Jrd::MetaName&, const Jrd::MetaName& = ""); -void SCL_check_create_access(Jrd::thread_db*, int type); +void SCL_check_create_access(Jrd::thread_db*, ObjectType type); void SCL_check_charset(Jrd::thread_db* tdbb, const Jrd::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_collation(Jrd::thread_db* tdbb, const Jrd::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_database(Jrd::thread_db* tdbb, Jrd::SecurityClass::flags_t mask); @@ -57,7 +57,7 @@ Jrd::SecurityClass::flags_t SCL_get_mask(Jrd::thread_db* tdbb, const TEXT*, cons void SCL_clear_classes(Jrd::thread_db*, const TEXT*); void SCL_release_all(Jrd::SecurityClassList*&); bool SCL_role_granted(Jrd::thread_db* tdbb, const Jrd::UserId& usr, const TEXT* sql_role); -Jrd::SecurityClass::flags_t SCL_get_object_mask(const int object_type); +Jrd::SecurityClass::flags_t SCL_get_object_mask(ObjectType object_type); ULONG SCL_get_number(const UCHAR*); USHORT SCL_convert_privilege(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, const Firebird::string& priv); diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 5b35691892..8f20769993 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -4076,12 +4076,12 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool if (fld) { - SCL_check_access(tdbb, s_class, 0, 0, SCL_select, SCL_object_column, + SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_column, false, fld->fld_name, blb_relation->rel_name); } else { - SCL_check_access(tdbb, s_class, 0, 0, SCL_select, SCL_object_table, + SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_relations, false, blb_relation->rel_name); } From b63e509a8a08542eb75587dfd7fd721c6d8d0550 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 10 Feb 2022 18:58:08 +0300 Subject: [PATCH 072/187] This should fix assertion when loading chacha plugin on windows --- src/common/utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 2104103d0d..aee24aae0b 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1143,7 +1143,7 @@ Firebird::PathName getPrefix(unsigned int prefType, const char* name) if (s.hasData() && name[0]) { - s += '/'; + s += PathUtils::dir_sep; } s += name; gds__prefix(tmp, s.c_str()); From 34b55060f307836959c781b209f465316559865d Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 11 Feb 2022 00:05:40 +0000 Subject: [PATCH 073/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 93ccfd914d..829ecc98cc 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:399 + FORMAL BUILD NUMBER:401 */ -#define PRODUCT_VER_STRING "5.0.0.399" -#define FILE_VER_STRING "WI-T5.0.0.399" -#define LICENSE_VER_STRING "WI-T5.0.0.399" -#define FILE_VER_NUMBER 5, 0, 0, 399 +#define PRODUCT_VER_STRING "5.0.0.401" +#define FILE_VER_STRING "WI-T5.0.0.401" +#define LICENSE_VER_STRING "WI-T5.0.0.401" +#define FILE_VER_NUMBER 5, 0, 0, 401 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "399" +#define FB_BUILD_NO "401" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 05e9e897ea..f1048924f1 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=399 +BuildNum=401 NowAt=`pwd` cd `dirname $0` From 7d065f52939ecfb3b4c595ef26a4a4f6c5bb73e3 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 11 Feb 2022 10:37:02 -0300 Subject: [PATCH 074/187] Remove bad asserts. --- src/intl/lc_ascii.cpp | 2 -- src/intl/lc_ksc.cpp | 2 -- src/intl/lc_narrow.cpp | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/intl/lc_ascii.cpp b/src/intl/lc_ascii.cpp index 137e6a8ef7..e70eadd900 100644 --- a/src/intl/lc_ascii.cpp +++ b/src/intl/lc_ascii.cpp @@ -523,8 +523,6 @@ USHORT famasc_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInChar, U { fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); - fb_assert(iInLen <= LANGASCII_MAX_KEY); -// fb_assert(iOutLen <= LANGASCII_MAX_KEY); fb_assert(iOutLen >= famasc_key_length(obj, iInLen)); // point inbuff at last character diff --git a/src/intl/lc_ksc.cpp b/src/intl/lc_ksc.cpp index 54ed5fc8d1..e24d5a1869 100644 --- a/src/intl/lc_ksc.cpp +++ b/src/intl/lc_ksc.cpp @@ -139,8 +139,6 @@ static USHORT LCKSC_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInC { fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); - fb_assert(iInLen <= LANGKSC_MAX_KEY); - fb_assert(iOutLen <= LANGKSC_MAX_KEY); fb_assert(iOutLen >= LCKSC_key_length(obj, iInLen)); const BYTE* inbuff = pInChar + iInLen - 1; diff --git a/src/intl/lc_narrow.cpp b/src/intl/lc_narrow.cpp index f4e4943e4d..f391f7fc5b 100644 --- a/src/intl/lc_narrow.cpp +++ b/src/intl/lc_narrow.cpp @@ -168,8 +168,6 @@ USHORT LC_NARROW_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInChar { fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); - // fb_assert (iInLen <= LANGFAM2_MAX_KEY); - fb_assert(iOutLen <= LANGFAM2_MAX_KEY); fb_assert(iOutLen >= LC_NARROW_key_length(obj, iInLen)); TextTypeImpl* impl = static_cast(obj->texttype_impl); From d39f248c778d7569c05f4e0f687528cdfa1539a1 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 12 Feb 2022 00:05:41 +0000 Subject: [PATCH 075/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 829ecc98cc..ee96aa6de1 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:401 + FORMAL BUILD NUMBER:402 */ -#define PRODUCT_VER_STRING "5.0.0.401" -#define FILE_VER_STRING "WI-T5.0.0.401" -#define LICENSE_VER_STRING "WI-T5.0.0.401" -#define FILE_VER_NUMBER 5, 0, 0, 401 +#define PRODUCT_VER_STRING "5.0.0.402" +#define FILE_VER_STRING "WI-T5.0.0.402" +#define LICENSE_VER_STRING "WI-T5.0.0.402" +#define FILE_VER_NUMBER 5, 0, 0, 402 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "401" +#define FB_BUILD_NO "402" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index f1048924f1..cbc392cecc 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=401 +BuildNum=402 NowAt=`pwd` cd `dirname $0` From 98f678d4ee480e94241dda2c41144a32cb8802aa Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 13 Feb 2022 10:51:30 -0300 Subject: [PATCH 076/187] Rename class jrd_req to Request. --- src/dsql/AggNodes.cpp | 68 ++--- src/dsql/AggNodes.h | 60 ++--- src/dsql/BoolNodes.cpp | 18 +- src/dsql/BoolNodes.h | 18 +- src/dsql/DsqlBatch.cpp | 2 +- src/dsql/DsqlRequests.h | 8 +- src/dsql/ExprNodes.cpp | 86 +++---- src/dsql/ExprNodes.h | 90 +++---- src/dsql/Nodes.h | 32 +-- src/dsql/StmtNodes.cpp | 356 +++++++++++++-------------- src/dsql/StmtNodes.h | 90 +++---- src/dsql/WinNodes.cpp | 48 ++-- src/dsql/WinNodes.h | 48 ++-- src/dsql/dsql.h | 2 +- src/jrd/Attachment.cpp | 6 +- src/jrd/Attachment.h | 6 +- src/jrd/ConfigTable.cpp | 4 +- src/jrd/ExtEngineManager.cpp | 38 +-- src/jrd/ExtEngineManager.h | 6 +- src/jrd/Monitoring.cpp | 10 +- src/jrd/Monitoring.h | 4 +- src/jrd/PreparedStatement.cpp | 4 +- src/jrd/ResultSet.cpp | 8 +- src/jrd/RuntimeStatistics.h | 4 +- src/jrd/Statement.cpp | 18 +- src/jrd/Statement.h | 8 +- src/jrd/SysFunction.cpp | 100 ++++---- src/jrd/blb.cpp | 22 +- src/jrd/blb.h | 2 +- src/jrd/btr.cpp | 6 +- src/jrd/cmp.cpp | 4 +- src/jrd/cmp_proto.h | 6 +- src/jrd/dfw.epp | 6 +- src/jrd/evl.cpp | 8 +- src/jrd/evl_proto.h | 2 +- src/jrd/exe.cpp | 72 +++--- src/jrd/exe_proto.h | 26 +- src/jrd/extds/ExtDS.cpp | 8 +- src/jrd/extds/ExtDS.h | 6 +- src/jrd/extds/InternalDS.cpp | 2 +- src/jrd/fun.epp | 2 +- src/jrd/inf.cpp | 12 +- src/jrd/inf_proto.h | 4 +- src/jrd/jrd.cpp | 32 +-- src/jrd/jrd.h | 10 +- src/jrd/jrd_proto.h | 12 +- src/jrd/met_proto.h | 2 +- src/jrd/par_proto.h | 2 +- src/jrd/recsrc/AggregatedStream.cpp | 22 +- src/jrd/recsrc/BitmapTableScan.cpp | 6 +- src/jrd/recsrc/BufferedStream.cpp | 12 +- src/jrd/recsrc/ConditionalStream.cpp | 12 +- src/jrd/recsrc/Cursor.cpp | 16 +- src/jrd/recsrc/Cursor.h | 2 +- src/jrd/recsrc/ExternalTableScan.cpp | 6 +- src/jrd/recsrc/FilteredStream.cpp | 12 +- src/jrd/recsrc/FirstRowsStream.cpp | 8 +- src/jrd/recsrc/FullOuterJoin.cpp | 8 +- src/jrd/recsrc/FullTableScan.cpp | 6 +- src/jrd/recsrc/HashJoin.cpp | 10 +- src/jrd/recsrc/IndexTableScan.cpp | 6 +- src/jrd/recsrc/LockedStream.cpp | 8 +- src/jrd/recsrc/MergeJoin.cpp | 14 +- src/jrd/recsrc/NestedLoopJoin.cpp | 8 +- src/jrd/recsrc/ProcedureScan.cpp | 12 +- src/jrd/recsrc/RecordSource.cpp | 8 +- src/jrd/recsrc/RecordSource.h | 74 +++--- src/jrd/recsrc/RecursiveStream.cpp | 10 +- src/jrd/recsrc/SingularStream.cpp | 10 +- src/jrd/recsrc/SkipRowsStream.cpp | 8 +- src/jrd/recsrc/SortedStream.cpp | 14 +- src/jrd/recsrc/Union.cpp | 12 +- src/jrd/recsrc/VirtualTableScan.cpp | 6 +- src/jrd/recsrc/WindowedStream.cpp | 34 +-- src/jrd/replication/Applier.cpp | 2 +- src/jrd/replication/Applier.h | 4 +- src/jrd/req.h | 14 +- src/jrd/tra.cpp | 26 +- src/jrd/tra.h | 4 +- src/jrd/tra_proto.h | 10 +- src/jrd/trace/TraceJrdHelpers.h | 20 +- src/jrd/trace/TraceObjects.h | 16 +- src/jrd/val.h | 4 +- src/jrd/vio.cpp | 20 +- 84 files changed, 926 insertions(+), 926 deletions(-) diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index af5c0e9b93..0e9a9d3dd8 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -356,7 +356,7 @@ AggNode* AggNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void AggNode::aggInit(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); impure->vlux_count = 0; @@ -378,7 +378,7 @@ void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -bool AggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool AggNode::aggPass(thread_db* tdbb, Request* request) const { dsc* desc = NULL; @@ -435,7 +435,7 @@ bool AggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void AggNode::aggFinish(thread_db* /*tdbb*/, jrd_req* request) const +void AggNode::aggFinish(thread_db* /*tdbb*/, Request* request) const { if (asb) { @@ -445,7 +445,7 @@ void AggNode::aggFinish(thread_db* /*tdbb*/, jrd_req* request) const } } -dsc* AggNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* AggNode::execute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -685,7 +685,7 @@ string AvgAggNode::internalPrint(NodePrinter& printer) const return "AvgAggNode"; } -void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void AvgAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -704,7 +704,7 @@ void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -void AvgAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void AvgAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); if (impure->vlux_count++ == 0) // first call to aggPass() @@ -720,7 +720,7 @@ void AvgAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const ArithmeticNode::add2(tdbb, desc, impure, this, blr_add); } -dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* AvgAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -848,7 +848,7 @@ string ListAggNode::internalPrint(NodePrinter& printer) const return "ListAggNode"; } -void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void ListAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -859,7 +859,7 @@ void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->vlu_desc.dsc_dtype = 0; } -void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void ListAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -897,7 +897,7 @@ void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const impure->vlu_blob->BLB_put_data(tdbb, temp, len); } -dsc* ListAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* ListAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1000,7 +1000,7 @@ string CountAggNode::internalPrint(NodePrinter& printer) const } //// TODO: Improve count(*) in local tables. -void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void CountAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1008,7 +1008,7 @@ void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0); } -void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const +void CountAggNode::aggPass(thread_db* /*tdbb*/, Request* request, dsc* /*desc*/) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1018,7 +1018,7 @@ void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) ++impure->vlu_misc.vlu_int64; } -dsc* CountAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* CountAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1253,7 +1253,7 @@ string SumAggNode::internalPrint(NodePrinter& printer) const return "SumAggNode"; } -void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void SumAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1269,7 +1269,7 @@ void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -void SumAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void SumAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; @@ -1280,7 +1280,7 @@ void SumAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const ArithmeticNode::add2(tdbb, desc, impure, this, blr_add); } -dsc* SumAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* SumAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1345,7 +1345,7 @@ string MaxMinAggNode::internalPrint(NodePrinter& printer) const return "MaxMinAggNode"; } -void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void MaxMinAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1353,7 +1353,7 @@ void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->vlu_desc.dsc_dtype = 0; } -void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void MaxMinAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; @@ -1370,7 +1370,7 @@ void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const EVL_make_value(tdbb, desc, impure); } -dsc* MaxMinAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* MaxMinAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1472,7 +1472,7 @@ string StdDevAggNode::internalPrint(NodePrinter& printer) const return "StdDevAggNode"; } -void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void StdDevAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1491,7 +1491,7 @@ void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void StdDevAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; @@ -1514,7 +1514,7 @@ void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const } } -dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* StdDevAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); StdDevImpure* impure2 = request->getImpure(impure2Offset); @@ -1681,7 +1681,7 @@ string CorrAggNode::internalPrint(NodePrinter& printer) const return "CorrAggNode"; } -void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void CorrAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1700,7 +1700,7 @@ void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool CorrAggNode::aggPass(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1744,12 +1744,12 @@ bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void CorrAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void CorrAggNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { fb_assert(false); } -dsc* CorrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* CorrAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); CorrImpure* impure2 = request->getImpure(impure2Offset); @@ -1957,7 +1957,7 @@ string RegrAggNode::internalPrint(NodePrinter& printer) const return "RegrAggNode"; } -void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RegrAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1976,7 +1976,7 @@ void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool RegrAggNode::aggPass(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -2021,12 +2021,12 @@ bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void RegrAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void RegrAggNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { fb_assert(false); } -dsc* RegrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* RegrAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -2226,7 +2226,7 @@ string RegrCountAggNode::internalPrint(NodePrinter& printer) const return "RegrCountAggNode"; } -void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RegrCountAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -2234,7 +2234,7 @@ void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0); } -bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool RegrCountAggNode::aggPass(thread_db* tdbb, Request* request) const { EVL_expr(tdbb, request, arg); if (request->req_flags & req_null) @@ -2250,12 +2250,12 @@ bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void RegrCountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void RegrCountAggNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { fb_assert(false); } -dsc* RegrCountAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* RegrCountAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); diff --git a/src/dsql/AggNodes.h b/src/dsql/AggNodes.h index ad1dae36fa..0c5f105a21 100644 --- a/src/dsql/AggNodes.h +++ b/src/dsql/AggNodes.h @@ -53,9 +53,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -91,9 +91,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -120,9 +120,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -150,9 +150,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -181,9 +181,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -230,9 +230,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -288,10 +288,10 @@ 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) 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; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -353,10 +353,10 @@ 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) 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; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -393,10 +393,10 @@ 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) 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; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 369583a179..35cb750ae3 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -170,7 +170,7 @@ BoolExprNode* BinaryBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } -bool BinaryBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool BinaryBoolNode::execute(thread_db* tdbb, Request* request) const { switch (blrOp) { @@ -185,7 +185,7 @@ bool BinaryBoolNode::execute(thread_db* tdbb, jrd_req* request) const return false; } -bool BinaryBoolNode::executeAnd(thread_db* tdbb, jrd_req* request) const +bool BinaryBoolNode::executeAnd(thread_db* tdbb, Request* request) const { // If either operand is false, then the result is false; // If both are true, the result is true; @@ -231,7 +231,7 @@ bool BinaryBoolNode::executeAnd(thread_db* tdbb, jrd_req* request) const return false; } -bool BinaryBoolNode::executeOr(thread_db* tdbb, jrd_req* request) const +bool BinaryBoolNode::executeOr(thread_db* tdbb, Request* request) const { // If either operand is true, then the result is true; // If both are false, the result is false; @@ -643,7 +643,7 @@ void ComparativeBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) } } -bool ComparativeBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool ComparativeBoolNode::execute(thread_db* tdbb, Request* request) const { dsc* desc[2] = {NULL, NULL}; bool computed_invariant = false; @@ -817,7 +817,7 @@ bool ComparativeBoolNode::execute(thread_db* tdbb, jrd_req* request) const } // Perform one of the complex string functions CONTAINING, MATCHES, or STARTS WITH. -bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, jrd_req* request, dsc* desc1, +bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request, dsc* desc1, dsc* desc2, bool computedInvariant) const { SET_TDBB(tdbb); @@ -1023,7 +1023,7 @@ bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, jrd_req* request, dsc* } // Execute SLEUTH operator. -bool ComparativeBoolNode::sleuth(thread_db* tdbb, jrd_req* request, const dsc* desc1, +bool ComparativeBoolNode::sleuth(thread_db* tdbb, Request* request, const dsc* desc1, const dsc* desc2) const { SET_TDBB(tdbb); @@ -1223,7 +1223,7 @@ void MissingBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) arg->getDesc(tdbb, csb, &descriptor_a); } -bool MissingBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool MissingBoolNode::execute(thread_db* tdbb, Request* request) const { EVL_expr(tdbb, request, arg); @@ -1298,7 +1298,7 @@ BoolExprNode* NotBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) return BoolExprNode::pass1(tdbb, csb); } -bool NotBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool NotBoolNode::execute(thread_db* tdbb, Request* request) const { bool value = arg->execute(tdbb, request); @@ -1612,7 +1612,7 @@ void RseBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(rsb, rse->rse_invariants); } -bool RseBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool RseBoolNode::execute(thread_db* tdbb, Request* request) const { USHORT* invariant_flags; impure_value* impure; diff --git a/src/dsql/BoolNodes.h b/src/dsql/BoolNodes.h index cbdd6cfc43..10ad9d72a2 100644 --- a/src/dsql/BoolNodes.h +++ b/src/dsql/BoolNodes.h @@ -54,11 +54,11 @@ public: virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: - virtual bool executeAnd(thread_db* tdbb, jrd_req* request) const; - virtual bool executeOr(thread_db* tdbb, jrd_req* request) const; + virtual bool executeAnd(thread_db* tdbb, Request* request) const; + virtual bool executeOr(thread_db* tdbb, Request* request) const; public: UCHAR blrOp; @@ -106,12 +106,12 @@ public: virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean2(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: - bool stringBoolean(thread_db* tdbb, jrd_req* request, dsc* desc1, dsc* desc2, + bool stringBoolean(thread_db* tdbb, Request* request, dsc* desc1, dsc* desc2, bool computedInvariant) const; - bool sleuth(thread_db* tdbb, jrd_req* request, const dsc* desc1, const dsc* desc2) const; + bool sleuth(thread_db* tdbb, Request* request, const dsc* desc1, const dsc* desc2) const; BoolExprNode* createRseNode(DsqlCompilerScratch* dsqlScratch, UCHAR rseBlrOp); @@ -151,7 +151,7 @@ public: virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean2(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; public: bool dsqlUnknown; @@ -183,7 +183,7 @@ public: virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: BoolExprNode* process(DsqlCompilerScratch* dsqlScratch, bool invert); @@ -235,7 +235,7 @@ public: virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean2(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: BoolExprNode* convertNeqAllToNotAny(thread_db* tdbb, CompilerScratch* csb); diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp index f5b249cc1b..10fafc3655 100644 --- a/src/dsql/DsqlBatch.cpp +++ b/src/dsql/DsqlBatch.cpp @@ -652,7 +652,7 @@ private: // execute request m_dsqlRequest->req_transaction = transaction; - jrd_req* req = m_dsqlRequest->getRequest(); + Request* req = m_dsqlRequest->getRequest(); fb_assert(req); // prepare completion interface diff --git a/src/dsql/DsqlRequests.h b/src/dsql/DsqlRequests.h index 7b41152c11..451936a900 100644 --- a/src/dsql/DsqlRequests.h +++ b/src/dsql/DsqlRequests.h @@ -41,7 +41,7 @@ class DsqlCompilerScratch; class DsqlCursor; class DsqlDmlStatement; class dsql_par; -class jrd_req; +class Request; class jrd_tra; class Statement; class SessionManagementNode; @@ -70,7 +70,7 @@ public: return nullptr; } - virtual jrd_req* getRequest() const + virtual Request* getRequest() const { return nullptr; } @@ -160,7 +160,7 @@ public: Statement* getStatement() const override; - jrd_req* getRequest() const override + Request* getRequest() const override { return request; } @@ -209,7 +209,7 @@ public: private: Firebird::RefPtr delayedFormat; - jrd_req* request = nullptr; + Request* request = nullptr; bool needDelayedFormat = false; bool firstRowFetched = false; }; diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index f103370450..cc030621e6 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -1838,7 +1838,7 @@ ValueExprNode* ArithmeticNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ArithmeticNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); @@ -3214,7 +3214,7 @@ ValueExprNode* AtNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* AtNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* AtNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -3332,7 +3332,7 @@ ValueExprNode* BoolAsValueNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* BoolAsValueNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* BoolAsValueNode::execute(thread_db* tdbb, Request* request) const { UCHAR booleanVal = (UCHAR) boolean->execute(tdbb, request); @@ -3552,7 +3552,7 @@ ValueExprNode* CastNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Cast from one datatype to another. -dsc* CastNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CastNode::execute(thread_db* tdbb, Request* request) const { dsc* value = EVL_expr(tdbb, request, source); @@ -3732,7 +3732,7 @@ ValueExprNode* CoalesceNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* CoalesceNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CoalesceNode::execute(thread_db* tdbb, Request* request) const { for (auto& item : args->items) { @@ -3931,7 +3931,7 @@ ValueExprNode* ConcatenateNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ConcatenateNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ConcatenateNode::execute(thread_db* tdbb, Request* request) const { const dsc* value1 = EVL_expr(tdbb, request, arg1); const ULONG flags = request->req_flags; @@ -4164,7 +4164,7 @@ ValueExprNode* CurrentDateNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* CurrentDateNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentDateNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4267,7 +4267,7 @@ ValueExprNode* CurrentTimeNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/) return this; } -dsc* CurrentTimeNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentTimeNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4372,7 +4372,7 @@ ValueExprNode* CurrentTimeStampNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch return this; } -dsc* CurrentTimeStampNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentTimeStampNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4450,7 +4450,7 @@ ValueExprNode* CurrentRoleNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // CVC: Current role will get a validated role; IE one that exists. -dsc* CurrentRoleNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentRoleNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4536,7 +4536,7 @@ ValueExprNode* CurrentUserNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* CurrentUserNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentUserNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4785,7 +4785,7 @@ ValueExprNode* DecodeNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* DecodeNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* DecodeNode::execute(thread_db* tdbb, Request* request) const { dsc* testDesc = EVL_expr(tdbb, request, test); @@ -5123,7 +5123,7 @@ ValueExprNode* DerivedExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* DerivedExprNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* DerivedExprNode::execute(thread_db* tdbb, Request* request) const { if (cursorNumber.specified) request->req_cursors[cursorNumber.value]->checkState(request); @@ -5208,7 +5208,7 @@ ValueExprNode* DomainValidationNode::pass2(thread_db* tdbb, CompilerScratch* csb return this; } -dsc* DomainValidationNode::execute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* DomainValidationNode::execute(thread_db* /*tdbb*/, Request* request) const { if (request->req_domain_validation == NULL || (request->req_domain_validation->dsc_flags & DSC_null)) @@ -5419,7 +5419,7 @@ ValueExprNode* ExtractNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Handles EXTRACT(part FROM date/time/timestamp) -dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ExtractNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -6796,7 +6796,7 @@ ValueExprNode* FieldNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* FieldNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* FieldNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); @@ -7056,7 +7056,7 @@ ValueExprNode* GenIdNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* GenIdNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* GenIdNode::execute(thread_db* tdbb, Request* request) const { request->req_flags &= ~req_null; @@ -7254,7 +7254,7 @@ ValueExprNode* InternalInfoNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Return a given element of the internal engine data. -dsc* InternalInfoNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* InternalInfoNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -7954,7 +7954,7 @@ ValueExprNode* LiteralNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* LiteralNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const +dsc* LiteralNode::execute(thread_db* /*tdbb*/, Request* /*request*/) const { return const_cast(&litDesc); } @@ -8131,7 +8131,7 @@ ValueExprNode* LocalTimeNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/) return this; } -dsc* LocalTimeNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* LocalTimeNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -8223,7 +8223,7 @@ ValueExprNode* LocalTimeStampNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/ return this; } -dsc* LocalTimeStampNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* LocalTimeStampNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -8834,7 +8834,7 @@ ValueExprNode* NegateNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* NegateNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* NegateNode::execute(thread_db* tdbb, Request* request) const { request->req_flags &= ~req_null; @@ -8962,7 +8962,7 @@ ValueExprNode* NullNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) const return &INSTANCE; } -dsc* NullNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const +dsc* NullNode::execute(thread_db* /*tdbb*/, Request* /*request*/) const { return NULL; } @@ -9363,7 +9363,7 @@ ValueExprNode* OverNode::copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const return NULL; } -dsc* OverNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const +dsc* OverNode::execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -9645,7 +9645,7 @@ bool ParameterNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* return o && outerDecl == o->outerDecl && dsqlParameter->par_index == o->dsqlParameter->par_index; } -jrd_req* ParameterNode::getParamRequest(jrd_req* request) const +Request* ParameterNode::getParamRequest(Request* request) const { auto paramRequest = request; @@ -9715,7 +9715,7 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const { dsc* retDesc; impure_value* impureForOuter; @@ -9733,7 +9733,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const const auto paramRequest = getParamRequest(request); - AutoSetRestore2 autoSetRequest( + AutoSetRestore2 autoSetRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); const dsc* desc; @@ -10278,7 +10278,7 @@ ValueExprNode* RecordKeyNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); const record_param* rpb = &request->req_rpb[recStream]; @@ -10466,7 +10466,7 @@ ValueExprNode* ScalarNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Evaluate a scalar item from an array. -dsc* ScalarNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ScalarNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); const dsc* desc = EVL_expr(tdbb, request, field); @@ -10553,7 +10553,7 @@ ValueExprNode* StmtExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) return NULL; } -dsc* StmtExprNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* StmtExprNode::execute(thread_db* tdbb, Request* request) const { fb_assert(false); return NULL; @@ -10680,7 +10680,7 @@ ValueExprNode* StrCaseNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Low/up case a string. -dsc* StrCaseNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* StrCaseNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -10898,7 +10898,7 @@ ValueExprNode* StrLenNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Handles BIT_LENGTH(s), OCTET_LENGTH(s) and CHAR[ACTER]_LENGTH(s) -dsc* StrLenNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* StrLenNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -11351,7 +11351,7 @@ ValueExprNode* SubQueryNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Evaluate a subquery expression. -dsc* SubQueryNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SubQueryNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -11674,7 +11674,7 @@ ValueExprNode* SubstringNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* SubstringNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SubstringNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); @@ -11980,7 +11980,7 @@ ValueExprNode* SubstringSimilarNode::pass2(thread_db* tdbb, CompilerScratch* csb return this; } -dsc* SubstringSimilarNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SubstringSimilarNode::execute(thread_db* tdbb, Request* request) const { // Run all expression arguments. @@ -12277,7 +12277,7 @@ ValueExprNode* SysFuncCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* SysFuncCallNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SysFuncCallNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); return function->evlFunc(tdbb, function, args->items, impure); @@ -12546,7 +12546,7 @@ ValueExprNode* TrimNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Perform trim function = TRIM([where what FROM] string). -dsc* TrimNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* TrimNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -12974,7 +12974,7 @@ ValueExprNode* UdfCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const { UCHAR* impure = request->getImpure(impureOffset); Impure* impureArea = request->getImpure(impureOffset); @@ -13098,7 +13098,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const const SavNumber savNumber = transaction->tra_save_point ? transaction->tra_save_point->getNumber() : 0; - jrd_req* funcRequest = function->getStatement()->findRequest(tdbb); + Request* funcRequest = function->getStatement()->findRequest(tdbb); // trace function execution start TraceFuncExecute trace(tdbb, funcRequest, request, inMsg, inMsgLength); @@ -13490,7 +13490,7 @@ ValueExprNode* ValueIfNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ValueIfNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ValueIfNode::execute(thread_db* tdbb, Request* request) const { return EVL_expr(tdbb, request, (condition->execute(tdbb, request) ? trueValue : falseValue)); } @@ -13652,7 +13652,7 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o return true; } -jrd_req* VariableNode::getVarRequest(jrd_req* request) const +Request* VariableNode::getVarRequest(Request* request) const { auto varRequest = request; @@ -13709,7 +13709,7 @@ ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* VariableNode::execute(thread_db* tdbb, Request* request) const { const auto varRequest = getVarRequest(request); const auto varImpure = varRequest->getImpure(varDecl->impureOffset); @@ -13736,7 +13736,7 @@ dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const { if (varInfo) { - AutoSetRestore2 autoSetRequest( + AutoSetRestore2 autoSetRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 88e2f0f319..a1d53e82aa 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -91,7 +91,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; // add and add2 are used in somewhat obscure way in aggregation. static dsc* add(thread_db* tdbb, const dsc* desc, impure_value* value, const ValueExprNode* node, @@ -160,7 +160,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -196,7 +196,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst dateTimeArg; @@ -230,7 +230,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst boolean; @@ -269,7 +269,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: MetaName dsqlAlias; @@ -310,7 +310,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; virtual bool possiblyUnknown() const { @@ -369,7 +369,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -409,7 +409,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg1; @@ -435,7 +435,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; }; @@ -459,7 +459,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; @@ -486,7 +486,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; @@ -512,7 +512,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; }; @@ -535,7 +535,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; }; @@ -575,7 +575,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: Firebird::string label; @@ -673,7 +673,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg; @@ -704,7 +704,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: dsc domDesc; @@ -737,7 +737,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: UCHAR blrSubOp; @@ -802,7 +802,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; private: static dsql_fld* resolveContext(DsqlCompilerScratch* dsqlScratch, @@ -853,7 +853,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: GeneratorItem generator; @@ -898,7 +898,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg; @@ -927,7 +927,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; SLONG getSlong() const { @@ -992,7 +992,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1041,7 +1041,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1117,7 +1117,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1151,7 +1151,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; @@ -1178,7 +1178,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; @@ -1209,7 +1209,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg; @@ -1247,7 +1247,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; private: static Firebird::GlobalPtr INSTANCE; @@ -1553,7 +1553,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst aggExpr; @@ -1595,12 +1595,12 @@ public: virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; - jrd_req* getParamRequest(jrd_req* request) const; + Request* getParamRequest(Request* request) const; virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: dsql_msg* dsqlMessage = nullptr; @@ -1665,7 +1665,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; const char* getAlias(bool rdb) const { @@ -1742,7 +1742,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst field; @@ -1801,7 +1801,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst stmt; @@ -1835,7 +1835,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: const UCHAR blrOp; @@ -1869,7 +1869,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: UCHAR blrSubOp; @@ -1923,7 +1923,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst dsqlRse; @@ -1964,7 +1964,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; static dsc* perform(thread_db* tdbb, impure_value* impure, const dsc* valueDsc, const dsc* startDsc, const dsc* lengthDsc); @@ -2005,7 +2005,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst expr; @@ -2039,7 +2039,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: MetaName name; @@ -2078,7 +2078,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: UCHAR where; @@ -2125,7 +2125,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: QualifiedName name; @@ -2172,7 +2172,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst condition; @@ -2200,13 +2200,13 @@ public: dsqlDesc = desc; } - jrd_req* getVarRequest(jrd_req* request) const; + Request* getVarRequest(Request* request) const; virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: MetaName dsqlName; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 65b20f4e15..ee21b08f64 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -746,7 +746,7 @@ public: } virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const = 0; - virtual bool execute(thread_db* tdbb, jrd_req* request) const = 0; + virtual bool execute(thread_db* tdbb, Request* request) const = 0; }; class ValueExprNode : public ExprNode @@ -828,7 +828,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) = 0; virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const = 0; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const = 0; + virtual dsc* execute(thread_db* tdbb, Request* request) const = 0; public: SCHAR nodScale; @@ -885,7 +885,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1043,19 +1043,19 @@ public: return false; } - virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/) const + virtual dsc* winPass(thread_db* /*tdbb*/, Request* /*request*/, SlidingWindow* /*window*/) const { return NULL; } - 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 void aggInit(thread_db* tdbb, Request* request) const = 0; // pure, but defined + virtual void aggFinish(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual dsc* execute(thread_db* tdbb, Request* 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; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const = 0; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const = 0; virtual AggNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); @@ -1087,11 +1087,11 @@ 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 void aggPass(thread_db* tdbb, Request* request, dsc* desc) const { } - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const { return NULL; } @@ -1441,7 +1441,7 @@ public: struct ExeState { - ExeState(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) + ExeState(thread_db* tdbb, Request* request, jrd_tra* transaction) : savedTdbb(tdbb), oldPool(tdbb->getDefaultPool()), oldRequest(tdbb->getRequest()), @@ -1467,7 +1467,7 @@ public: thread_db* savedTdbb; MemoryPool* oldPool; // Save the old pool to restore on exit. - jrd_req* oldRequest; // Save the old request to restore on exit. + Request* oldRequest; // Save the old request to restore on exit. jrd_tra* oldTransaction; // Save the old transcation to restore on exit. const StmtNode* topNode; const StmtNode* prevNode; @@ -1529,7 +1529,7 @@ public: return NULL; } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const = 0; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const = 0; public: NestConst parentStmt; @@ -1567,7 +1567,7 @@ public: return NULL; } - const StmtNode* execute(thread_db* /*tdbb*/, jrd_req* /*request*/, ExeState* /*exeState*/) const + const StmtNode* execute(thread_db* /*tdbb*/, Request* /*request*/, ExeState* /*exeState*/) const { fb_assert(false); return NULL; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index ee65d9b15f..f8b3b19573 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -111,7 +111,7 @@ static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs, StmtNode::WhichTrigger whichTrig, record_param* rpb, record_param* rec, TriggerAction op); static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, StreamType stream, CompoundStmtNode* compoundNode, const Nullable* insertOverride); -static void restartRequest(const jrd_req* request, jrd_tra* transaction); +static void restartRequest(const Request* request, jrd_tra* transaction); static void validateExpressions(thread_db* tdbb, const Array& validations); } // namespace Jrd @@ -382,12 +382,12 @@ AssignmentNode* AssignmentNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* AssignmentNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* AssignmentNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { EXE_assignment(tdbb, this); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -484,14 +484,14 @@ BlockNode* BlockNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* BlockNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { jrd_tra* transaction = request->req_transaction; SavNumber savNumber; switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (!(transaction->tra_flags & TRA_system)) { const Savepoint* const savepoint = transaction->startSavepoint(); @@ -500,7 +500,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } return action; - case jrd_req::req_unwind: + case Request::req_unwind: { if (request->req_flags & (req_leave | req_continue_loop)) { @@ -574,7 +574,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* if (testAndFixupError(tdbb, request, handlerNode->conditions)) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; exeState->errorPending = false; // On entering looper exeState->oldRequest etc. are saved. @@ -649,7 +649,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* return temp; } - case jrd_req::req_return: + case Request::req_return: if (!(transaction->tra_flags & TRA_system)) { savNumber = *request->getImpure(impureOffset); @@ -673,7 +673,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Test for match of current state with list of error conditions. Fix type and code of the exception. -bool BlockNode::testAndFixupError(thread_db* tdbb, jrd_req* request, const ExceptionArray& conditions) +bool BlockNode::testAndFixupError(thread_db* tdbb, Request* request, const ExceptionArray& conditions) { if (tdbb->tdbb_flags & TDBB_sys_error) return false; @@ -841,13 +841,13 @@ CompoundStmtNode* CompoundStmtNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { const NestConst* end = statements.end(); if (onlyAssignments) { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { for (const NestConst* i = statements.begin(); i != end; ++i) { @@ -862,7 +862,7 @@ const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, jrd_req* request, Exe EXE_assignment(tdbb, static_cast(stmt)); } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -872,18 +872,18 @@ const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, jrd_req* request, Exe switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: impure->sta_state = 0; // fall into - case jrd_req::req_return: - case jrd_req::req_sync: + case Request::req_return: + case Request::req_sync: if (impure->sta_state < statements.getCount()) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statements[impure->sta_state++]; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; // fall into default: @@ -938,11 +938,11 @@ void ContinueLeaveNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(labelNumber); } -const StmtNode* ContinueLeaveNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ContinueLeaveNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { - request->req_operation = jrd_req::req_unwind; + request->req_operation = Request::req_unwind; request->req_label = labelNumber; request->req_flags |= (blrOp == blr_continue_loop ? req_continue_loop : req_leave); } @@ -1117,7 +1117,7 @@ CursorStmtNode* CursorStmtNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* CursorStmtNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { fb_assert(cursorNumber < request->req_cursors.getCount()); const Cursor* const cursor = request->req_cursors[cursorNumber]; @@ -1126,18 +1126,18 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt switch (cursorOp) { case blr_cursor_open: - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { cursor->open(tdbb); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; case blr_cursor_close: - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { cursor->close(tdbb); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -1145,7 +1145,7 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt case blr_cursor_fetch_scroll: switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (cursor->isUpdateCounters()) request->req_records_affected.clear(); @@ -1187,11 +1187,11 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt if (fetched) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return intoStmt; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; default: return parentStmt; @@ -1329,9 +1329,9 @@ DeclareCursorNode* DeclareCursorNode::pass2(thread_db* tdbb, CompilerScratch* cs return this; } -const StmtNode* DeclareCursorNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareCursorNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { // Set up the cursors array... if (cursorNumber >= request->req_cursors.getCount()) @@ -1339,7 +1339,7 @@ const StmtNode* DeclareCursorNode::execute(thread_db* /*tdbb*/, jrd_req* request // And store cursor there. request->req_cursors[cursorNumber] = cursor; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -1439,15 +1439,15 @@ DeclareLocalTableNode* DeclareLocalTableNode::pass2(thread_db* /*tdbb*/, Compile return this; } -const StmtNode* DeclareLocalTableNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareLocalTableNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; return parentStmt; } -DeclareLocalTableNode::Impure* DeclareLocalTableNode::getImpure(thread_db* tdbb, jrd_req* request, bool createWhenDead) const +DeclareLocalTableNode::Impure* DeclareLocalTableNode::getImpure(thread_db* tdbb, Request* request, bool createWhenDead) const { const auto impure = request->getImpure(impureOffset); @@ -1780,12 +1780,12 @@ DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* /*tdbb*/, CompilerScrat return this; } -const StmtNode* DeclareSubFuncNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareSubFuncNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { // Nothing to execute. This is the declaration node. - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; return parentStmt; } @@ -2121,12 +2121,12 @@ DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* /*tdbb*/, CompilerScrat return this; } -const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { // Nothing to execute. This is the declaration node. - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; return parentStmt; } @@ -2216,9 +2216,9 @@ DeclareVariableNode* DeclareVariableNode::pass2(thread_db* /*tdbb*/, CompilerScr return this; } -const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { impure_value* variable = request->getImpure(impureOffset); variable->vlu_desc = varDesc; @@ -2238,7 +2238,7 @@ const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, jrd_req* request, else variable->vlu_desc.dsc_address = (UCHAR*) &variable->vlu_misc; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -2569,13 +2569,13 @@ EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* EraseNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { const StmtNode* retNode; - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) retNode = parentStmt; - else if (request->req_operation == jrd_req::req_return && subStatement) + else if (request->req_operation == Request::req_return && subStatement) { if (!exeState->topNode) { @@ -2599,7 +2599,7 @@ const StmtNode* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState->whichEraseTrig = ALL_TRIGS; } else - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; } else { @@ -2614,7 +2614,7 @@ const StmtNode* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Perform erase operation. -const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const { jrd_tra* transaction = request->req_transaction; record_param* rpb = &request->req_rpb[stream]; @@ -2622,7 +2622,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: { if (!(marks & MARK_AVOID_COUNTERS)) request->req_records_affected.bumpModified(false); @@ -2640,14 +2640,14 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger return statement; } - case jrd_req::req_return: + case Request::req_return: break; default: return parentStmt; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; RLCK_reserve_relation(tdbb, transaction, relation, true); if (rpb->rpb_runtime_flags & RPB_just_deleted) @@ -2884,7 +2884,7 @@ ErrorHandlerNode* ErrorHandlerNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* exeState) const +const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* exeState) const { if ((request->req_flags & req_error_handler) && !exeState->errorPending) { @@ -2897,7 +2897,7 @@ const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, const StmtNode* retNode = parentStmt; retNode = retNode->parentStmt; - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) retNode = retNode->parentStmt; request->req_last_xcp.clear(); @@ -3205,12 +3205,12 @@ ExecProcedureNode* ExecProcedureNode::pass2(thread_db* tdbb, CompilerScratch* cs return this; } -const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { executeProcedure(tdbb, request); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -3218,7 +3218,7 @@ const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, jrd_req* request, Ex // Execute a stored procedure. Begin by assigning the input parameters. // End by assigning the output parameters. -void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) const +void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) const { if (!procedure->isImplemented()) { @@ -3275,7 +3275,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons const SavNumber savNumber = transaction->tra_save_point ? transaction->tra_save_point->getNumber() : 0; - jrd_req* procRequest = procedure->getStatement()->findRequest(tdbb); + Request* procRequest = procedure->getStatement()->findRequest(tdbb); // trace procedure execution start TraceProcExecute trace(tdbb, procRequest, request, inputTargets); @@ -3754,12 +3754,12 @@ ExecStatementNode* ExecStatementNode::pass2(thread_db* tdbb, CompilerScratch* cs return this; } -const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ExecStatementNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { EDS::Statement** stmtPtr = request->getImpure(impureOffset); EDS::Statement* stmt = *stmtPtr; - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { fb_assert(!*stmtPtr); @@ -3798,10 +3798,10 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex else stmt->execute(tdbb, tran, inpNames, inputs, excessInputs, outputs); - request->req_operation = jrd_req::req_return; - } // jrd_req::req_evaluate + request->req_operation = Request::req_return; + } // Request::req_evaluate - if (request->req_operation == jrd_req::req_return || request->req_operation == jrd_req::req_sync) + if (request->req_operation == Request::req_return || request->req_operation == Request::req_sync) { fb_assert(stmt); @@ -3809,15 +3809,15 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex { if (stmt->fetch(tdbb, outputs)) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return innerStmt; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } } - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) { const LabelNode* label = nodeAs(parentStmt.getObject()); @@ -3825,7 +3825,7 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; return this; } } @@ -3836,7 +3836,7 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex return parentStmt; } -void ExecStatementNode::getString(thread_db* tdbb, jrd_req* request, const ValueExprNode* node, +void ExecStatementNode::getString(thread_db* tdbb, Request* request, const ValueExprNode* node, string& str, bool useAttCS) const { MoveBuffer buffer; @@ -3925,23 +3925,23 @@ IfNode* IfNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* IfNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* IfNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (condition->execute(tdbb, request)) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return trueAction; } if (falseAction) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return falseAction; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -4010,14 +4010,14 @@ InAutonomousTransactionNode* InAutonomousTransactionNode::pass2(thread_db* tdbb, return this; } -const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); Impure* const impure = request->getImpure(impureOffset); - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { // Force unconditional reschedule. It prevents new transactions being // started after an attachment or a database shutdown has been initiated. @@ -4071,7 +4071,7 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r switch (request->req_operation) { - case jrd_req::req_return: + case Request::req_return: if (!(attachment->att_flags & ATT_no_db_triggers)) { // run ON TRANSACTION COMMIT triggers @@ -4086,13 +4086,13 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r } { // scope - AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } // end scope break; - case jrd_req::req_unwind: + case Request::req_unwind: if (request->req_flags & (req_leave | req_continue_loop)) { try @@ -4110,7 +4110,7 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r transaction->releaseSavepoint(tdbb); } - AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } @@ -4140,7 +4140,7 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r try { - AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_rollback(tdbb, transaction, false, false); @@ -4230,9 +4230,9 @@ InitVariableNode* InitVariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* InitVariableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (varInfo) { @@ -4255,7 +4255,7 @@ const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, Exe } } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -4684,9 +4684,9 @@ ExceptionNode* ExceptionNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ExceptionNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ExceptionNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (exception) { @@ -4703,7 +4703,7 @@ const StmtNode* ExceptionNode::execute(thread_db* tdbb, jrd_req* request, ExeSta { // PsqlException is undefined and there weren't any exceptions before, // so just do nothing. - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } } @@ -4715,7 +4715,7 @@ void ExceptionNode::setError(thread_db* tdbb) const { SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (!exception) { @@ -5072,7 +5072,7 @@ StmtNode* ForNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ForNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { jrd_tra* transaction = request->req_transaction; ImpureMerge* merge = request->getImpure(impureOffset); @@ -5080,7 +5080,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: // initialize impure values impure->savepoint = 0; impure->writeLockMode = false; @@ -5102,13 +5102,13 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* // fall into - case jrd_req::req_return: + case Request::req_return: if (stall) return stall; // fall into - case jrd_req::req_sync: + case Request::req_sync: { if (hasLineColumn) { @@ -5119,7 +5119,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* const bool fetched = cursor->fetchNext(tdbb); if (withLock) { - const jrd_req* top_request = request->req_snapshot.m_owner; + const Request* top_request = request->req_snapshot.m_owner; if ((top_request) && (top_request->req_flags & req_update_conflict)) impure->writeLockMode = true; } @@ -5129,11 +5129,11 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* if (impure->writeLockMode && withLock) { // Skip statement execution and fetch (and try to lock) next record. - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; return this; } - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement; } } @@ -5141,7 +5141,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* if (impure->writeLockMode) restartRequest(request, transaction); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; if (impure->savepoint) { @@ -5157,7 +5157,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* default: { - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) { if (request->req_flags & (req_leave | req_continue_loop)) { @@ -5169,7 +5169,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; return this; } @@ -5204,13 +5204,13 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* return NULL; } -bool ForNode::isWriteLockMode(jrd_req* request) const +bool ForNode::isWriteLockMode(Request* request) const { const Impure* impure = request->getImpure(impureOffset); return impure->writeLockMode; } -void ForNode::setWriteLockMode(jrd_req* request) const +void ForNode::setWriteLockMode(Request* request) const { Impure* impure = request->getImpure(impureOffset); fb_assert(!impure->writeLockMode); @@ -5218,7 +5218,7 @@ void ForNode::setWriteLockMode(jrd_req* request) const impure->writeLockMode = true; } -void ForNode::checkRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const +void ForNode::checkRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const { jrd_rel* relation = rpb->rpb_relation; if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->rel_file || relation->rel_view_rse) @@ -5233,7 +5233,7 @@ void ForNode::checkRecordUpdated(thread_db* tdbb, jrd_req* request, record_param Arg::Gds(isc_merge_dup_update).raise(); } -void ForNode::setRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const +void ForNode::setRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const { jrd_rel* relation = rpb->rpb_relation; if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->rel_file || relation->rel_view_rse) @@ -5286,16 +5286,16 @@ HandlerNode* HandlerNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* HandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* HandlerNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: return statement; - case jrd_req::req_unwind: + case Request::req_unwind: if (!request->req_label) - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; default: return parentStmt; @@ -5349,21 +5349,21 @@ LabelNode* LabelNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* LabelNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* LabelNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: return statement; - case jrd_req::req_unwind: + case Request::req_unwind: fb_assert(!(request->req_flags & req_continue_loop)); if (request->req_label == labelNumber && (request->req_flags & (req_leave | req_error_handler))) { request->req_flags &= ~req_leave; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } // fall into @@ -5475,16 +5475,16 @@ LoopNode* LoopNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* LoopNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* LoopNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: - case jrd_req::req_return: - request->req_operation = jrd_req::req_evaluate; + case Request::req_evaluate: + case Request::req_return: + request->req_operation = Request::req_evaluate; return statement; - case jrd_req::req_unwind: + case Request::req_unwind: { const LabelNode* label = nodeAs(parentStmt.getObject()); @@ -5492,7 +5492,7 @@ const StmtNode* LoopNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeStat (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement; } // fall into @@ -6372,13 +6372,13 @@ MessageNode* MessageNode::pass2(thread_db* /*tdbb*/, CompilerScratch* csb) return this; } -const StmtNode* MessageNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* MessageNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { USHORT* flags = request->getImpure(impureFlags); memset(flags, 0, sizeof(USHORT) * format->fmt_count); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -6890,15 +6890,15 @@ ModifyNode* ModifyNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ModifyNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* ModifyNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { impure_state* impure = request->getImpure(impureOffset); const StmtNode* retNode; - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) return parentStmt; - if (request->req_operation == jrd_req::req_return && !impure->sta_state && subMod) + if (request->req_operation == Request::req_return && !impure->sta_state && subMod) { if (!exeState->topNode) { @@ -6922,7 +6922,7 @@ const StmtNode* ModifyNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState->whichModTrig = ALL_TRIGS; } else - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; } else { @@ -6937,7 +6937,7 @@ const StmtNode* ModifyNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Execute a MODIFY statement. -const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const { jrd_tra* transaction = request->req_transaction; impure_state* impure = request->getImpure(impureOffset); @@ -6949,24 +6949,24 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (!(marks & MARK_AVOID_COUNTERS)) request->req_records_affected.bumpModified(false); if (impure->sta_state == 0 && forNode && forNode->isWriteLockMode(request)) - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; // fall thru else break; - case jrd_req::req_return: + case Request::req_return: if (impure->sta_state == 1) { impure->sta_state = 0; Record* orgRecord = orgRpb->rpb_record; const Record* newRecord = newRpb->rpb_record; orgRecord->copyDataFrom(newRecord, true); - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement; } @@ -7047,7 +7047,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg if (statement2) { impure->sta_state = 2; - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement2; } } @@ -7068,7 +7068,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg if (orgRpb->rpb_runtime_flags & RPB_just_deleted) { - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; return parentStmt; } @@ -7090,7 +7090,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg if (orgRpb->rpb_runtime_flags & RPB_undo_deleted) { - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; return parentStmt; } @@ -7221,10 +7221,10 @@ DmlNode* OuterMapNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* return node; } -const StmtNode* OuterMapNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* OuterMapNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; return parentStmt; } @@ -7295,9 +7295,9 @@ PostEventNode* PostEventNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* PostEventNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* PostEventNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { jrd_tra* transaction = request->req_transaction; @@ -7312,7 +7312,7 @@ const StmtNode* PostEventNode::execute(thread_db* tdbb, jrd_req* request, ExeSta if (transaction->tra_flags & TRA_autocommit) transaction->tra_flags |= TRA_perform_autocommit; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -7373,23 +7373,23 @@ ReceiveNode* ReceiveNode::pass2(thread_db* tdbb, CompilerScratch* csb) // Execute a RECEIVE statement. This can be entered either with "req_evaluate" (ordinary receive // statement) or "req_proceed" (select statement). // In the latter case, the statement isn't every formalled evaluated. -const StmtNode* ReceiveNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ReceiveNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_return: + case Request::req_return: if (!(request->req_batch_mode && batchFlag)) break; // fall into - case jrd_req::req_evaluate: - request->req_operation = jrd_req::req_receive; + case Request::req_evaluate: + request->req_operation = Request::req_receive; request->req_message = message; request->req_flags |= req_stall; return this; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_evaluate; + case Request::req_proceed: + request->req_operation = Request::req_evaluate; return statement; default: @@ -7946,12 +7946,12 @@ StoreNode* StoreNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* StoreNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* StoreNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { impure_state* impure = request->getImpure(impureOffset); const StmtNode* retNode; - if (request->req_operation == jrd_req::req_return && !impure->sta_state && subStore) + if (request->req_operation == Request::req_return && !impure->sta_state && subStore) { if (!exeState->topNode) { @@ -7975,7 +7975,7 @@ const StmtNode* StoreNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState->whichStoTrig = ALL_TRIGS; } else - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; } else { @@ -7990,7 +7990,7 @@ const StmtNode* StoreNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Execute a STORE statement. -const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const { jrd_tra* transaction = request->req_transaction; impure_state* impure = request->getImpure(impureOffset); @@ -8007,7 +8007,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (!(marks & MARK_AVOID_COUNTERS)) { if (!nodeIs(parentStmt)) @@ -8021,7 +8021,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger RLCK_reserve_relation(tdbb, transaction, relation, true); break; - case jrd_req::req_return: + case Request::req_return: if (!impure->sta_state) { SavepointChangeMarker scMarker(transaction); @@ -8079,7 +8079,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger if (statement2) { impure->sta_state = 1; - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement2; } } @@ -8365,13 +8365,13 @@ SelectNode* SelectNode::pass2(thread_db* tdbb, CompilerScratch* csb) // EXE_send will then loop thru the sub-statements of select looking for the appropriate RECEIVE // statement. When (or if) it finds it, it will set it up the next statement to be executed. // The RECEIVE, then, will be entered with the operation "req_proceed". -const StmtNode* SelectNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SelectNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: request->req_message = this; - request->req_operation = jrd_req::req_receive; + request->req_operation = Request::req_receive; request->req_flags |= req_stall; return this; @@ -8432,9 +8432,9 @@ SetGeneratorNode* SetGeneratorNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { jrd_tra* const transaction = request->req_transaction; @@ -8447,7 +8447,7 @@ const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, jrd_req* request, Exe DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_SEQUENCE, generator.name, NULL, *request->getStatement()->sqlText); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -8493,19 +8493,19 @@ StallNode* StallNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) // Execute a stall statement. This is like a blr_receive, except that there is no need for a // gds__send () from the user (i.e. EXE_send () in the engine). // A gds__receive () will unblock the user. -const StmtNode* StallNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* StallNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: - case jrd_req::req_return: + case Request::req_evaluate: + case Request::req_return: request->req_message = this; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; request->req_flags |= req_stall; return this; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_return; + case Request::req_proceed: + request->req_operation = Request::req_return; return parentStmt; default: @@ -8587,11 +8587,11 @@ SuspendNode* SuspendNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Execute a SEND statement. -const StmtNode* SuspendNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SuspendNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: { // ASF: If this is the send in the tail of a procedure and the procedure was called // with a SELECT, don't run all the send statements. It may make validations fail when @@ -8623,14 +8623,14 @@ const StmtNode* SuspendNode::execute(thread_db* tdbb, jrd_req* request, ExeState // fall into } - case jrd_req::req_return: - request->req_operation = jrd_req::req_send; + case Request::req_return: + request->req_operation = Request::req_send; request->req_message = message; request->req_flags |= req_stall; return this; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_return; + case Request::req_proceed: + request->req_operation = Request::req_return; return parentStmt; default: @@ -8751,11 +8751,11 @@ SavepointEncloseNode* SavepointEncloseNode::pass2(thread_db* tdbb, CompilerScrat return this; } -const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { const auto transaction = request->req_transaction; - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (!(transaction->tra_flags & TRA_system)) { @@ -8766,7 +8766,7 @@ const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, return statement; } - if (request->req_operation == jrd_req::req_return) + if (request->req_operation == Request::req_return) { if (!(transaction->tra_flags & TRA_system)) { @@ -9160,9 +9160,9 @@ TruncateLocalTableNode* TruncateLocalTableNode::copy(thread_db* tdbb, NodeCopier return node; } -const StmtNode* TruncateLocalTableNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* TruncateLocalTableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { const auto localTable = request->getStatement()->localTables[tableNumber]; @@ -9172,7 +9172,7 @@ const StmtNode* TruncateLocalTableNode::execute(thread_db* tdbb, jrd_req* reques recordBuffer = nullptr; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -10876,9 +10876,9 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, } } -static void restartRequest(const jrd_req* request, jrd_tra* transaction) +static void restartRequest(const Request* request, jrd_tra* transaction) { - const jrd_req* top_request = request->req_snapshot.m_owner; + const Request* top_request = request->req_snapshot.m_owner; fb_assert(top_request); fb_assert(top_request->req_flags & req_update_conflict); @@ -10898,7 +10898,7 @@ static void validateExpressions(thread_db* tdbb, const Array& vali for (Array::const_iterator i = validations.begin(); i != end; ++i) { - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (!i->boolean->execute(tdbb, request) && !(request->req_flags & req_null)) { diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index 9d356297f2..5886d3fd63 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -143,7 +143,7 @@ public: virtual AssignmentNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AssignmentNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual AssignmentNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst asgnFrom; @@ -171,10 +171,10 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual BlockNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual BlockNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: - static bool testAndFixupError(thread_db* tdbb, jrd_req* request, const ExceptionArray& conditions); + static bool testAndFixupError(thread_db* tdbb, Request* request, const ExceptionArray& conditions); public: NestConst action; @@ -201,7 +201,7 @@ public: virtual CompoundStmtNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual CompoundStmtNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual CompoundStmtNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: Firebird::Array > statements; @@ -238,7 +238,7 @@ public: return this; } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: UCHAR blrOp; @@ -271,7 +271,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual CursorStmtNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual CursorStmtNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: MetaName dsqlName; @@ -314,7 +314,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual DeclareCursorNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareCursorNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: MetaName dsqlName; @@ -356,10 +356,10 @@ public: } DeclareLocalTableNode* pass2(thread_db* tdbb, CompilerScratch* csb) override; - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; public: - Impure* getImpure(thread_db* tdbb, jrd_req* request, bool createWhenDead = true) const; + Impure* getImpure(thread_db* tdbb, Request* request, bool createWhenDead = true) const; public: NestConst format; @@ -396,7 +396,7 @@ public: virtual DeclareSubFuncNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareSubFuncNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void parseParameters(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, @@ -449,7 +449,7 @@ public: virtual DeclareSubProcNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareSubProcNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void parseParameters(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, @@ -491,7 +491,7 @@ public: virtual DeclareVariableNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual DeclareVariableNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareVariableNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst dsqlDef; @@ -530,11 +530,11 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual EraseNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual EraseNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* node); - const StmtNode* erase(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; + const StmtNode* erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const; public: NestConst dsqlRelation; @@ -572,7 +572,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ErrorHandlerNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ErrorHandlerNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst action; @@ -607,11 +607,11 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ExecProcedureNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExecProcedureNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: ValueListNode* explodeOutputs(DsqlCompilerScratch* dsqlScratch, const dsql_prc* procedure); - void executeProcedure(thread_db* tdbb, jrd_req* request) const; + void executeProcedure(thread_db* tdbb, Request* request) const; public: QualifiedName dsqlName; @@ -656,12 +656,12 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ExecStatementNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExecStatementNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void genOptionalExpr(DsqlCompilerScratch* dsqlScratch, const UCHAR code, ValueExprNode* node); - void getString(thread_db* tdbb, jrd_req* request, const ValueExprNode* node, + void getString(thread_db* tdbb, Request* request, const ValueExprNode* node, Firebird::string& str, bool useAttCS = false) const; public: @@ -701,7 +701,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual IfNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual IfNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst condition; @@ -733,7 +733,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual InAutonomousTransactionNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual InAutonomousTransactionNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst action; @@ -760,7 +760,7 @@ public: virtual InitVariableNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual InitVariableNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual InitVariableNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: USHORT varId; @@ -826,7 +826,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ExceptionNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExceptionNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: void setError(thread_db* tdbb) const; @@ -881,14 +881,14 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual StmtNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual StmtNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; - bool isWriteLockMode(jrd_req* request) const; - void setWriteLockMode(jrd_req* request) const; + bool isWriteLockMode(Request* request) const; + void setWriteLockMode(Request* request) const; // Used by UPDATE and DELETE sub-statements of MERGE - void checkRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const; - void setRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const; + void checkRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const; + void setRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const; public: struct Impure @@ -935,7 +935,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual HandlerNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual HandlerNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; @@ -960,7 +960,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual LabelNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual LabelNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; @@ -1009,7 +1009,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual LoopNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual LoopNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: MetaName* dsqlLabelName; @@ -1110,7 +1110,7 @@ public: virtual MessageNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual MessageNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual MessageNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: Firebird::SortedArray itemsUsedInSubroutines; @@ -1139,11 +1139,11 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ModifyNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ModifyNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void pass1Modify(thread_db* tdbb, CompilerScratch* csb, ModifyNode* node); - const StmtNode* modify(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; + const StmtNode* modify(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const; public: NestConst dsqlRelation; @@ -1199,7 +1199,7 @@ public: return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; }; @@ -1221,7 +1221,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual PostEventNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual PostEventNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst event; @@ -1248,7 +1248,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ReceiveNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ReceiveNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; @@ -1277,12 +1277,12 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual StoreNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual StoreNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static bool pass1Store(thread_db* tdbb, CompilerScratch* csb, StoreNode* node); void makeDefaults(thread_db* tdbb, CompilerScratch* csb); - const StmtNode* store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; + const StmtNode* store(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const; public: NestConst target; @@ -1321,7 +1321,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual SelectNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SelectNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst dsqlExpr; @@ -1355,7 +1355,7 @@ public: virtual SetGeneratorNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SetGeneratorNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: GeneratorItem generator; @@ -1379,7 +1379,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual StallNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual StallNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; }; @@ -1401,7 +1401,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual SuspendNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SuspendNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst message; @@ -1448,7 +1448,7 @@ public: virtual SavepointEncloseNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SavepointEncloseNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; @@ -1893,7 +1893,7 @@ public: return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; public: USHORT tableNumber = 0; diff --git a/src/dsql/WinNodes.cpp b/src/dsql/WinNodes.cpp index 5caaca0190..296ed000cd 100644 --- a/src/dsql/WinNodes.cpp +++ b/src/dsql/WinNodes.cpp @@ -76,7 +76,7 @@ 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) const +void DenseRankWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -84,11 +84,11 @@ void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0, 0); } -void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { } -dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlu_misc.vlu_int64; @@ -146,7 +146,7 @@ AggNode* RankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RankWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -155,13 +155,13 @@ void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->vlux_count = 0; } -void RankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const +void RankWinNode::aggPass(thread_db* /*tdbb*/, Request* request, dsc* /*desc*/) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; } -dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* RankWinNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -222,7 +222,7 @@ AggNode* PercentRankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void PercentRankWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -235,13 +235,13 @@ void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impureTemp->vlux_count = 0; } -void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const +void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, Request* request, dsc* /*desc*/) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; } -dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); impure_value_ex* impureTemp = request->getImpure(tempImpure); @@ -254,7 +254,7 @@ dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const return NULL; } -dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impureTemp = request->getImpure(tempImpure); @@ -307,7 +307,7 @@ AggNode* CumeDistWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void CumeDistWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -315,7 +315,7 @@ void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_double(0); } -dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -366,7 +366,7 @@ 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) const +void RowNumberWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -374,7 +374,7 @@ void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0, 0); } -dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impure = request->getImpure(impureOffset); impure->vlu_misc.vlu_int64 = window->getRecordPosition() - window->getPartitionStart() + 1; @@ -425,12 +425,12 @@ ValueExprNode* FirstValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) cons return node; } -void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void FirstValueWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); } -dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* FirstValueWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { if (!window->moveWithinFrame(-(window->getRecordPosition() - window->getFrameStart()))) return NULL; @@ -486,12 +486,12 @@ ValueExprNode* LastValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } -void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void LastValueWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); } -dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* LastValueWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { if (!window->moveWithinFrame(window->getFrameEnd() - window->getRecordPosition())) return NULL; @@ -558,7 +558,7 @@ ValueExprNode* NthValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } -void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void NthValueWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -566,7 +566,7 @@ void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0, 0); } -dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* NthValueWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { dsc* desc = EVL_expr(tdbb, request, row); if (!desc || (request->req_flags & req_null)) @@ -652,12 +652,12 @@ void LagLeadWinNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) arg->getDesc(tdbb, csb, desc); } -void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void LagLeadWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); } -dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* LagLeadWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { dsc* desc = EVL_expr(tdbb, request, rows); if (!desc || (request->req_flags & req_null)) @@ -818,7 +818,7 @@ AggNode* NTileWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void NTileWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -847,7 +847,7 @@ void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impure = request->getImpure(impureOffset); ThisImpure* thisImpure = request->getImpure(thisImpureOffset); diff --git a/src/dsql/WinNodes.h b/src/dsql/WinNodes.h index 918f82cd5d..68097198e8 100644 --- a/src/dsql/WinNodes.h +++ b/src/dsql/WinNodes.h @@ -51,9 +51,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -81,9 +81,9 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -114,11 +114,11 @@ 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) 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 aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -149,9 +149,9 @@ 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) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -178,9 +178,9 @@ 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) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -202,9 +202,9 @@ 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) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -228,9 +228,9 @@ 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) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -269,9 +269,9 @@ 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) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -306,8 +306,8 @@ public: 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) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count); @@ -373,9 +373,9 @@ 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) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index be7141d6d3..7ca31a3bc1 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -87,7 +87,7 @@ namespace Jrd class ValueListNode; class WindowClause; class jrd_tra; - class jrd_req; + class Request; class blb; struct bid; diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index 8b15f37c1a..9d3cbd5915 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -646,7 +646,7 @@ bool Attachment::hasActiveRequests() const for (const jrd_tra* transaction = att_transactions; transaction; transaction = transaction->tra_next) { - for (const jrd_req* request = transaction->tra_requests; + for (const Request* request = transaction->tra_requests; request; request = request->req_tra_next) { if (request->req_transaction && (request->req_flags & req_active)) @@ -659,7 +659,7 @@ bool Attachment::hasActiveRequests() const // Find an inactive incarnation of a system request. If necessary, clone it. -jrd_req* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT which) +Request* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT which) { static const int MAX_RECURSION = 100; @@ -685,7 +685,7 @@ jrd_req* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT w // Msg363 "request depth exceeded. (Recursive definition?)" } - jrd_req* clone = statement->getRequest(tdbb, n); + Request* clone = statement->getRequest(tdbb, n); if (!(clone->req_flags & (req_active | req_reserved))) { diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 11294625a3..7be7939088 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -68,7 +68,7 @@ namespace Jrd class thread_db; class Database; class jrd_tra; - class jrd_req; + class Request; class Lock; class jrd_file; class Format; @@ -546,7 +546,7 @@ private: public: Firebird::SortedArray att_statements; // Statements belonging to attachment - Firebird::SortedArray att_requests; // Requests belonging to attachment + Firebird::SortedArray att_requests; // Requests belonging to attachment Lock* att_id_lock; // Attachment lock (if any) AttNumber att_attachment_id; // Attachment ID Lock* att_cancel_lock; // Lock to cancel the active request @@ -625,7 +625,7 @@ public: Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt Firebird::DecimalStatus att_dec_status; // error handling and rounding - jrd_req* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); + Request* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); Firebird::Array att_charsets; // intl character set descriptions Firebird::GenericMapgetRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); delete impure->table; @@ -112,7 +112,7 @@ bool ConfigTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, RecordBuffer* ConfigTableScan::getRecords(thread_db* tdbb, jrd_rel* relation) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->table) diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index b8158020d3..2b049f75ef 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -134,9 +134,9 @@ namespace return type_alignments[desc->dsc_dtype]; } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { // Clear the message. This is important for external routines. UCHAR* msg = request->getImpure(impureOffset); @@ -204,9 +204,9 @@ namespace return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { dsc* defaultDesc = NULL; @@ -242,7 +242,7 @@ namespace MOV_move(tdbb, &temp, &desc); } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -322,9 +322,9 @@ namespace { } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { UCHAR* inMsg = extInMessageNode ? request->getImpure(extInMessageNode->impureOffset) : NULL; UCHAR* outMsg = request->getImpure(extOutMessageNode->impureOffset); @@ -361,7 +361,7 @@ namespace statements.add(FB_NEW_POOL(pool) StallNode(pool)); } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const { impure_state* const impure = request->getImpure(impureOffset); ExtEngineManager::ResultSet*& resultSet = request->req_ext_resultset; @@ -373,7 +373,7 @@ namespace switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: fb_assert(!resultSet); resultSet = procedure->open(tdbb, extInMsg, extOutMsg); @@ -386,8 +386,8 @@ namespace *eof = -1; // fall into - case jrd_req::req_proceed: - case jrd_req::req_sync: + case Request::req_proceed: + case Request::req_sync: if (resultSet) { if (resultSet->fetch(tdbb) && (request->req_flags & req_proc_fetch)) @@ -401,10 +401,10 @@ namespace } impure->sta_state = 0; // suspend node - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; break; - case jrd_req::req_unwind: + case Request::req_unwind: delete resultSet; resultSet = NULL; break; @@ -450,21 +450,21 @@ namespace return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { trigger->execute(tdbb, request, request->req_trigger_action, getRpb(request, 0), getRpb(request, 1)); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; } private: - static record_param* getRpb(jrd_req* request, USHORT n) + static record_param* getRpb(Request* request, USHORT n) { return request->req_rpb.getCount() > n && request->req_rpb[n].rpb_number.isValid() ? &request->req_rpb[n] : NULL; @@ -893,7 +893,7 @@ ExtEngineManager::Trigger::~Trigger() } -void ExtEngineManager::Trigger::execute(thread_db* tdbb, jrd_req* request, unsigned action, +void ExtEngineManager::Trigger::execute(thread_db* tdbb, Request* request, unsigned action, record_param* oldRpb, record_param* newRpb) const { EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine); @@ -1039,7 +1039,7 @@ void ExtEngineManager::Trigger::setupComputedFields(thread_db* tdbb, MemoryPool& } -void ExtEngineManager::Trigger::setValues(thread_db* tdbb, jrd_req* request, Array& msgBuffer, +void ExtEngineManager::Trigger::setValues(thread_db* tdbb, Request* request, Array& msgBuffer, record_param* rpb) const { if (!rpb || !rpb->rpb_record) diff --git a/src/jrd/ExtEngineManager.h b/src/jrd/ExtEngineManager.h index 9ae59dd9a6..031358103d 100644 --- a/src/jrd/ExtEngineManager.h +++ b/src/jrd/ExtEngineManager.h @@ -40,7 +40,7 @@ namespace Jrd { class thread_db; class jrd_prc; -class jrd_req; +class Request; class jrd_tra; class Attachment; class CompilerScratch; @@ -280,12 +280,12 @@ public: Firebird::IExternalTrigger* aTrigger, const Jrd::Trigger* aTrg); ~Trigger(); - void execute(thread_db* tdbb, jrd_req* request, unsigned action, + void execute(thread_db* tdbb, Request* request, unsigned action, record_param* oldRpb, record_param* newRpb) const; private: void setupComputedFields(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb); - void setValues(thread_db* tdbb, jrd_req* request, Firebird::Array& msgBuffer, record_param* rpb) const; + void setValues(thread_db* tdbb, Request* request, Firebird::Array& msgBuffer, record_param* rpb) const; public: Firebird::Array> computedStatements; diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index c9e0d61fe3..17aa3438ec 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -1078,7 +1078,7 @@ void Monitoring::putTransaction(SnapshotData::DumpRecord& record, const jrd_tra* record.reset(rel_mon_transactions); int temp = mon_state_idle; - for (const jrd_req* request = transaction->tra_requests; + for (const Request* request = transaction->tra_requests; request; request = request->req_tra_next) { if (request->req_transaction && (request->req_flags & req_active)) @@ -1176,7 +1176,7 @@ void Monitoring::putStatement(SnapshotData::DumpRecord& record, const Statement* } -void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* request, +void Monitoring::putRequest(SnapshotData::DumpRecord& record, const Request* request, const string& plan) { fb_assert(request); @@ -1236,12 +1236,12 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req } -void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* request) +void Monitoring::putCall(SnapshotData::DumpRecord& record, const Request* request) { fb_assert(request); const auto dbb = request->req_attachment->att_database; - const jrd_req* initialRequest = request->req_caller; + const Request* initialRequest = request->req_caller; while (initialRequest->req_caller) { @@ -1470,7 +1470,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) for (transaction = attachment->att_transactions; transaction; transaction = transaction->tra_next) { - for (jrd_req* request = transaction->tra_requests; + for (Request* request = transaction->tra_requests; request && (request->req_flags & req_active) && (request->req_transaction == transaction); request = request->req_caller) { diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index f77b7ef983..649e88484f 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -390,8 +390,8 @@ private: static void putAttachment(SnapshotData::DumpRecord&, const Attachment*); static void putTransaction(SnapshotData::DumpRecord&, const jrd_tra*); static void putStatement(SnapshotData::DumpRecord&, const Statement*, const Firebird::string&); - static void putRequest(SnapshotData::DumpRecord&, const jrd_req*, const Firebird::string&); - static void putCall(SnapshotData::DumpRecord&, const jrd_req*); + static void putRequest(SnapshotData::DumpRecord&, const Request*, const Firebird::string&); + static void putCall(SnapshotData::DumpRecord&, const Request*); static void putStatistics(SnapshotData::DumpRecord&, const RuntimeStatistics&, int, int); static void putContextVars(SnapshotData::DumpRecord&, const Firebird::StringMap&, SINT64, bool); static void putMemoryUsage(SnapshotData::DumpRecord&, const Firebird::MemoryStats&, int, int); diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index 62ebcf1ab4..371e1c161a 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -346,10 +346,10 @@ void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& valu { fb_assert(param > 0); - jrd_req* request = getDsqlRequest()->getRequest(); + Request* request = getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. - AutoSetRestore2 autoRequest( + AutoSetRestore2 autoRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, request); AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); diff --git a/src/jrd/ResultSet.cpp b/src/jrd/ResultSet.cpp index 01bf321647..59d0f03861 100644 --- a/src/jrd/ResultSet.cpp +++ b/src/jrd/ResultSet.cpp @@ -103,10 +103,10 @@ Firebird::string ResultSet::getString(thread_db* tdbb, unsigned param) { fb_assert(param > 0); - jrd_req* request = stmt->getDsqlRequest()->getRequest(); + Request* request = stmt->getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. - AutoSetRestore2 autoRequest( + AutoSetRestore2 autoRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, request); AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); @@ -131,10 +131,10 @@ void ResultSet::moveDesc(thread_db* tdbb, unsigned param, dsc& desc) { fb_assert(param > 0); - jrd_req* request = stmt->getDsqlRequest()->getRequest(); + Request* request = stmt->getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. - AutoSetRestore2 autoRequest( + AutoSetRestore2 autoRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, request); AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); diff --git a/src/jrd/RuntimeStatistics.h b/src/jrd/RuntimeStatistics.h index 68c0c51fb6..8871aee017 100644 --- a/src/jrd/RuntimeStatistics.h +++ b/src/jrd/RuntimeStatistics.h @@ -32,7 +32,7 @@ namespace Firebird { // declared in firebird/Interface.h -struct TraceCounts; +struct TraceCounts; struct PerformanceInfo; } // namespace Firebird @@ -360,7 +360,7 @@ private: // These two numbers are used in adjust() and assign() methods as "generation" // values in order to avoid costly operations when two instances of RuntimeStatistics // contain equal counters values. This is intended to use *only* with the - // same pair of class instances, as in jrd_req. + // same pair of class instances, as in Request. ULONG allChgNumber; // incremented when any counter changes ULONG relChgNumber; // incremented when relation counter changes diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 51a4870152..ac1d05c235 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -209,7 +209,7 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool Database* const dbb = tdbb->getDatabase(); fb_assert(dbb); - jrd_req* const old_request = tdbb->getRequest(); + Request* const old_request = tdbb->getRequest(); tdbb->setRequest(NULL); Statement* statement = NULL; @@ -310,7 +310,7 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool } // Turn a parsed scratch into an executable request. -jrd_req* Statement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +Request* Statement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) { Statement* statement = makeStatement(tdbb, csb, internalFlag); return statement->getRequest(tdbb, 0); @@ -330,7 +330,7 @@ const Routine* Statement::getRoutine() const // Determine if any request of this statement are active. bool Statement::isActive() const { - for (const jrd_req* const* request = requests.begin(); request != requests.end(); ++request) + for (const Request* const* request = requests.begin(); request != requests.end(); ++request) { if (*request && ((*request)->req_flags & req_in_use)) return true; @@ -339,7 +339,7 @@ bool Statement::isActive() const return false; } -jrd_req* Statement::findRequest(thread_db* tdbb, bool unique) +Request* Statement::findRequest(thread_db* tdbb, bool unique) { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -351,14 +351,14 @@ jrd_req* Statement::findRequest(thread_db* tdbb, bool unique) // Search clones for one request in use by this attachment. // If not found, return first inactive request. - jrd_req* clone = NULL; + Request* clone = NULL; USHORT count = 0; const USHORT clones = requests.getCount(); USHORT n; for (n = 0; n < clones; ++n) { - jrd_req* next = getRequest(tdbb, n); + Request* next = getRequest(tdbb, n); if (next->req_attachment == attachment) { @@ -391,7 +391,7 @@ jrd_req* Statement::findRequest(thread_db* tdbb, bool unique) return clone; } -jrd_req* Statement::getRequest(thread_db* tdbb, USHORT level) +Request* Statement::getRequest(thread_db* tdbb, USHORT level) { SET_TDBB(tdbb); @@ -408,7 +408,7 @@ jrd_req* Statement::getRequest(thread_db* tdbb, USHORT level) &dbb->dbb_memory_stats : &attachment->att_memory_stats; // Create the request. - jrd_req* const request = FB_NEW_POOL(*pool) jrd_req(attachment, this, parentStats); + Request* const request = FB_NEW_POOL(*pool) Request(attachment, this, parentStats); if (level == 0) pool->setStatsGroup(request->req_memory_stats); @@ -646,7 +646,7 @@ void Statement::release(thread_db* tdbb) } } - for (jrd_req** instance = requests.begin(); instance != requests.end(); ++instance) + for (Request** instance = requests.begin(); instance != requests.end(); ++instance) EXE_release(tdbb, *instance); const auto attachment = tdbb->getAttachment(); diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index 318e6d1d2b..cdf98ff9ad 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -46,7 +46,7 @@ private: public: static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); - static jrd_req* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + static Request* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); StmtNumber getStatementId() const { @@ -58,8 +58,8 @@ public: const Routine* getRoutine() const; bool isActive() const; - jrd_req* findRequest(thread_db* tdbb, bool unique = false); - jrd_req* getRequest(thread_db* tdbb, USHORT level); + Request* findRequest(thread_db* tdbb, bool unique = false); + Request* getRequest(thread_db* tdbb, USHORT level); void verifyAccess(thread_db* tdbb); void release(thread_db* tdbb); @@ -79,7 +79,7 @@ public: ULONG impureSize; // Size of impure area mutable StmtNumber id; // statement identifier Firebird::Array rpbsSetup; - Firebird::Array requests; // vector of requests + Firebird::Array requests; // vector of requests ExternalAccessList externalList; // Access to procedures/triggers to be checked AccessItemList accessList; // Access items to be checked ResourceList resources; // Resources (relations and indices) diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index b3555ecccf..068fe65bb2 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -1862,7 +1862,7 @@ dsc* evlStdMath(thread_db* tdbb, const SysFunction* function, const NestValueArr fb_assert(args.getCount() == 1); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -1962,7 +1962,7 @@ dsc* evlAbs(thread_db* tdbb, const SysFunction*, const NestValueArray& args, imp { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2020,7 +2020,7 @@ dsc* evlAsciiChar(thread_db* tdbb, const SysFunction*, const NestValueArray& arg { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2042,7 +2042,7 @@ dsc* evlAsciiVal(thread_db* tdbb, const SysFunction*, const NestValueArray& args { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2077,7 +2077,7 @@ dsc* evlAtan2(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* desc1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if desc1 is NULL @@ -2146,7 +2146,7 @@ dsc* evlBin(thread_db* tdbb, const SysFunction* function, const NestValueArray& fb_assert(function->misc != NULL); Function func = (Function)(IPTR) function->misc; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); bool f128 = false; for (unsigned i = 0; i < args.getCount(); ++i) @@ -2212,7 +2212,7 @@ dsc* evlBinShift(thread_db* tdbb, const SysFunction* function, const NestValueAr fb_assert(args.getCount() == 2); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -2263,7 +2263,7 @@ dsc* evlCeil(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2348,7 +2348,7 @@ dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValue { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2466,7 +2466,7 @@ dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() == 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* quantityDsc = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if quantityDsc is NULL @@ -2994,7 +2994,7 @@ dsc* evlEncryptDecrypt(thread_db* tdbb, const SysFunction* function, const NestV fb_assert(args.getCount() == CRYPT_ARG_MAX); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[CRYPT_ARG_MAX]; @@ -3512,7 +3512,7 @@ dsc* evlRsaEncryptDecrypt(thread_db* tdbb, const SysFunction* function, const Ne fb_assert(args.getCount() == RSA_CRYPT_ARG_MAX || args.getCount() == RSA_CRYPT_ARG_MAX - 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[RSA_CRYPT_ARG_MAX]; @@ -3582,7 +3582,7 @@ dsc* evlRsaPrivate(thread_db* tdbb, const SysFunction* function, const NestValue fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -3613,7 +3613,7 @@ dsc* evlRsaPublic(thread_db* tdbb, const SysFunction* function, const NestValueA fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -3652,7 +3652,7 @@ dsc* evlRsaSign(thread_db* tdbb, const SysFunction* function, const NestValueArr fb_assert(args.getCount() == RSA_SIGN_ARG_MAX || args.getCount() == RSA_SIGN_ARG_MAX - 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[RSA_SIGN_ARG_MAX]; @@ -3721,7 +3721,7 @@ dsc* evlRsaVerify(thread_db* tdbb, const SysFunction* function, const NestValueA fb_assert(args.getCount() == RSA_VERIFY_ARG_MAX || args.getCount() == RSA_VERIFY_ARG_MAX - 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[RSA_VERIFY_ARG_MAX]; @@ -3780,7 +3780,7 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr { fb_assert(args.getCount() == 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* partDsc = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if partDsc is NULL @@ -4010,7 +4010,7 @@ dsc* evlExp(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4047,7 +4047,7 @@ dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestVal { fb_assert(args.getCount() >= 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* partDsc = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if partDsc is NULL @@ -4174,7 +4174,7 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4292,7 +4292,7 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar Jrd::Attachment* attachment = tdbb->getAttachment(); Database* dbb = tdbb->getDatabase(); jrd_tra* transaction = tdbb->getTransaction(); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); request->req_flags &= ~req_null; const dsc* nameSpace = EVL_expr(tdbb, request, args[0]); @@ -4442,7 +4442,7 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar else if ((transaction->tra_flags & TRA_read_committed) && (transaction->tra_flags & TRA_read_consistency)) { - jrd_req* snapshot_req = request->req_snapshot.m_owner; + Request* snapshot_req = request->req_snapshot.m_owner; if (snapshot_req) resultStr.printf("%" SQUADFORMAT, snapshot_req->req_snapshot.m_number); else @@ -4579,7 +4579,7 @@ dsc* evlSetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar Jrd::Attachment* attachment = tdbb->getAttachment(); jrd_tra* transaction = tdbb->getTransaction(); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); request->req_flags &= ~req_null; const dsc* nameSpace = EVL_expr(tdbb, request, args[0]); @@ -4679,7 +4679,7 @@ dsc* evlGetTranCN(thread_db* tdbb, const SysFunction* function, const NestValueA fb_assert(args.getCount() == 1); Database* dbb = tdbb->getDatabase(); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); request->req_flags &= ~req_null; const dsc* value = EVL_expr(tdbb, request, args[0]); @@ -4720,7 +4720,7 @@ dsc* evlHash(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() >= 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4779,7 +4779,7 @@ dsc* evlLeft(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* str = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if str is NULL @@ -4803,7 +4803,7 @@ dsc* evlLnLog10(thread_db* tdbb, const SysFunction* function, const NestValueArr fb_assert(args.getCount() == 1); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4876,7 +4876,7 @@ dsc* evlLog(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -4942,7 +4942,7 @@ dsc* evlQuantize(thread_db* tdbb, const SysFunction* function, const NestValueAr { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -4981,7 +4981,7 @@ dsc* evlCompare(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -5037,7 +5037,7 @@ dsc* evlNormDec(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value; value = EVL_expr(tdbb, request, args[0]); @@ -5070,7 +5070,7 @@ dsc* evlMakeDbkey(Jrd::thread_db* tdbb, const SysFunction* function, const NestV // MAKE_DBKEY ( REL_NAME | REL_ID, RECNUM [, DPNUM [, PPNUM] ] ) Database* const dbb = tdbb->getDatabase(); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); fb_assert(args.getCount() >= 2 && args.getCount() <= 4); @@ -5171,7 +5171,7 @@ dsc* evlMaxMinValue(thread_db* tdbb, const SysFunction* function, const NestValu fb_assert(args.getCount() >= 1); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* result = NULL; for (FB_SIZE_T i = 0; i < args.getCount(); ++i) @@ -5211,7 +5211,7 @@ dsc* evlMod(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -5274,7 +5274,7 @@ dsc* evlOverlay(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() >= 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -5458,7 +5458,7 @@ dsc* evlPad(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() >= 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -5633,7 +5633,7 @@ dsc* evlPosition(thread_db* tdbb, const SysFunction* function, const NestValueAr { fb_assert(args.getCount() >= 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -5759,7 +5759,7 @@ dsc* evlPower(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -5835,7 +5835,7 @@ dsc* evlReplace(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* values[3]; // 0 = searched, 1 = find, 2 = replacement const dsc* firstBlob = NULL; @@ -5993,7 +5993,7 @@ dsc* evlReverse(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6096,7 +6096,7 @@ dsc* evlRight(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6149,7 +6149,7 @@ dsc* evlRound(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() >= 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6197,7 +6197,7 @@ dsc* evlSign(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6228,7 +6228,7 @@ dsc* evlSqrt(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6271,7 +6271,7 @@ dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() >= 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6404,7 +6404,7 @@ dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValue { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6448,7 +6448,7 @@ dsc* evlRoleInUse(thread_db* tdbb, const SysFunction*, const NestValueArray& arg { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); Jrd::Attachment* attachment = tdbb->getAttachment(); const dsc* value = EVL_expr(tdbb, request, args[0]); @@ -6475,7 +6475,7 @@ dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction*, const NestValueArra { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL return NULL; @@ -6497,7 +6497,7 @@ dsc* evlUnicodeChar(thread_db* tdbb, const SysFunction* function, const NestValu { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6532,7 +6532,7 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL diff --git a/src/jrd/blb.cpp b/src/jrd/blb.cpp index 10cbbd763b..66c73150f4 100644 --- a/src/jrd/blb.cpp +++ b/src/jrd/blb.cpp @@ -345,13 +345,13 @@ blb* blb::create2(thread_db* tdbb, // Bind non-user blob to the request - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (!userBlob && request) { transaction->tra_blobs->locate(blob->blb_temp_id); BlobIndex* current = &transaction->tra_blobs->current(); - jrd_req* blob_request = request; + Request* blob_request = request; while (blob_request->req_caller) blob_request = blob_request->req_caller; @@ -1052,7 +1052,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, return; } - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (relation->isVirtual()) { ERR_post(Arg::Gds(isc_read_only)); @@ -1138,7 +1138,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, { // Walk through call stack looking if our BLOB is // owned by somebody from our call chain - jrd_req* temp_req = request; + Request* temp_req = request; do { if (blobIndex->bli_request == temp_req) break; @@ -1224,7 +1224,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, blobIndex->bli_materialized = true; blobIndex->bli_blob_id = *destination; // Assign temporary BLOB ownership to top-level request if it is not assigned yet - jrd_req* own_request; + Request* own_request; if (blobIndex->bli_request) { own_request = blobIndex->bli_request; } @@ -2081,7 +2081,7 @@ blb* blb::copy_blob(thread_db* tdbb, const bid* source, bid* destination, **************************************/ SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); blb* input = open2(tdbb, transaction, source, bpb_length, bpb); blb* output = create(tdbb, transaction, destination); @@ -2532,7 +2532,7 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc status_exception::raise(Arg::Gds(isc_malformed_string)); } - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); transaction = transaction->getOuter(); @@ -2580,7 +2580,7 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc if (current->bli_materialized) { // Delete BLOB from request owned blob list - jrd_req* blob_request = current->bli_request; + Request* blob_request = current->bli_request; if (blob_request) { if (blob_request->req_blobs.locate(blob_temp_id)) { @@ -2603,7 +2603,7 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc // we may still bind lifetime of blob to current top level request. if (!current->bli_request) { - jrd_req* blob_request = request; + Request* blob_request = request; while (blob_request->req_caller) blob_request = blob_request->req_caller; @@ -2641,7 +2641,7 @@ static void move_to_string(thread_db* tdbb, dsc* fromDesc, dsc* toDesc) else blobAsText.dsc_ttype() = ttype_ascii; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); transaction = transaction->getOuter(); @@ -2678,7 +2678,7 @@ void blb::destroy(const bool purge_flag) { if (blb_transaction->tra_blobs->locate(blb_temp_id)) { - jrd_req* blob_request = blb_transaction->tra_blobs->current().bli_request; + Request* blob_request = blb_transaction->tra_blobs->current().bli_request; if (blob_request) { diff --git a/src/jrd/blb.h b/src/jrd/blb.h index 99d9c055bb..29d9dde90a 100644 --- a/src/jrd/blb.h +++ b/src/jrd/blb.h @@ -51,7 +51,7 @@ namespace Jrd class Attachment; class BlobControl; class jrd_rel; -class jrd_req; +class Request; class jrd_tra; class vcl; class thread_db; diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 3cc257ccc3..2babf8bc52 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -541,8 +541,8 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& fb_assert(idx->idx_expression != NULL); // check for resursive expression evaluation - jrd_req* const org_request = tdbb->getRequest(); - jrd_req* const expr_request = idx->idx_expression_statement->findRequest(tdbb, true); + Request* const org_request = tdbb->getRequest(); + Request* const expr_request = idx->idx_expression_statement->findRequest(tdbb, true); if (expr_request == NULL) ERR_post(Arg::Gds(isc_random) << "Attempt to evaluate index expression recursively"); @@ -3281,7 +3281,7 @@ static DSC* eval(thread_db* tdbb, const ValueExprNode* node, DSC* temp, bool* is **************************************/ SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* desc = EVL_expr(tdbb, request, node); *isNull = false; diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index 07d143ee91..9a7051cc6e 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -199,7 +199,7 @@ Statement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool return statement; } -jrd_req* CMP_compile_request(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag) +Request* CMP_compile_request(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag) { /************************************** * @@ -401,7 +401,7 @@ void CMP_post_resource( ResourceList* rsc_ptr, void* obj, Resource::rsc_s type, } -void CMP_release(thread_db* tdbb, jrd_req* request) +void CMP_release(thread_db* tdbb, Request* request) { /************************************** * diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index 4312fe3f21..a8938a5665 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -39,11 +39,11 @@ Jrd::BoolExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jr Jrd::ValueExprNode* CMP_clone_node(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); Jrd::Statement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, ULONG dbginfoLength, const UCHAR* dbginfo); -Jrd::jrd_req* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); +Jrd::Request* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element); const Jrd::Format* CMP_format(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType); Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); -Jrd::jrd_req* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); +Jrd::Request* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&); void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName&, SLONG ssRelationId, @@ -53,6 +53,6 @@ void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName void CMP_post_procedure_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*); void CMP_post_resource(Jrd::ResourceList*, void*, Jrd::Resource::rsc_s, USHORT); Jrd::RecordSource* CMP_post_rse(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::RseNode*); -void CMP_release(Jrd::thread_db*, Jrd::jrd_req*); +void CMP_release(Jrd::thread_db*, Jrd::Request*); #endif // JRD_CMP_PROTO_H diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 7b8e783533..ac11591c31 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -2795,7 +2795,7 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* SelectivityList selectivity(*tdbb->getDefaultPool()); jrd_tra* const current_transaction = tdbb->getTransaction(); - jrd_req* const current_request = tdbb->getRequest(); + Request* const current_request = tdbb->getRequest(); try { @@ -5911,10 +5911,10 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ ValueExprNode* defaultNode = static_cast(MET_parse_blob( tdbb, relation, defaultValue, NULL, &defaultStatement, false, false)); - jrd_req* const defaultRequest = defaultStatement->findRequest(tdbb); + Request* const defaultRequest = defaultStatement->findRequest(tdbb); // Attention: this is scoped to the end of this "try". - AutoSetRestore2 autoRequest(tdbb, + AutoSetRestore2 autoRequest(tdbb, &thread_db::getRequest, &thread_db::setRequest, defaultRequest); defaultRequest->validateTimeStamp(); diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 4e4839f7df..5116c08806 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -134,7 +134,7 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) DEV_BLKCHK(node, type_nod); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // The only nodes that can be assigned to are: argument, field and variable. @@ -243,7 +243,7 @@ RecordBitmap** EVL_bitmap(thread_db* tdbb, const InversionNode* node, RecordBitm case InversionNode::TYPE_DBKEY: { - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); impure_inversion* impure = request->getImpure(node->impure); RecordBitmap::reset(impure->inv_bitmap); const dsc* const desc = EVL_expr(tdbb, request, node->value); @@ -303,7 +303,7 @@ void EVL_dbkey_bounds(thread_db* tdbb, const Array& ranges, **************************************/ SET_TDBB(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); for (const auto node : ranges) { @@ -624,7 +624,7 @@ void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, d if (itemInfo == NULL) return; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); bool err = false; if (null && !itemInfo->nullable) diff --git a/src/jrd/evl_proto.h b/src/jrd/evl_proto.h index 964e0b9f44..cfaa53eafa 100644 --- a/src/jrd/evl_proto.h +++ b/src/jrd/evl_proto.h @@ -46,7 +46,7 @@ void EVL_validate(Jrd::thread_db*, const Jrd::Item&, const Jrd::ItemInfo*, dsc* namespace Jrd { // Evaluate a value expression. - inline dsc* EVL_expr(thread_db* tdbb, jrd_req* request, const ValueExprNode* node) + inline dsc* EVL_expr(thread_db* tdbb, Request* request, const ValueExprNode* node) { if (!node) BUGCHECK(303); // msg 303 Invalid expression for evaluation diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 2c2313f2d9..5dac7198b9 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -214,11 +214,11 @@ string StatusXcp::as_text() const } -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*); -static void trigger_failure(thread_db*, jrd_req*); -static void stuff_stack_trace(const jrd_req*); +static void execute_looper(thread_db*, Request*, jrd_tra*, const StmtNode*, Request::req_s); +static void looper_seh(thread_db*, Request*, const StmtNode*); +static void release_blobs(thread_db*, Request*); +static void trigger_failure(thread_db*, Request*); +static void stuff_stack_trace(const Request*); const size_t MAX_STACK_TRACE = 2048; @@ -229,7 +229,7 @@ void EXE_assignment(thread_db* tdbb, const AssignmentNode* node) DEV_BLKCHK(node, type_nod); SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // Get descriptors of src field/parameter/variable, etc. request->req_flags &= ~req_null; @@ -243,7 +243,7 @@ void EXE_assignment(thread_db* tdbb, const AssignmentNode* node) void EXE_assignment(thread_db* tdbb, const ValueExprNode* source, const ValueExprNode* target) { SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // Get descriptors of src field/parameter/variable, etc. request->req_flags &= ~req_null; @@ -257,14 +257,14 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo const ValueExprNode* missing_node, const ValueExprNode* missing2_node) { SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const auto toVar = nodeAs(to); if (toVar && toVar->outerDecl) request = toVar->getVarRequest(request); - AutoSetRestore2 autoSetRequest( + AutoSetRestore2 autoSetRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, request); // Get descriptors of receiving and sending fields/parameters, variables, etc. @@ -299,7 +299,7 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo if (toParam->argInfo) { - AutoSetRestore2 autoSetRequest( + AutoSetRestore2 autoSetRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber), @@ -315,7 +315,7 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo if (toVar->varInfo) { - AutoSetRestore2 autoSetRequest( + AutoSetRestore2 autoSetRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId), @@ -589,7 +589,7 @@ void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTri void EXE_receive(thread_db* tdbb, - jrd_req* request, + Request* request, USHORT msg, ULONG length, void* buffer, @@ -647,9 +647,9 @@ void EXE_receive(thread_db* tdbb, try { if (nodeIs(request->req_message)) - execute_looper(tdbb, request, transaction, request->req_next, jrd_req::req_sync); + execute_looper(tdbb, request, transaction, request->req_next, Request::req_sync); - if (!(request->req_flags & req_active) || request->req_operation != jrd_req::req_send) + if (!(request->req_flags & req_active) || request->req_operation != Request::req_send) ERR_post(Arg::Gds(isc_req_sync)); const MessageNode* message = nodeAs(request->req_message); @@ -694,7 +694,7 @@ void EXE_receive(thread_db* tdbb, } } - execute_looper(tdbb, request, transaction, request->req_next, jrd_req::req_proceed); + execute_looper(tdbb, request, transaction, request->req_next, Request::req_proceed); } catch (const Exception&) { @@ -744,7 +744,7 @@ void EXE_receive(thread_db* tdbb, // Release a request instance. -void EXE_release(thread_db* tdbb, jrd_req* request) +void EXE_release(thread_db* tdbb, Request* request) { DEV_BLKCHK(request, type_req); @@ -768,7 +768,7 @@ void EXE_release(thread_db* tdbb, jrd_req* request) } -void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const void* buffer) +void EXE_send(thread_db* tdbb, Request* request, USHORT msg, ULONG length, const void* buffer) { /************************************** * @@ -792,7 +792,7 @@ void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const const StmtNode* message = NULL; const StmtNode* node; - if (request->req_operation != jrd_req::req_receive) + if (request->req_operation != Request::req_receive) ERR_post(Arg::Gds(isc_req_sync)); node = request->req_message; @@ -831,11 +831,11 @@ void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const memcpy(request->getImpure(message->impureOffset), buffer, length); - execute_looper(tdbb, request, transaction, request->req_next, jrd_req::req_proceed); + execute_looper(tdbb, request, transaction, request->req_next, Request::req_proceed); } -void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) +void EXE_start(thread_db* tdbb, Request* request, jrd_tra* transaction) { /************************************** * @@ -908,11 +908,11 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) execute_looper(tdbb, request, transaction, request->getStatement()->topNode, - jrd_req::req_evaluate); + Request::req_evaluate); } -void EXE_unwind(thread_db* tdbb, jrd_req* request) +void EXE_unwind(thread_db* tdbb, Request* request) { /************************************** * @@ -935,7 +935,7 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) if (statement->fors.getCount() || request->req_ext_resultset || request->req_ext_stmt) { Jrd::ContextPoolHolder context(tdbb, request->req_pool); - jrd_req* old_request = tdbb->getRequest(); + Request* old_request = tdbb->getRequest(); jrd_tra* old_transaction = tdbb->getTransaction(); try { tdbb->setRequest(request); @@ -1002,10 +1002,10 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) static void execute_looper(thread_db* tdbb, - jrd_req* request, + Request* request, jrd_tra* transaction, const StmtNode* node, - jrd_req::req_s next_state) + Request::req_s next_state) { /************************************** * @@ -1120,7 +1120,7 @@ void EXE_execute_triggers(thread_db* tdbb, SET_TDBB(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request ? request->req_transaction : tdbb->getTransaction(); TrigVector* vector = *triggers; @@ -1149,7 +1149,7 @@ void EXE_execute_triggers(thread_db* tdbb, else TimeZoneUtil::validateGmtTimeStamp(timestamp); - jrd_req* trigger = NULL; + Request* trigger = NULL; try { @@ -1210,7 +1210,7 @@ void EXE_execute_triggers(thread_db* tdbb, EXE_start(tdbb, trigger, transaction); } - const bool ok = (trigger->req_operation != jrd_req::req_unwind); + const bool ok = (trigger->req_operation != Request::req_unwind); trace.finish(ok ? ITracePlugin::RESULT_SUCCESS : ITracePlugin::RESULT_FAILED); EXE_unwind(tdbb, trigger); @@ -1246,11 +1246,11 @@ void EXE_execute_triggers(thread_db* tdbb, } -static void stuff_stack_trace(const jrd_req* request) +static void stuff_stack_trace(const Request* request) { string sTrace; - for (const jrd_req* req = request; req; req = req->req_caller) + for (const Request* req = request; req; req = req->req_caller) { const Statement* const statement = req->getStatement(); @@ -1310,7 +1310,7 @@ static void stuff_stack_trace(const jrd_req* request) } -const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* node) +const StmtNode* EXE_looper(thread_db* tdbb, Request* request, const StmtNode* node) { /************************************** * @@ -1348,7 +1348,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no { try { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { JRD_reschedule(tdbb); @@ -1390,7 +1390,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no exeState.errorPending = true; exeState.catchDisabled = true; - request->req_operation = jrd_req::req_unwind; + request->req_operation = Request::req_unwind; request->req_label = 0; if (!(tdbb->tdbb_flags & TDBB_stack_trace_done) && !(tdbb->tdbb_flags & TDBB_sys_error)) @@ -1453,7 +1453,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no // Start looper under Windows SEH (Structured Exception Handling) control -static void looper_seh(thread_db* tdbb, jrd_req* request, const StmtNode* node) +static void looper_seh(thread_db* tdbb, Request* request, const StmtNode* node) { #ifdef WIN_NT START_CHECK_FOR_EXCEPTIONS(NULL); @@ -1475,7 +1475,7 @@ static void looper_seh(thread_db* tdbb, jrd_req* request, const StmtNode* node) } -static void release_blobs(thread_db* tdbb, jrd_req* request) +static void release_blobs(thread_db* tdbb, Request* request) { /************************************** * @@ -1550,7 +1550,7 @@ static void release_blobs(thread_db* tdbb, jrd_req* request) } -static void trigger_failure(thread_db* tdbb, jrd_req* trigger) +static void trigger_failure(thread_db* tdbb, Request* trigger) { /************************************** * diff --git a/src/jrd/exe_proto.h b/src/jrd/exe_proto.h index ee60d69e6f..e644e97bfe 100644 --- a/src/jrd/exe_proto.h +++ b/src/jrd/exe_proto.h @@ -27,7 +27,7 @@ #include "../jrd/cmp_proto.h" namespace Jrd { - class jrd_req; + class Request; class jrd_tra; class AssignmentNode; } @@ -40,17 +40,17 @@ void EXE_assignment(Jrd::thread_db* tdbb, const Jrd::ValueExprNode* to, dsc* fro void EXE_execute_db_triggers(Jrd::thread_db*, Jrd::jrd_tra*, enum TriggerAction); void EXE_execute_ddl_triggers(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, bool preTriggers, int action); -const Jrd::StmtNode* EXE_looper(Jrd::thread_db* tdbb, Jrd::jrd_req* request, +const Jrd::StmtNode* EXE_looper(Jrd::thread_db* tdbb, Jrd::Request* request, const Jrd::StmtNode* in_node); void EXE_execute_triggers(Jrd::thread_db*, Jrd::TrigVector**, Jrd::record_param*, Jrd::record_param*, enum TriggerAction, Jrd::StmtNode::WhichTrigger); -void EXE_receive(Jrd::thread_db*, Jrd::jrd_req*, USHORT, ULONG, void*, bool = false); -void EXE_release(Jrd::thread_db*, Jrd::jrd_req*); -void EXE_send(Jrd::thread_db*, Jrd::jrd_req*, USHORT, ULONG, const void*); -void EXE_start(Jrd::thread_db*, Jrd::jrd_req*, Jrd::jrd_tra*); -void EXE_unwind(Jrd::thread_db*, Jrd::jrd_req*); +void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false); +void EXE_release(Jrd::thread_db*, Jrd::Request*); +void EXE_send(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, const void*); +void EXE_start(Jrd::thread_db*, Jrd::Request*, Jrd::jrd_tra*); +void EXE_unwind(Jrd::thread_db*, Jrd::Request*); namespace Jrd { @@ -97,12 +97,12 @@ namespace Jrd cacheRequest(); } - jrd_req* operator ->() + Request* operator ->() { return request; } - operator jrd_req*() + operator Request*() { return request; } @@ -139,7 +139,7 @@ namespace Jrd private: USHORT id; USHORT which; - jrd_req* request; + Request* request; }; class AutoRequest @@ -169,12 +169,12 @@ namespace Jrd request = CMP_compile_request(tdbb, blr, blrLength, true); } - jrd_req* operator ->() + Request* operator ->() { return request; } - operator jrd_req*() + operator Request*() { return request; } @@ -195,7 +195,7 @@ namespace Jrd } private: - jrd_req* request; + Request* request; }; } diff --git a/src/jrd/extds/ExtDS.cpp b/src/jrd/extds/ExtDS.cpp index a88f5b14d2..7180d19d4a 100644 --- a/src/jrd/extds/ExtDS.cpp +++ b/src/jrd/extds/ExtDS.cpp @@ -2249,7 +2249,7 @@ void Statement::doSetInParams(thread_db* tdbb, unsigned int count, const MetaStr const NestConst* jrdVar = params; GenericMap > > paramDescs(getPool()); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); for (FB_SIZE_T i = 0; i < count; ++i, ++jrdVar) { @@ -2364,7 +2364,7 @@ void Statement::getExtBlob(thread_db* tdbb, const dsc& src, dsc& dst) { extBlob->open(tdbb, *m_transaction, src, NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const UCHAR bpb[] = {isc_bpb_version1, isc_bpb_storage, 1, isc_bpb_storage_temp}; bid* localBlobID = (bid*) dst.dsc_address; destBlob = blb::create2(tdbb, request->req_transaction, localBlobID, sizeof(bpb), bpb); @@ -2408,7 +2408,7 @@ void Statement::putExtBlob(thread_db* tdbb, dsc& src, dsc& dst) { extBlob->create(tdbb, *m_transaction, dst, NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); bid* srcBid = (bid*) src.dsc_address; UCharBuffer bpb; @@ -2478,7 +2478,7 @@ void Statement::raise(FbStatusVector* status, thread_db* tdbb, const char* sWher Arg::Str(m_connection.getDataSourceName())); } -void Statement::bindToRequest(jrd_req* request, Statement** impure) +void Statement::bindToRequest(Request* request, Statement** impure) { fb_assert(!m_boundReq); fb_assert(!m_prevInReq); diff --git a/src/jrd/extds/ExtDS.h b/src/jrd/extds/ExtDS.h index ff0b91e975..dad7527d5e 100644 --- a/src/jrd/extds/ExtDS.h +++ b/src/jrd/extds/ExtDS.h @@ -631,7 +631,7 @@ public: const Firebird::string* sQuery = NULL); // Active statement must be bound to parent jrd request - void bindToRequest(Jrd::jrd_req* request, Statement** impure); + void bindToRequest(Jrd::Request* request, Statement** impure); void unBindFromRequest(); protected: @@ -666,7 +666,7 @@ protected: Statement* m_nextFree; // next free statement - Jrd::jrd_req* m_boundReq; + Jrd::Request* m_boundReq; Statement** m_ReqImpure; Statement* m_nextInReq; Statement* m_prevInReq; @@ -693,7 +693,7 @@ protected: unsigned int m_outputs; bool m_callerPrivileges; - Jrd::jrd_req* m_preparedByReq; + Jrd::Request* m_preparedByReq; // set in preprocess Firebird::SortedObjectsArray m_sqlParamNames; diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 1ecd53d20c..54bce85cf1 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -459,7 +459,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (m_callerPrivileges) { - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); auto statement = request ? request->getStatement() : NULL; CallerName callerName; const Routine* routine; diff --git a/src/jrd/fun.epp b/src/jrd/fun.epp index 9c6522f35b..f7fd84c68d 100644 --- a/src/jrd/fun.epp +++ b/src/jrd/fun.epp @@ -359,7 +359,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra const Parameter* return_ptr = function->getOutputFields()[0]; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // CVC: restoring the null flag seems like a Borland hack to try to // patch a bug with null handling. There's no evident reason to restore it // because EVL_expr() resets it every time it's called. Kept it for now. diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index 8bbf73394a..96ed00b384 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -1008,7 +1008,7 @@ UCHAR* INF_put_item(UCHAR item, } -ULONG INF_request_info(const jrd_req* request, +ULONG INF_request_info(const Request* request, const ULONG item_length, const UCHAR* items, const ULONG output_length, @@ -1090,9 +1090,9 @@ ULONG INF_request_info(const jrd_req* request, else { auto state = isc_info_req_active; - if (request->req_operation == jrd_req::req_send) + if (request->req_operation == Request::req_send) state = isc_info_req_send; - else if (request->req_operation == jrd_req::req_receive) + else if (request->req_operation == Request::req_receive) { const StmtNode* node = request->req_next; @@ -1101,7 +1101,7 @@ ULONG INF_request_info(const jrd_req* request, else state = isc_info_req_receive; } - else if ((request->req_operation == jrd_req::req_return) && + else if ((request->req_operation == Request::req_return) && (request->req_flags & req_stall)) { state = isc_info_req_sql_stall; @@ -1113,8 +1113,8 @@ ULONG INF_request_info(const jrd_req* request, case isc_info_message_number: case isc_info_message_size: if (!(request->req_flags & req_active) || - (request->req_operation != jrd_req::req_receive && - request->req_operation != jrd_req::req_send)) + (request->req_operation != Request::req_receive && + request->req_operation != Request::req_send)) { buffer_ptr[0] = item; item = isc_info_error; diff --git a/src/jrd/inf_proto.h b/src/jrd/inf_proto.h index d19f5b9a0c..2478689bfc 100644 --- a/src/jrd/inf_proto.h +++ b/src/jrd/inf_proto.h @@ -25,7 +25,7 @@ #define JRD_INF_PROTO_H namespace Jrd { - class jrd_req; + class Request; class jrd_tra; class blb; } @@ -34,7 +34,7 @@ void INF_blob_info(const Jrd::blb*, const ULONG, const UCHAR*, const ULONG, UCHA USHORT INF_convert(SINT64, UCHAR*); void INF_database_info(Jrd::thread_db*, const ULONG, const UCHAR*, const ULONG, UCHAR*); UCHAR* INF_put_item(UCHAR, ULONG, const void*, UCHAR*, const UCHAR*, const bool inserting = false); -ULONG INF_request_info(const Jrd::jrd_req*, const ULONG, const UCHAR*, const ULONG, UCHAR*); +ULONG INF_request_info(const Jrd::Request*, const ULONG, const UCHAR*, const ULONG, UCHAR*); void INF_transaction_info(const Jrd::jrd_tra*, const ULONG, const UCHAR*, const ULONG, UCHAR*); #endif // JRD_INF_PROTO_H diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index b1bfa3d2b9..03bc4ab561 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1311,7 +1311,7 @@ static bool drop_files(const jrd_file*); static void find_intl_charset(thread_db*, Jrd::Attachment*, const DatabaseOptions*); static void init_database_lock(thread_db*); static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction); -static jrd_req* verify_request_synchronization(Statement* statement, USHORT level); +static Request* verify_request_synchronization(Statement* statement, USHORT level); static void purge_transactions(thread_db*, Jrd::Attachment*, const bool); static void check_single_maintenance(thread_db* tdbb); @@ -1357,7 +1357,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas // do it here to prevent committing every record update // in a statement // -static void check_autocommit(thread_db* tdbb, jrd_req* request) +static void check_autocommit(thread_db* tdbb, Request* request) { jrd_tra* const transaction = request->req_transaction; @@ -3880,7 +3880,7 @@ void JRequest::receive(CheckStatusWrapper* user_status, int level, unsigned int EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -4017,7 +4017,7 @@ void JRequest::getInfo(CheckStatusWrapper* user_status, int level, unsigned int EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -4220,7 +4220,7 @@ void JRequest::send(CheckStatusWrapper* user_status, int level, unsigned int msg EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -4447,7 +4447,7 @@ void JRequest::startAndSend(CheckStatusWrapper* user_status, ITransaction* tra, validateHandle(tdbb, transaction); check_database(tdbb); - jrd_req* request = getHandle()->getRequest(tdbb, level); + Request* request = getHandle()->getRequest(tdbb, level); try { @@ -4505,7 +4505,7 @@ void JRequest::start(CheckStatusWrapper* user_status, ITransaction* tra, int lev validateHandle(tdbb, transaction); check_database(tdbb); - jrd_req* request = getHandle()->getRequest(tdbb, level); + Request* request = getHandle()->getRequest(tdbb, level); try { @@ -4703,7 +4703,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* const MessageNode* inMessage = NULL; const MessageNode* outMessage = NULL; - jrd_req* request = NULL; + Request* request = NULL; MemoryPool* new_pool = att->createPool(); try @@ -4922,7 +4922,7 @@ void JRequest::unwind(CheckStatusWrapper* user_status, int level) EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -8335,7 +8335,7 @@ static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction) // // @param request The incoming, parent request to be replaced. // @param level The level of the sub-request we need to find. -static jrd_req* verify_request_synchronization(Statement* statement, USHORT level) +static Request* verify_request_synchronization(Statement* statement, USHORT level) { if (level) { @@ -8895,7 +8895,7 @@ void thread_db::setTransaction(jrd_tra* val) traStat = val ? &val->tra_stats : RuntimeStatistics::getDummy(); } -void thread_db::setRequest(jrd_req* val) +void thread_db::setRequest(Request* val) { request = val; reqStat = val ? &val->req_stats : RuntimeStatistics::getDummy(); @@ -9084,7 +9084,7 @@ void JRD_autocommit_ddl(thread_db* tdbb, jrd_tra* transaction) } -void JRD_receive(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_length, void* msg) +void JRD_receive(thread_db* tdbb, Request* request, USHORT msg_type, ULONG msg_length, void* msg) { /************************************** * @@ -9108,7 +9108,7 @@ void JRD_receive(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_l } -void JRD_send(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_length, const void* msg) +void JRD_send(thread_db* tdbb, Request* request, USHORT msg_type, ULONG msg_length, const void* msg) { /************************************** * @@ -9132,7 +9132,7 @@ void JRD_send(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_leng } -void JRD_start(Jrd::thread_db* tdbb, jrd_req* request, jrd_tra* transaction) +void JRD_start(Jrd::thread_db* tdbb, Request* request, jrd_tra* transaction) { /************************************** * @@ -9221,7 +9221,7 @@ void JRD_rollback_retaining(thread_db* tdbb, jrd_tra* transaction) } -void JRD_start_and_send(thread_db* tdbb, jrd_req* request, jrd_tra* transaction, +void JRD_start_and_send(thread_db* tdbb, Request* request, jrd_tra* transaction, USHORT msg_type, ULONG msg_length, const void* msg) { /************************************** @@ -9349,7 +9349,7 @@ void JRD_start_transaction(thread_db* tdbb, jrd_tra** transaction, } -void JRD_unwind_request(thread_db* tdbb, jrd_req* request) +void JRD_unwind_request(thread_db* tdbb, Request* request) { /************************************** * diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 0cc93f82a5..d42c37f9f6 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -106,7 +106,7 @@ const unsigned MAX_CALLBACKS = 50; class thread_db; class Attachment; class jrd_tra; -class jrd_req; +class Request; class Statement; class jrd_file; class Format; @@ -506,7 +506,7 @@ private: Database* database; Attachment* attachment; jrd_tra* transaction; - jrd_req* request; + Request* request; RuntimeStatistics *reqStat, *traStat, *attStat, *dbbStat; public: @@ -591,17 +591,17 @@ public: void setTransaction(jrd_tra* val); - jrd_req* getRequest() + Request* getRequest() { return request; } - const jrd_req* getRequest() const + const Request* getRequest() const { return request; } - void setRequest(jrd_req* val); + void setRequest(Request* val); SSHORT getCharSet() const; diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index 3784ba6224..d7f4024eea 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -35,7 +35,7 @@ namespace Jrd { class jrd_tra; class blb; struct bid; - class jrd_req; + class Request; class Statement; class Service; class thread_db; @@ -55,22 +55,22 @@ void JRD_print_procedure_info(Jrd::thread_db*, const char*); void JRD_autocommit_ddl(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); -void JRD_receive(Jrd::thread_db* tdbb, Jrd::jrd_req* request, USHORT msg_type, ULONG msg_length, +void JRD_receive(Jrd::thread_db* tdbb, Jrd::Request* request, USHORT msg_type, ULONG msg_length, void* msg); -void JRD_start(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tra* transaction); +void JRD_start(Jrd::thread_db* tdbb, Jrd::Request* request, Jrd::jrd_tra* transaction); void JRD_commit_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_commit_retaining(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_rollback_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_rollback_retaining(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_run_trans_start_triggers(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); -void JRD_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, USHORT msg_type, ULONG msg_length, +void JRD_send(Jrd::thread_db* tdbb, Jrd::Request* request, USHORT msg_type, ULONG msg_length, const void* msg); -void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tra* transaction, +void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::Request* request, Jrd::jrd_tra* transaction, USHORT msg_type, ULONG msg_length, const void* msg); void JRD_start_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction, Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb); -void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::jrd_req* request); +void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::Request* request); bool JRD_verify_database_access(const Firebird::PathName&); void JRD_shutdown_attachment(Jrd::Attachment* attachment); void JRD_shutdown_attachments(Jrd::Database* dbb); diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index 018338db70..bd90314f21 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -31,7 +31,7 @@ struct dsc; namespace Jrd { class jrd_tra; - class jrd_req; + class Request; class Statement; class jrd_prc; class Format; diff --git a/src/jrd/par_proto.h b/src/jrd/par_proto.h index c8b23789d9..77ea5dd2a0 100644 --- a/src/jrd/par_proto.h +++ b/src/jrd/par_proto.h @@ -27,7 +27,7 @@ namespace Jrd { class CompilerScratch; class jrd_rel; - class jrd_req; + class Request; class Statement; class thread_db; class ItemInfo; diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index d2592e03d0..049a8eb576 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -54,7 +54,7 @@ BaseAggWinStream::BaseAggWinStream(thread_db* tdbb, Compiler template void BaseAggWinStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = getImpure(request); impure->irsb_flags = irsb_open; @@ -77,7 +77,7 @@ void BaseAggWinStream::open(thread_db* tdbb) const template void BaseAggWinStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -111,7 +111,7 @@ void BaseAggWinStream::markRecursive() } template -void BaseAggWinStream::invalidateRecords(jrd_req* request) const +void BaseAggWinStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -130,7 +130,7 @@ void BaseAggWinStream::findUsedStreams(StreamList& streams, template bool BaseAggWinStream::evaluateGroup(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); JRD_reschedule(tdbb); @@ -194,7 +194,7 @@ bool BaseAggWinStream::evaluateGroup(thread_db* tdbb) const // Initialize the aggregate record template -void BaseAggWinStream::aggInit(thread_db* tdbb, jrd_req* request, +void BaseAggWinStream::aggInit(thread_db* tdbb, Request* request, const MapNode* map) const { const NestConst* const sourceEnd = map->sourceList.end(); @@ -215,7 +215,7 @@ void BaseAggWinStream::aggInit(thread_db* tdbb, jrd_req* req // Go through and compute all the aggregates on this record template -bool BaseAggWinStream::aggPass(thread_db* tdbb, jrd_req* request, +bool BaseAggWinStream::aggPass(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const { bool ret = true; @@ -245,7 +245,7 @@ bool BaseAggWinStream::aggPass(thread_db* tdbb, jrd_req* req } template -void BaseAggWinStream::aggExecute(thread_db* tdbb, jrd_req* request, +void BaseAggWinStream::aggExecute(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const { const NestConst* const sourceEnd = sourceList.end(); @@ -277,7 +277,7 @@ void BaseAggWinStream::aggExecute(thread_db* tdbb, jrd_req* // Finalize a sort for distinct aggregate template -void BaseAggWinStream::aggFinish(thread_db* tdbb, jrd_req* request, +void BaseAggWinStream::aggFinish(thread_db* tdbb, Request* request, const MapNode* map) const { const NestConst* const sourceEnd = map->sourceList.end(); @@ -295,7 +295,7 @@ void BaseAggWinStream::aggFinish(thread_db* tdbb, jrd_req* r // Look for change in the values of a group/order. template -int BaseAggWinStream::lookForChange(thread_db* tdbb, jrd_req* request, +int BaseAggWinStream::lookForChange(thread_db* tdbb, Request* request, const NestValueArray* group, const SortNode* sort, impure_value* values) const { if (!group) @@ -340,7 +340,7 @@ int BaseAggWinStream::lookForChange(thread_db* tdbb, jrd_req } template -bool BaseAggWinStream::getNextRecord(thread_db* tdbb, jrd_req* request) const +bool BaseAggWinStream::getNextRecord(thread_db* tdbb, Request* request) const { Impure* const impure = getImpure(request); @@ -377,7 +377,7 @@ bool AggregatedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = getImpure(request); diff --git a/src/jrd/recsrc/BitmapTableScan.cpp b/src/jrd/recsrc/BitmapTableScan.cpp index 6b3485eda7..1c7cc3ec03 100644 --- a/src/jrd/recsrc/BitmapTableScan.cpp +++ b/src/jrd/recsrc/BitmapTableScan.cpp @@ -48,7 +48,7 @@ BitmapTableScan::BitmapTableScan(CompilerScratch* csb, const string& alias, void BitmapTableScan::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -62,7 +62,7 @@ void BitmapTableScan::open(thread_db* tdbb) const void BitmapTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -84,7 +84,7 @@ bool BitmapTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 5bfad5b7ff..24302b6dda 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -114,7 +114,7 @@ BufferedStream::BufferedStream(CompilerScratch* csb, RecordSource* next) void BufferedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_mustread; @@ -130,7 +130,7 @@ void BufferedStream::open(thread_db* tdbb) const void BufferedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -151,7 +151,7 @@ bool BufferedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -334,7 +334,7 @@ void BufferedStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void BufferedStream::invalidateRecords(jrd_req* request) const +void BufferedStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -346,7 +346,7 @@ void BufferedStream::nullRecords(thread_db* tdbb) const void BufferedStream::locate(thread_db* tdbb, FB_UINT64 position) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); // If we haven't fetched and cached the underlying stream completely, do it now @@ -362,7 +362,7 @@ void BufferedStream::locate(thread_db* tdbb, FB_UINT64 position) const FB_UINT64 BufferedStream::getCount(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); // If we haven't fetched and cached the underlying stream completely, do it now diff --git a/src/jrd/recsrc/ConditionalStream.cpp b/src/jrd/recsrc/ConditionalStream.cpp index 91d3e85403..3175cb1a0e 100644 --- a/src/jrd/recsrc/ConditionalStream.cpp +++ b/src/jrd/recsrc/ConditionalStream.cpp @@ -50,7 +50,7 @@ ConditionalStream::ConditionalStream(CompilerScratch* csb, void ConditionalStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -61,7 +61,7 @@ void ConditionalStream::open(thread_db* tdbb) const void ConditionalStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -79,7 +79,7 @@ bool ConditionalStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -90,7 +90,7 @@ bool ConditionalStream::getRecord(thread_db* tdbb) const bool ConditionalStream::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -101,7 +101,7 @@ bool ConditionalStream::refetchRecord(thread_db* tdbb) const bool ConditionalStream::lockRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -146,7 +146,7 @@ void ConditionalStream::findUsedStreams(StreamList& streams, bool expandAll) con m_second->findUsedStreams(streams, expandAll); } -void ConditionalStream::invalidateRecords(jrd_req* request) const +void ConditionalStream::invalidateRecords(Request* request) const { m_first->invalidateRecords(request); m_second->invalidateRecords(request); diff --git a/src/jrd/recsrc/Cursor.cpp b/src/jrd/recsrc/Cursor.cpp index 941e228bf6..b65ed23896 100644 --- a/src/jrd/recsrc/Cursor.cpp +++ b/src/jrd/recsrc/Cursor.cpp @@ -35,7 +35,7 @@ namespace { bool validate(thread_db* tdbb) { - const jrd_req* const request = tdbb->getRequest(); + const Request* const request = tdbb->getRequest(); if (request->req_flags & req_abort) return false; @@ -47,7 +47,7 @@ namespace } // Initialize dependent invariants - void initializeInvariants(jrd_req* request, const VarInvariantArray* invariants) + void initializeInvariants(Request* request, const VarInvariantArray* invariants) { if (invariants) { @@ -105,7 +105,7 @@ Cursor::Cursor(CompilerScratch* csb, const RecordSource* rsb, void Cursor::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* impure = request->getImpure(m_impure); impure->irsb_active = true; @@ -117,7 +117,7 @@ void Cursor::open(thread_db* tdbb) const void Cursor::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_active) @@ -135,7 +135,7 @@ bool Cursor::fetchNext(thread_db* tdbb) const if (!validate(tdbb)) return false; - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->irsb_active) @@ -207,7 +207,7 @@ bool Cursor::fetchAbsolute(thread_db* tdbb, SINT64 offset) const if (!validate(tdbb)) return false; - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->irsb_active) @@ -268,7 +268,7 @@ bool Cursor::fetchRelative(thread_db* tdbb, SINT64 offset) const if (!validate(tdbb)) return false; - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->irsb_active) @@ -335,7 +335,7 @@ bool Cursor::fetchRelative(thread_db* tdbb, SINT64 offset) const } // Check if the cursor is in a good state for access a field. -void Cursor::checkState(jrd_req* request) const +void Cursor::checkState(Request* request) const { const Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/Cursor.h b/src/jrd/recsrc/Cursor.h index 280dd7e9f2..0e96439bee 100644 --- a/src/jrd/recsrc/Cursor.h +++ b/src/jrd/recsrc/Cursor.h @@ -75,7 +75,7 @@ namespace Jrd bool fetchAbsolute(thread_db* tdbb, SINT64 offset) const; bool fetchRelative(thread_db* tdbb, SINT64 offset) const; - void checkState(jrd_req* request) const; + void checkState(Request* request) const; const RecordSource* getAccessPath() const { diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 836b4b1130..9567d9d914 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -47,7 +47,7 @@ ExternalTableScan::ExternalTableScan(CompilerScratch* csb, const string& alias, void ExternalTableScan::open(thread_db* tdbb) const { Database* const dbb = tdbb->getDatabase(); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -65,7 +65,7 @@ void ExternalTableScan::open(thread_db* tdbb) const void ExternalTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -79,7 +79,7 @@ bool ExternalTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/FilteredStream.cpp b/src/jrd/recsrc/FilteredStream.cpp index 5ae71e8803..8fd1eeaf96 100644 --- a/src/jrd/recsrc/FilteredStream.cpp +++ b/src/jrd/recsrc/FilteredStream.cpp @@ -46,7 +46,7 @@ FilteredStream::FilteredStream(CompilerScratch* csb, RecordSource* next, BoolExp void FilteredStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -56,7 +56,7 @@ void FilteredStream::open(thread_db* tdbb) const void FilteredStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -74,7 +74,7 @@ bool FilteredStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -91,7 +91,7 @@ bool FilteredStream::getRecord(thread_db* tdbb) const bool FilteredStream::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); return m_next->refetchRecord(tdbb) && m_boolean->execute(tdbb, request); @@ -120,7 +120,7 @@ void FilteredStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void FilteredStream::invalidateRecords(jrd_req* request) const +void FilteredStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -132,7 +132,7 @@ void FilteredStream::nullRecords(thread_db* tdbb) const bool FilteredStream::evaluateBoolean(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); // For ANY and ALL clauses (ALL is handled as a negated ANY), // we must first detect them, and then make sure that the returned diff --git a/src/jrd/recsrc/FirstRowsStream.cpp b/src/jrd/recsrc/FirstRowsStream.cpp index f5deb5328c..e0cdb5d596 100644 --- a/src/jrd/recsrc/FirstRowsStream.cpp +++ b/src/jrd/recsrc/FirstRowsStream.cpp @@ -46,7 +46,7 @@ FirstRowsStream::FirstRowsStream(CompilerScratch* csb, RecordSource* next, Value void FirstRowsStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = 0; @@ -67,7 +67,7 @@ void FirstRowsStream::open(thread_db* tdbb) const void FirstRowsStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -85,7 +85,7 @@ bool FirstRowsStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -130,7 +130,7 @@ void FirstRowsStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void FirstRowsStream::invalidateRecords(jrd_req* request) const +void FirstRowsStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index 73b77f8f05..62c62b7ab7 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -47,7 +47,7 @@ FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSou void FullOuterJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_first; @@ -57,7 +57,7 @@ void FullOuterJoin::open(thread_db* tdbb) const void FullOuterJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -78,7 +78,7 @@ bool FullOuterJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -141,7 +141,7 @@ void FullOuterJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_arg2->findUsedStreams(streams, expandAll); } -void FullOuterJoin::invalidateRecords(jrd_req* request) const +void FullOuterJoin::invalidateRecords(Request* request) const { m_arg1->invalidateRecords(request); m_arg2->invalidateRecords(request); diff --git a/src/jrd/recsrc/FullTableScan.cpp b/src/jrd/recsrc/FullTableScan.cpp index d8096fea15..c1494abb81 100644 --- a/src/jrd/recsrc/FullTableScan.cpp +++ b/src/jrd/recsrc/FullTableScan.cpp @@ -51,7 +51,7 @@ void FullTableScan::open(thread_db* tdbb) const { Database* const dbb = tdbb->getDatabase(); Attachment* const attachment = tdbb->getAttachment(); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -112,7 +112,7 @@ void FullTableScan::open(thread_db* tdbb) const void FullTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -135,7 +135,7 @@ bool FullTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index 5dab9e12f4..a32339da68 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -283,7 +283,7 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, void HashJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_mustread; @@ -324,7 +324,7 @@ void HashJoin::open(thread_db* tdbb) const void HashJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); invalidateRecords(request); @@ -350,7 +350,7 @@ bool HashJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -470,7 +470,7 @@ void HashJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_args[i].source->findUsedStreams(streams, expandAll); } -void HashJoin::invalidateRecords(jrd_req* request) const +void HashJoin::invalidateRecords(Request* request) const { m_leader.source->invalidateRecords(request); @@ -487,7 +487,7 @@ void HashJoin::nullRecords(thread_db* tdbb) const } ULONG HashJoin::computeHash(thread_db* tdbb, - jrd_req* request, + Request* request, const SubStream& sub, UCHAR* keyBuffer) const { diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index 08f0a37ed5..a976ab3b8a 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -61,7 +61,7 @@ IndexTableScan::IndexTableScan(CompilerScratch* csb, const string& alias, void IndexTableScan::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_first | irsb_open; @@ -74,7 +74,7 @@ void IndexTableScan::open(thread_db* tdbb) const void IndexTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -126,7 +126,7 @@ bool IndexTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/LockedStream.cpp b/src/jrd/recsrc/LockedStream.cpp index a4d389a4a4..7cebce895c 100644 --- a/src/jrd/recsrc/LockedStream.cpp +++ b/src/jrd/recsrc/LockedStream.cpp @@ -44,7 +44,7 @@ LockedStream::LockedStream(CompilerScratch* csb, RecordSource* next) void LockedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -54,7 +54,7 @@ void LockedStream::open(thread_db* tdbb) const void LockedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -72,7 +72,7 @@ bool LockedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -120,7 +120,7 @@ void LockedStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void LockedStream::invalidateRecords(jrd_req* request) const +void LockedStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index b9e72b89da..c8e79a456e 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -57,7 +57,7 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, void MergeJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -93,7 +93,7 @@ void MergeJoin::open(thread_db* tdbb) const void MergeJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -128,7 +128,7 @@ bool MergeJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -371,7 +371,7 @@ void MergeJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_args[i]->findUsedStreams(streams, expandAll); } -void MergeJoin::invalidateRecords(jrd_req* request) const +void MergeJoin::invalidateRecords(Request* request) const { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->invalidateRecords(request); @@ -386,7 +386,7 @@ void MergeJoin::nullRecords(thread_db* tdbb) const int MergeJoin::compare(thread_db* tdbb, const NestValueArray* node1, const NestValueArray* node2) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); const NestConst* ptr1 = node1->begin(); const NestConst* ptr2 = node2->begin(); @@ -435,7 +435,7 @@ UCHAR* MergeJoin::getData(thread_db* /*tdbb*/, MergeFile* mfb, SLONG record) con SLONG MergeJoin::getRecord(thread_db* tdbb, FB_SIZE_T index) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); const SortedStream* const sort_rsb = m_args[index]; @@ -473,7 +473,7 @@ SLONG MergeJoin::getRecord(thread_db* tdbb, FB_SIZE_T index) const bool MergeJoin::fetchRecord(thread_db* tdbb, FB_SIZE_T index) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); Impure::irsb_mrg_repeat* tail = &impure->irsb_mrg_rpt[index]; diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index 0446f601b7..9545619c8b 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -59,7 +59,7 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, Record void NestedLoopJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_first | irsb_mustread; @@ -67,7 +67,7 @@ void NestedLoopJoin::open(thread_db* tdbb) const void NestedLoopJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -86,7 +86,7 @@ bool NestedLoopJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -257,7 +257,7 @@ void NestedLoopJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_args[i]->findUsedStreams(streams, expandAll); } -void NestedLoopJoin::invalidateRecords(jrd_req* request) const +void NestedLoopJoin::invalidateRecords(Request* request) const { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->invalidateRecords(request); diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index 3398576332..9eb7d95229 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -64,7 +64,7 @@ void ProcedureScan::open(thread_db* tdbb) const const_cast(m_procedure)->checkReload(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -98,7 +98,7 @@ void ProcedureScan::open(thread_db* tdbb) const im = NULL; } - jrd_req* const proc_request = m_procedure->getStatement()->findRequest(tdbb); + Request* const proc_request = m_procedure->getStatement()->findRequest(tdbb); impure->irsb_req_handle = proc_request; // req_proc_fetch flag used only when fetching rows, so @@ -134,7 +134,7 @@ void ProcedureScan::open(thread_db* tdbb) const void ProcedureScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -144,7 +144,7 @@ void ProcedureScan::close(thread_db* tdbb) const { impure->irsb_flags &= ~irsb_open; - jrd_req* const proc_request = impure->irsb_req_handle; + Request* const proc_request = impure->irsb_req_handle; if (proc_request) { @@ -166,7 +166,7 @@ bool ProcedureScan::getRecord(thread_db* tdbb) const UserId* invoker = m_procedure->invoker ? m_procedure->invoker : tdbb->getAttachment()->att_ss_user; AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -185,7 +185,7 @@ bool ProcedureScan::getRecord(thread_db* tdbb) const Record* const record = VIO_record(tdbb, rpb, m_format, tdbb->getDefaultPool()); - jrd_req* const proc_request = impure->irsb_req_handle; + Request* const proc_request = impure->irsb_req_handle; TraceProcFetch trace(tdbb, proc_request); diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index 3c6b5fb641..be59ce762c 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -188,7 +188,7 @@ RecordStream::RecordStream(CompilerScratch* csb, StreamType stream, const Format bool RecordStream::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request->req_transaction; record_param* const rpb = &request->req_rpb[m_stream]; @@ -207,7 +207,7 @@ bool RecordStream::refetchRecord(thread_db* tdbb) const bool RecordStream::lockRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request->req_transaction; record_param* const rpb = &request->req_rpb[m_stream]; @@ -231,7 +231,7 @@ void RecordStream::findUsedStreams(StreamList& streams, bool /*expandAll*/) cons streams.add(m_stream); } -void RecordStream::invalidateRecords(jrd_req* request) const +void RecordStream::invalidateRecords(Request* request) const { record_param* const rpb = &request->req_rpb[m_stream]; rpb->rpb_number.setValid(false); @@ -239,7 +239,7 @@ void RecordStream::invalidateRecords(jrd_req* request) const void RecordStream::nullRecords(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; rpb->rpb_number.setValid(false); diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 20f2048e76..ac29e89a96 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -35,7 +35,7 @@ namespace Jrd { class thread_db; - class jrd_req; + class Request; class jrd_prc; class AggNode; class BoolExprNode; @@ -68,7 +68,7 @@ namespace Jrd bool detailed, unsigned level) const = 0; virtual void markRecursive() = 0; - virtual void invalidateRecords(jrd_req* request) const = 0; + virtual void invalidateRecords(Request* request) const = 0; virtual void findUsedStreams(StreamList& streams, bool expandAll = false) const = 0; virtual void nullRecords(thread_db* tdbb) const = 0; @@ -130,7 +130,7 @@ namespace Jrd bool lockRecord(thread_db* tdbb) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -306,7 +306,7 @@ namespace Jrd { struct Impure : public RecordSource::Impure { - jrd_req* irsb_req_handle; + Request* irsb_req_handle; UCHAR* irsb_message; }; @@ -355,7 +355,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -383,7 +383,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -413,7 +413,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -449,7 +449,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -480,7 +480,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -575,7 +575,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -598,7 +598,7 @@ namespace Jrd bool compareKeys(const UCHAR* p, const UCHAR* q) const; UCHAR* getData(thread_db* tdbb) const; - void mapData(thread_db* tdbb, jrd_req* request, UCHAR* data) const; + void mapData(thread_db* tdbb, Request* request, UCHAR* data) const; bool isKey(const dsc* desc) const { @@ -624,7 +624,7 @@ namespace Jrd class SlidingWindow { public: - SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, jrd_req* request, + SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, Request* request, FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd, FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd); ~SlidingWindow(); @@ -714,28 +714,28 @@ namespace Jrd bool lockRecord(thread_db* tdbb) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; protected: - Impure* getImpure(jrd_req* request) const + Impure* getImpure(Request* request) const { return request->getImpure(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, + void aggInit(thread_db* tdbb, Request* request, const MapNode* map) const; + bool aggPass(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const; - void aggExecute(thread_db* tdbb, jrd_req* request, + void aggExecute(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const; - void aggFinish(thread_db* tdbb, jrd_req* request, const MapNode* map) const; + void aggFinish(thread_db* tdbb, Request* request, const MapNode* map) const; // Cache the values of a group/order in the impure. template - void cacheValues(thread_db* tdbb, jrd_req* request, + void cacheValues(thread_db* tdbb, Request* request, const NestValueArray* group, impure_value* values, AdjustFunctor adjustFunctor) const { @@ -761,11 +761,11 @@ namespace Jrd } } - int lookForChange(thread_db* tdbb, jrd_req* request, + int lookForChange(thread_db* tdbb, Request* request, const NestValueArray* group, const SortNode* sort, impure_value* values) const; private: - bool getNextRecord(thread_db* tdbb, jrd_req* request) const; + bool getNextRecord(thread_db* tdbb, Request* request) const; protected: NestConst m_next; @@ -855,16 +855,16 @@ namespace Jrd void nullRecords(thread_db* tdbb) const; protected: - Impure* getImpure(jrd_req* request) const + Impure* getImpure(Request* request) const { return request->getImpure(m_impure); } private: - const void getFrameValue(thread_db* tdbb, jrd_req* request, + const void getFrameValue(thread_db* tdbb, Request* request, const Frame* frame, impure_value_ex* impureValue) const; - SINT64 locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure, + SINT64 locateFrameRange(thread_db* tdbb, Request* request, Impure* impure, const Frame* frame, const dsc* offsetDesc, SINT64 position) const; private: @@ -893,7 +893,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -909,7 +909,7 @@ namespace Jrd public: virtual void locate(thread_db* tdbb, FB_UINT64 position) const = 0; virtual FB_UINT64 getCount(thread_db* tdbb) const = 0; - virtual FB_UINT64 getPosition(jrd_req* request) const = 0; + virtual FB_UINT64 getPosition(Request* request) const = 0; }; class BufferedStream : public BaseBufferedStream @@ -953,7 +953,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -961,7 +961,7 @@ namespace Jrd void locate(thread_db* tdbb, FB_UINT64 position) const override; FB_UINT64 getCount(thread_db* tdbb) const override; - FB_UINT64 getPosition(jrd_req* request) const override + FB_UINT64 getPosition(Request* request) const override { Impure* const impure = request->getImpure(m_impure); return impure->irsb_position; @@ -993,7 +993,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1022,7 +1022,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1071,13 +1071,13 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; private: - ULONG computeHash(thread_db* tdbb, jrd_req* request, + ULONG computeHash(thread_db* tdbb, Request* request, const SubStream& sub, UCHAR* buffer) const; bool fetchRecord(thread_db* tdbb, Impure* impure, FB_SIZE_T stream) const; @@ -1131,7 +1131,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1188,7 +1188,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; private: @@ -1229,11 +1229,11 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; private: - void cleanupLevel(jrd_req* request, Impure* impure) const; + void cleanupLevel(Request* request, Impure* impure) const; const StreamType m_mapStream; NestConst m_root; @@ -1267,7 +1267,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; diff --git a/src/jrd/recsrc/RecursiveStream.cpp b/src/jrd/recsrc/RecursiveStream.cpp index f6339df6f9..3083f39d70 100644 --- a/src/jrd/recsrc/RecursiveStream.cpp +++ b/src/jrd/recsrc/RecursiveStream.cpp @@ -68,7 +68,7 @@ RecursiveStream::RecursiveStream(CompilerScratch* csb, StreamType stream, Stream void RecursiveStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -96,7 +96,7 @@ void RecursiveStream::open(thread_db* tdbb) const void RecursiveStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); invalidateRecords(request); @@ -119,7 +119,7 @@ bool RecursiveStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); Impure* const saveImpure = request->getImpure(m_saveOffset); @@ -273,7 +273,7 @@ void RecursiveStream::markRecursive() m_inner->markRecursive(); } -void RecursiveStream::invalidateRecords(jrd_req* request) const +void RecursiveStream::invalidateRecords(Request* request) const { m_root->invalidateRecords(request); m_inner->invalidateRecords(request); @@ -293,7 +293,7 @@ void RecursiveStream::findUsedStreams(StreamList& streams, bool expandAll) const } } -void RecursiveStream::cleanupLevel(jrd_req* request, Impure* impure) const +void RecursiveStream::cleanupLevel(Request* request, Impure* impure) const { Impure* const saveImpure = request->getImpure(m_saveOffset); diff --git a/src/jrd/recsrc/SingularStream.cpp b/src/jrd/recsrc/SingularStream.cpp index 45e3f0b03e..5e66c943b7 100644 --- a/src/jrd/recsrc/SingularStream.cpp +++ b/src/jrd/recsrc/SingularStream.cpp @@ -44,7 +44,7 @@ SingularStream::SingularStream(CompilerScratch* csb, RecordSource* next) void SingularStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -54,7 +54,7 @@ void SingularStream::open(thread_db* tdbb) const void SingularStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -72,7 +72,7 @@ bool SingularStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -92,7 +92,7 @@ bool SingularStream::getRecord(thread_db* tdbb) const void SingularStream::doGetRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); const FB_SIZE_T streamCount = m_streams.getCount(); @@ -160,7 +160,7 @@ void SingularStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void SingularStream::invalidateRecords(jrd_req* request) const +void SingularStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/SkipRowsStream.cpp b/src/jrd/recsrc/SkipRowsStream.cpp index 6a22ea099f..3ee32787f3 100644 --- a/src/jrd/recsrc/SkipRowsStream.cpp +++ b/src/jrd/recsrc/SkipRowsStream.cpp @@ -46,7 +46,7 @@ SkipRowsStream::SkipRowsStream(CompilerScratch* csb, RecordSource* next, ValueEx void SkipRowsStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -66,7 +66,7 @@ void SkipRowsStream::open(thread_db* tdbb) const void SkipRowsStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -84,7 +84,7 @@ bool SkipRowsStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -131,7 +131,7 @@ void SkipRowsStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void SkipRowsStream::invalidateRecords(jrd_req* request) const +void SkipRowsStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index 9c160508ad..b74761251e 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -52,7 +52,7 @@ SortedStream::SortedStream(CompilerScratch* csb, RecordSource* next, SortMap* ma void SortedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -67,7 +67,7 @@ void SortedStream::open(thread_db* tdbb) const void SortedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -88,7 +88,7 @@ bool SortedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -150,7 +150,7 @@ void SortedStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void SortedStream::invalidateRecords(jrd_req* request) const +void SortedStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -162,7 +162,7 @@ void SortedStream::nullRecords(thread_db* tdbb) const Sort* SortedStream::init(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); m_next->open(tdbb); ULONG records = 0; @@ -310,7 +310,7 @@ bool SortedStream::compareKeys(const UCHAR* p, const UCHAR* q) const UCHAR* SortedStream::getData(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); ULONG* data = nullptr; @@ -319,7 +319,7 @@ UCHAR* SortedStream::getData(thread_db* tdbb) const return reinterpret_cast(data); } -void SortedStream::mapData(thread_db* tdbb, jrd_req* request, UCHAR* data) const +void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const { StreamType stream = INVALID_STREAM; dsc from, to; diff --git a/src/jrd/recsrc/Union.cpp b/src/jrd/recsrc/Union.cpp index 1f3692ea50..95c9179bbe 100644 --- a/src/jrd/recsrc/Union.cpp +++ b/src/jrd/recsrc/Union.cpp @@ -61,7 +61,7 @@ Union::Union(CompilerScratch* csb, StreamType stream, void Union::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -82,7 +82,7 @@ void Union::open(thread_db* tdbb) const void Union::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -101,7 +101,7 @@ bool Union::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -144,7 +144,7 @@ bool Union::getRecord(thread_db* tdbb) const bool Union::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_count >= m_args.getCount()) @@ -155,7 +155,7 @@ bool Union::refetchRecord(thread_db* tdbb) const bool Union::lockRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_count >= m_args.getCount()) @@ -197,7 +197,7 @@ void Union::markRecursive() m_args[i]->markRecursive(); } -void Union::invalidateRecords(jrd_req* request) const +void Union::invalidateRecords(Request* request) const { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->invalidateRecords(request); diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 3b56c467ae..2b7d88c7df 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -45,7 +45,7 @@ VirtualTableScan::VirtualTableScan(CompilerScratch* csb, const string& alias, void VirtualTableScan::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -60,7 +60,7 @@ void VirtualTableScan::open(thread_db* tdbb) const void VirtualTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -74,7 +74,7 @@ bool VirtualTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index b715c34127..67449b60e1 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -61,14 +61,14 @@ namespace void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level) const; void markRecursive(); - void invalidateRecords(jrd_req* request) const; + void invalidateRecords(Request* request) const; void findUsedStreams(StreamList& streams, bool expandAll) const; void nullRecords(thread_db* tdbb) const; void locate(thread_db* tdbb, FB_UINT64 position) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_position = position; } @@ -78,7 +78,7 @@ namespace return m_next->getCount(tdbb); } - FB_UINT64 getPosition(jrd_req* request) const + FB_UINT64 getPosition(Request* request) const { Impure* const impure = request->getImpure(m_impure); return impure->irsb_position; @@ -98,7 +98,7 @@ namespace void BufferedStreamWindow::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -107,7 +107,7 @@ namespace void BufferedStreamWindow::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -119,7 +119,7 @@ namespace bool BufferedStreamWindow::getRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -158,7 +158,7 @@ namespace m_next->findUsedStreams(streams, expandAll); } - void BufferedStreamWindow::invalidateRecords(jrd_req* request) const + void BufferedStreamWindow::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -335,7 +335,7 @@ WindowedStream::WindowedStream(thread_db* tdbb, Optimizer* opt, void WindowedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -346,7 +346,7 @@ void WindowedStream::open(thread_db* tdbb) const void WindowedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -364,7 +364,7 @@ bool WindowedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -397,7 +397,7 @@ void WindowedStream::markRecursive() m_joinedStream->markRecursive(); } -void WindowedStream::invalidateRecords(jrd_req* request) const +void WindowedStream::invalidateRecords(Request* request) const { m_joinedStream->invalidateRecords(request); } @@ -509,7 +509,7 @@ void WindowedStream::WindowStream::open(thread_db* tdbb) const { BaseAggWinStream::open(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = getImpure(request); impure->partitionBlock.startPosition = impure->partitionBlock.endPosition = @@ -533,7 +533,7 @@ void WindowedStream::WindowStream::open(thread_db* tdbb) const void WindowedStream::WindowStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_flags & irsb_open) @@ -546,7 +546,7 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = getImpure(request); @@ -893,7 +893,7 @@ void WindowedStream::WindowStream::nullRecords(thread_db* tdbb) const m_next->nullRecords(tdbb); } -const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* request, +const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, Request* request, const Frame* frame, impure_value_ex* impureValue) const { dsc* desc = EVL_expr(tdbb, request, frame->value); @@ -928,7 +928,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* } } -SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure, +SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, Request* request, Impure* impure, const Frame* frame, const dsc* offsetDesc, SINT64 position) const { if (m_order->expressions.getCount() != 1) @@ -1044,7 +1044,7 @@ SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, jrd_req* // ------------------------------ SlidingWindow::SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, - jrd_req* request, + Request* request, FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd, FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd) : tdbb(aTdbb), // Note: instantiate the class only as local variable diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index e2d08454bc..03d2427cec 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -204,7 +204,7 @@ namespace class LocalThreadContext { public: - LocalThreadContext(thread_db* tdbb, jrd_tra* tra, jrd_req* req = NULL) + LocalThreadContext(thread_db* tdbb, jrd_tra* tra, Request* req = NULL) : m_tdbb(tdbb) { tdbb->setTransaction(tra); diff --git a/src/jrd/replication/Applier.h b/src/jrd/replication/Applier.h index 2be2e54cc2..a0cd28cd76 100644 --- a/src/jrd/replication/Applier.h +++ b/src/jrd/replication/Applier.h @@ -125,7 +125,7 @@ namespace Jrd public: Applier(Firebird::MemoryPool& pool, const Firebird::PathName& database, - Jrd::jrd_req* request) + Jrd::Request* request) : PermanentStorage(pool), m_txnMap(pool), m_database(pool, database), m_request(request), m_bitmap(FB_NEW_POOL(pool) RecordBitmap(pool)), m_record(NULL) @@ -150,7 +150,7 @@ namespace Jrd private: TransactionMap m_txnMap; const Firebird::PathName m_database; - jrd_req* m_request; + Request* m_request; Firebird::AutoPtr m_bitmap; Record* m_record; JReplicator* m_interface; diff --git a/src/jrd/req.h b/src/jrd/req.h index 79299f3f92..be5a82a55c 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -165,7 +165,7 @@ private: // request block -class jrd_req : public pool_alloc +class Request : public pool_alloc { private: class TimeStampCache @@ -266,7 +266,7 @@ private: }; public: - jrd_req(Attachment* attachment, /*const*/ Statement* aStatement, + Request(Attachment* attachment, /*const*/ Statement* aStatement, Firebird::MemoryStats* parent_stats) : statement(aStatement), req_pool(statement->pool), @@ -358,10 +358,10 @@ public: // Transaction pointer and doubly linked list pointers for requests in this // transaction. Maintained by TRA_attach_request/TRA_detach_request. jrd_tra* req_transaction; - jrd_req* req_tra_next; - jrd_req* req_tra_prev; + Request* req_tra_next; + Request* req_tra_prev; - jrd_req* req_caller; // Caller of this request + Request* req_caller; // Caller of this request // This field may be used to reconstruct the whole call stack TempBlobIdTree req_blobs; // Temporary BLOBs owned by this request const StmtNode* req_message; // Current message for send/receive @@ -388,7 +388,7 @@ public: Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time SINT64 req_fetch_rowcount; // Total number of rows returned by this request - jrd_req* req_proc_caller; // Procedure's caller request + Request* req_proc_caller; // Procedure's caller request const ValueListNode* req_proc_inputs; // and its node with input parameters TraNumber req_conflict_txn; // Transaction number for update conflict in read consistency mode @@ -405,7 +405,7 @@ public: // Fields to support read consistency in READ COMMITTED transactions struct snapshot_data { - jrd_req* m_owner; + Request* m_owner; SnapshotHandle m_handle; CommitNumber m_number; diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 8f20769993..63acfbec28 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -115,10 +115,10 @@ static const UCHAR sweep_tpb[] = }; -jrd_req* TRA_get_prior_request(thread_db* tdbb) +Request* TRA_get_prior_request(thread_db* tdbb) { // See if there is any request right above us in the call stack - jrd_req* org_request; + Request* org_request; thread_db* jrd_ctx = tdbb; do { @@ -143,7 +143,7 @@ jrd_req* TRA_get_prior_request(thread_db* tdbb) return org_request; } -void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) +void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::Request* request) { // This function is called whenever request is started in a transaction. // Setup context to preserve read consistency in READ COMMITTED transactions. @@ -158,7 +158,7 @@ void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) return; // See if there is any request right above us in the call stack - jrd_req* org_request = TRA_get_prior_request(tdbb); + Request* org_request = TRA_get_prior_request(tdbb); if (org_request && org_request->req_transaction == transaction) { @@ -179,7 +179,7 @@ void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) } -void TRA_release_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) +void TRA_release_request_snapshot(Jrd::thread_db* tdbb, Jrd::Request* request) { // This function is called whenever request has completed processing // in a transaction (normally or abnormally) @@ -199,7 +199,7 @@ void TRA_release_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) } -void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request) +void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::Request* request) { // When request finishes normally transaction reference is not cleared. // Then if afterwards request is restarted TRA_attach_request is called again. @@ -227,7 +227,7 @@ void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request) transaction->tra_requests = request; } -void TRA_detach_request(Jrd::jrd_req* request) +void TRA_detach_request(Jrd::Request* request) { if (!request->req_transaction) { @@ -1640,12 +1640,12 @@ int TRA_snapshot_state(thread_db* tdbb, const jrd_tra* trans, TraNumber number, if ((trans->tra_flags & TRA_read_consistency) && state == tra_committed) { // GC thread accesses data directly without any request - if (jrd_req* current_request = tdbb->getRequest()) + if (Request* current_request = tdbb->getRequest()) { // Notes: // 1) There is no request snapshot when we build expression index // 2) Disable read committed snapshot after we encountered update conflict - jrd_req* snapshot_request = current_request->req_snapshot.m_owner; + Request* snapshot_request = current_request->req_snapshot.m_owner; if (snapshot_request && !(snapshot_request->req_flags & req_update_conflict)) { if (stateCn > snapshot_request->req_snapshot.m_number) @@ -2527,15 +2527,15 @@ static void restart_requests(thread_db* tdbb, jrd_tra* trans) **************************************/ SET_TDBB(tdbb); - for (jrd_req** i = trans->tra_attachment->att_requests.begin(); + for (Request** i = trans->tra_attachment->att_requests.begin(); i != trans->tra_attachment->att_requests.end(); ++i) { - Array& requests = (*i)->getStatement()->requests; + Array& requests = (*i)->getStatement()->requests; - for (jrd_req** j = requests.begin(); j != requests.end(); ++j) + for (Request** j = requests.begin(); j != requests.end(); ++j) { - jrd_req* request = *j; + Request* request = *j; if (request && request->req_transaction) { diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 19f265dcc7..1f705424b4 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -85,7 +85,7 @@ struct BlobIndex { ULONG bli_temp_id; bool bli_materialized; - jrd_req* bli_request; + Request* bli_request; union { bid bli_blob_id; // ID of materialized blob @@ -286,7 +286,7 @@ public: SSHORT tra_lock_timeout; // in seconds, -1 means infinite, 0 means NOWAIT ULONG tra_next_blob_id; // ID of the previous blob or array created in this transaction const ISC_TIMESTAMP_TZ tra_timestamp; // transaction start time - jrd_req* tra_requests; // Doubly linked list of requests active in this transaction + Request* tra_requests; // Doubly linked list of requests active in this transaction MonitoringSnapshot* tra_mon_snapshot; // Database state snapshot (for monitoring purposes) RuntimeStatistics tra_stats; Firebird::Array tra_open_cursors; diff --git a/src/jrd/tra_proto.h b/src/jrd/tra_proto.h index 8a0efbf58a..3c2d4b0ebb 100644 --- a/src/jrd/tra_proto.h +++ b/src/jrd/tra_proto.h @@ -61,11 +61,11 @@ int TRA_state(const UCHAR*, TraNumber oldest, TraNumber number); void TRA_sweep(Jrd::thread_db* tdbb); void TRA_update_counters(Jrd::thread_db*, Jrd::Database*); int TRA_wait(Jrd::thread_db* tdbb, Jrd::jrd_tra* trans, TraNumber number, Jrd::jrd_tra::wait_t wait); -void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request); -void TRA_detach_request(Jrd::jrd_req* request); -void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request); -void TRA_release_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request); -Jrd::jrd_req* TRA_get_prior_request(Jrd::thread_db*); +void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::Request* request); +void TRA_detach_request(Jrd::Request* request); +void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::Request* request); +void TRA_release_request_snapshot(Jrd::thread_db*, Jrd::Request* request); +Jrd::Request* TRA_get_prior_request(Jrd::thread_db*); void TRA_shutdown_sweep(); #endif // JRD_TRA_PROTO_H diff --git a/src/jrd/trace/TraceJrdHelpers.h b/src/jrd/trace/TraceJrdHelpers.h index 882b6f5d6c..61a5589fe8 100644 --- a/src/jrd/trace/TraceJrdHelpers.h +++ b/src/jrd/trace/TraceJrdHelpers.h @@ -94,7 +94,7 @@ private: class TraceProcExecute { public: - TraceProcExecute(thread_db* tdbb, jrd_req* request, jrd_req* caller, const ValueListNode* inputs) : + TraceProcExecute(thread_db* tdbb, Request* request, Request* caller, const ValueListNode* inputs) : m_tdbb(tdbb), m_request(request) { @@ -161,14 +161,14 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; }; class TraceProcFetch { public: - TraceProcFetch(thread_db* tdbb, jrd_req* request) : + TraceProcFetch(thread_db* tdbb, Request* request) : m_tdbb(tdbb), m_request(request) { @@ -219,7 +219,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; }; @@ -227,7 +227,7 @@ private: class TraceFuncExecute { public: - TraceFuncExecute(thread_db* tdbb, jrd_req* request, jrd_req* caller, + TraceFuncExecute(thread_db* tdbb, Request* request, Request* caller, const UCHAR* inMsg, ULONG inMsgLength) : m_tdbb(tdbb), m_request(request), @@ -301,7 +301,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; const UCHAR* m_inMsg; ULONG m_inMsgLength; SINT64 m_start_clock; @@ -311,7 +311,7 @@ private: class TraceTrigExecute { public: - TraceTrigExecute(thread_db* tdbb, jrd_req* trigger, int which_trig) : + TraceTrigExecute(thread_db* tdbb, Request* trigger, int which_trig) : m_tdbb(tdbb), m_request(trigger), m_which_trig(which_trig) @@ -367,7 +367,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; const int m_which_trig; }; @@ -437,7 +437,7 @@ private: class TraceBlrExecute { public: - TraceBlrExecute(thread_db* tdbb, jrd_req* request) : + TraceBlrExecute(thread_db* tdbb, Request* request) : m_tdbb(tdbb), m_request(request) { @@ -490,7 +490,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; }; diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index ec47e03038..ed661a8a76 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -342,7 +342,7 @@ private: class TraceDscFromValues : public TraceDescriptors { public: - TraceDscFromValues(Firebird::MemoryPool& pool, jrd_req* request, const ValueListNode* params) : + TraceDscFromValues(Firebird::MemoryPool& pool, Request* request, const ValueListNode* params) : TraceDescriptors(pool), m_request(request), m_params(params) @@ -352,7 +352,7 @@ protected: void fillParams(); private: - jrd_req* m_request; + Request* m_request; const ValueListNode* m_params; }; @@ -402,7 +402,7 @@ class TraceProcedureImpl : public Firebird::AutoIface > { public: - TraceProcedureImpl(jrd_req* request, Firebird::PerformanceInfo* perf) : + TraceProcedureImpl(Request* request, Firebird::PerformanceInfo* perf) : m_request(request), m_perf(perf), m_inputs(*getDefaultMemoryPool(), request->req_proc_caller, request->req_proc_inputs), @@ -420,7 +420,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; }; private: - jrd_req* const m_request; + Request* const m_request; Firebird::PerformanceInfo* const m_perf; TraceDscFromValues m_inputs; Firebird::string m_name; @@ -431,7 +431,7 @@ class TraceFunctionImpl : public Firebird::AutoIface > { public: - TraceFunctionImpl(jrd_req* request, Firebird::ITraceParams* inputs, Firebird::PerformanceInfo* perf, const dsc* value) : + TraceFunctionImpl(Request* request, Firebird::ITraceParams* inputs, Firebird::PerformanceInfo* perf, const dsc* value) : m_request(request), m_perf(perf), m_inputs(inputs), @@ -450,7 +450,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; }; private: - jrd_req* const m_request; + Request* const m_request; Firebird::PerformanceInfo* const m_perf; Firebird::ITraceParams* m_inputs; TraceDscFromDsc m_value; @@ -462,7 +462,7 @@ class TraceTriggerImpl : public Firebird::AutoIface > { public: - TraceTriggerImpl(const jrd_req* trig, SSHORT which, Firebird::PerformanceInfo* perf) : + TraceTriggerImpl(const Request* trig, SSHORT which, Firebird::PerformanceInfo* perf) : m_trig(trig), m_which(which), m_perf(perf) @@ -476,7 +476,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; } private: - const jrd_req* const m_trig; + const Request* const m_trig; const SSHORT m_which; Firebird::PerformanceInfo* const m_perf; }; diff --git a/src/jrd/val.h b/src/jrd/val.h index 91e024f0c5..e44b17a140 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -55,7 +55,7 @@ namespace Jrd { class ArrayField; class blb; -class jrd_req; +class Request; class jrd_tra; // Various structures in the impure area @@ -254,7 +254,7 @@ public: blb* arr_blob; // Blob for data access jrd_tra* arr_transaction; // Parent transaction block ArrayField* arr_next; // Next array in transaction - jrd_req* arr_request; // request + Request* arr_request; // request SLONG arr_effective_length; // Length of array instance USHORT arr_desc_length; // Length of array descriptor ULONG arr_temp_id; // Temporary ID for open array inside the transaction diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index e01e08971c..5ecbdfc3f3 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -161,7 +161,7 @@ const int PREPARE_LOCKERR = 3; static int prepare_update(thread_db*, jrd_tra*, TraNumber commit_tid_read, record_param*, record_param*, record_param*, PageStack&, bool); -static void protect_system_table_insert(thread_db* tdbb, const jrd_req* req, const jrd_rel* relation, +static void protect_system_table_insert(thread_db* tdbb, const Request* req, const jrd_rel* relation, bool force_flag = false); static void protect_system_table_delupd(thread_db* tdbb, const jrd_rel* relation, const char* operation, bool force_flag = false); @@ -223,7 +223,7 @@ static bool assert_gc_enabled(const jrd_tra* transaction, const jrd_rel* relatio inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation, const char* op) { const Attachment* const attachment = tdbb->getAttachment(); - const jrd_req* const request = tdbb->getRequest(); + const Request* const request = tdbb->getRequest(); if (relation->isSystem() && attachment->isGbak() && !(attachment->att_flags & ATT_creator) && !request->hasInternalStatement()) @@ -1451,7 +1451,7 @@ void VIO_data(thread_db* tdbb, record_param* rpb, MemoryPool* pool) } -static bool check_prepare_result(int prepare_result, jrd_tra* transaction, jrd_req* request, record_param* rpb) +static bool check_prepare_result(int prepare_result, jrd_tra* transaction, Request* request, record_param* rpb) { /************************************** * @@ -1469,7 +1469,7 @@ static bool check_prepare_result(int prepare_result, jrd_tra* transaction, jrd_r if (prepare_result == PREPARE_OK) return true; - jrd_req* top_request = request->req_snapshot.m_owner; + Request* top_request = request->req_snapshot.m_owner; const bool restart_ready = top_request && (top_request->req_flags & req_restart_ready); @@ -1520,7 +1520,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) MetaName object_name, package_name; SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_rel* relation = rpb->rpb_relation; #ifdef VIO_DEBUG @@ -3476,7 +3476,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) * **************************************/ SET_TDBB(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_rel* relation = rpb->rpb_relation; DeferredWork* work = NULL; @@ -4081,7 +4081,7 @@ bool VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction) case PREPARE_DELETE: if ((transaction->tra_flags & TRA_read_consistency)) { - jrd_req* top_request = tdbb->getRequest()->req_snapshot.m_owner; + Request* top_request = tdbb->getRequest()->req_snapshot.m_owner; if (top_request && !(top_request->req_flags & req_update_conflict)) { if (!(top_request->req_flags & req_restart_ready)) @@ -5116,7 +5116,7 @@ static void invalidate_cursor_records(jrd_tra* transaction, record_param* mod_rp **************************************/ fb_assert(mod_rpb && mod_rpb->rpb_relation); - for (jrd_req* request = transaction->tra_requests; request; request = request->req_tra_next) + for (Request* request = transaction->tra_requests; request; request = request->req_tra_next) { if (request->req_flags & req_active) { @@ -5870,7 +5870,7 @@ static int prepare_update( thread_db* tdbb, static void protect_system_table_insert(thread_db* tdbb, - const jrd_req* request, + const Request* request, const jrd_rel* relation, bool force_flag) { @@ -5918,7 +5918,7 @@ static void protect_system_table_delupd(thread_db* tdbb, * **************************************/ const Attachment* const attachment = tdbb->getAttachment(); - const jrd_req* const request = tdbb->getRequest(); + const Request* const request = tdbb->getRequest(); if (!force_flag) { From 260308264fc3dc3bd964b300628aebfae8f2685f Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Mon, 14 Feb 2022 00:05:39 +0000 Subject: [PATCH 077/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index ee96aa6de1..6faac09ca1 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:402 + FORMAL BUILD NUMBER:403 */ -#define PRODUCT_VER_STRING "5.0.0.402" -#define FILE_VER_STRING "WI-T5.0.0.402" -#define LICENSE_VER_STRING "WI-T5.0.0.402" -#define FILE_VER_NUMBER 5, 0, 0, 402 +#define PRODUCT_VER_STRING "5.0.0.403" +#define FILE_VER_STRING "WI-T5.0.0.403" +#define LICENSE_VER_STRING "WI-T5.0.0.403" +#define FILE_VER_NUMBER 5, 0, 0, 403 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "402" +#define FB_BUILD_NO "403" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index cbc392cecc..cc4d676c0b 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=402 +BuildNum=403 NowAt=`pwd` cd `dirname $0` From cace576208ffbc0fa0e2d96e55a0594574aebc8d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 15 Feb 2022 08:22:42 -0300 Subject: [PATCH 078/187] Fix assertion creating database with trace enabled. --- src/jrd/trace/TraceObjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 039b1cc7b7..741882585a 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -231,7 +231,7 @@ ITraceParams* TraceSQLStatementImpl::getInputs() void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() { - if (m_descs.getCount() || !m_params) + if (m_descs.getCount() || !m_params || m_params->getCount() == 0) return; if (!m_stmt->isDml()) From 87c8d493cf6b19d0a5822045c0f60a1686822f04 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 16 Feb 2022 00:05:47 +0000 Subject: [PATCH 079/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 6faac09ca1..a1e8e83973 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:403 + FORMAL BUILD NUMBER:404 */ -#define PRODUCT_VER_STRING "5.0.0.403" -#define FILE_VER_STRING "WI-T5.0.0.403" -#define LICENSE_VER_STRING "WI-T5.0.0.403" -#define FILE_VER_NUMBER 5, 0, 0, 403 +#define PRODUCT_VER_STRING "5.0.0.404" +#define FILE_VER_STRING "WI-T5.0.0.404" +#define LICENSE_VER_STRING "WI-T5.0.0.404" +#define FILE_VER_NUMBER 5, 0, 0, 404 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "403" +#define FB_BUILD_NO "404" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index cc4d676c0b..5ef1ddd9a3 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=403 +BuildNum=404 NowAt=`pwd` cd `dirname $0` From 7485bad77b488a9acfb43717612a1a5e2a51c0e3 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Thu, 10 Feb 2022 09:43:41 +0300 Subject: [PATCH 080/187] Ensure the variables are initialized before usage (even if they're only printed) --- src/jrd/optimizer/InnerJoin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp index 97fb72c13d..97ed2d6d76 100644 --- a/src/jrd/optimizer/InnerJoin.cpp +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -289,7 +289,7 @@ void InnerJoin::findBestOrder(unsigned position, streamFlags.add(innerStream->used); // Compute delta and total estimate cost to fetch this stream - double position_cost, position_cardinality, new_cost = 0, new_cardinality = 0; + double position_cost = 0, position_cardinality = 0, new_cost = 0, new_cardinality = 0; if (!plan) { From b1c32fa1cfcab82c3b68b905ca7f2de21a2ea302 Mon Sep 17 00:00:00 2001 From: Aleksei Mochalov Date: Wed, 16 Feb 2022 13:01:26 +0300 Subject: [PATCH 081/187] Fix #7129 alter package SQL SECURITY option reset --- src/dsql/PackageNodes.epp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 0c50cc49c7..da0ebd5637 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -439,6 +439,8 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* PKG.RDB$SQL_SECURITY.NULL = FALSE; PKG.RDB$SQL_SECURITY = ssDefiner.value ? FB_TRUE : FB_FALSE; } + else + PKG.RDB$SQL_SECURITY.NULL = TRUE; END_MODIFY owner = PKG.RDB$OWNER_NAME; From 935df87b7106a6a593ee7a8540566ef247233a4f Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 17 Feb 2022 00:05:50 +0000 Subject: [PATCH 082/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index a1e8e83973..637c59ca99 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:404 + FORMAL BUILD NUMBER:406 */ -#define PRODUCT_VER_STRING "5.0.0.404" -#define FILE_VER_STRING "WI-T5.0.0.404" -#define LICENSE_VER_STRING "WI-T5.0.0.404" -#define FILE_VER_NUMBER 5, 0, 0, 404 +#define PRODUCT_VER_STRING "5.0.0.406" +#define FILE_VER_STRING "WI-T5.0.0.406" +#define LICENSE_VER_STRING "WI-T5.0.0.406" +#define FILE_VER_NUMBER 5, 0, 0, 406 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "404" +#define FB_BUILD_NO "406" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 5ef1ddd9a3..eba9f971a9 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=404 +BuildNum=406 NowAt=`pwd` cd `dirname $0` From b985bcafa960458c3dcccfecdfabbfdf488cdb08 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Wed, 16 Feb 2022 22:46:51 +0200 Subject: [PATCH 083/187] This should fix bug #7134 : Firebird 4: Database page errors directly after gbak, which dissappear after some calls of gfix --- src/jrd/jrd.cpp | 1 + src/jrd/validation.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 03bc4ab561..3e12105072 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -2072,6 +2072,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch // Can't allow garbage collection during database validation. + AutoSetRestoreFlag noCleanup(&attachment->att_flags, ATT_no_cleanup, true); VIO_fini(tdbb); if (!VAL_validate(tdbb, options.dpb_verify)) diff --git a/src/jrd/validation.cpp b/src/jrd/validation.cpp index da6929ea54..f03551166b 100644 --- a/src/jrd/validation.cpp +++ b/src/jrd/validation.cpp @@ -724,6 +724,7 @@ static int validate(Firebird::UtilSvc* svc) { dpb.insertString(isc_dpb_trusted_auth, userName); } + dpb.insertTag(isc_dpb_no_garbage_collect); PathName expandedFilename; if (expandDatabaseName(dbName, expandedFilename, NULL)) From 9ce82b1009adf8b173c4a02a0ddeccb5a98d48f1 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 18 Feb 2022 00:05:47 +0000 Subject: [PATCH 084/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 637c59ca99..550f46ef3d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:406 + FORMAL BUILD NUMBER:407 */ -#define PRODUCT_VER_STRING "5.0.0.406" -#define FILE_VER_STRING "WI-T5.0.0.406" -#define LICENSE_VER_STRING "WI-T5.0.0.406" -#define FILE_VER_NUMBER 5, 0, 0, 406 +#define PRODUCT_VER_STRING "5.0.0.407" +#define FILE_VER_STRING "WI-T5.0.0.407" +#define LICENSE_VER_STRING "WI-T5.0.0.407" +#define FILE_VER_NUMBER 5, 0, 0, 407 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "406" +#define FB_BUILD_NO "407" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index eba9f971a9..f74404e53f 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=406 +BuildNum=407 NowAt=`pwd` cd `dirname $0` From f7c3c2ccd7e17a70fe039f560a4b931ba09d1061 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 18 Feb 2022 14:52:24 +0200 Subject: [PATCH 085/187] Front ported fix for bug #7128 : Incorrect error message with isc_sql_interprete() --- src/include/firebird/impl/msg/dsql.h | 2 +- src/remote/server/server.cpp | 6 ++++-- src/yvalve/why.cpp | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/include/firebird/impl/msg/dsql.h b/src/include/firebird/impl/msg/dsql.h index c909b42ed7..54a5339418 100644 --- a/src/include/firebird/impl/msg/dsql.h +++ b/src/include/firebird/impl/msg/dsql.h @@ -19,7 +19,7 @@ FB_IMPL_MSG(DSQL, 19, dsql_cursor_not_found, -502, "34", "000", "Cursor @1 is no FB_IMPL_MSG(DSQL, 20, dsql_cursor_exists, -502, "24", "000", "Cursor @1 already exists in the current context") FB_IMPL_MSG(DSQL, 21, dsql_cursor_rel_ambiguous, -502, "34", "000", "Relation @1 is ambiguous in cursor @2") FB_IMPL_MSG(DSQL, 22, dsql_cursor_rel_not_found, -502, "34", "000", "Relation @1 is not found in cursor @2") -FB_IMPL_MSG(DSQL, 23, dsql_cursor_not_open, -502, "24", "000", "Cursor is not open") +FB_IMPL_MSG(DSQL, 23, dsql_cursor_not_open, -504, "24", "000", "Cursor is not open") FB_IMPL_MSG(DSQL, 24, dsql_type_not_supp_ext_tab, -607, "HY", "004", "Data type @1 is not supported for EXTERNAL TABLES. Relation '@2', field '@3'") FB_IMPL_MSG(DSQL, 25, dsql_feature_not_supported_ods, -804, "0A", "000", "Feature not supported on ODS version older than @1.@2") FB_IMPL_MSG(DSQL, 26, primary_key_required, -660, "22", "000", "Primary key required on table @1") diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 1cc35256da..9f0892267c 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -3833,7 +3833,8 @@ ISC_STATUS rem_port::execute_statement(P_OP op, P_SQLDATA* sqldata, PACKET* send if (statement->rsr_cursor || statement->rsr_batch) { - Arg::Gds(isc_dsql_cursor_open_err).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)).raise(); } InternalMessageBuffer iMsgBuffer(sqldata->p_sqldata_blr.cstr_length, @@ -3989,7 +3990,8 @@ ISC_STATUS rem_port::fetch(P_SQLDATA * sqldata, PACKET* sendL, bool scroll) sqldata->p_sqldata_blr.cstr_address, msg_length, NULL); if (!msgBuffer.metadata) - Arg::Gds(isc_dsql_cursor_open_err).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)).raise(); cursor->setDelayedOutputFormat(&status_vector, msgBuffer.metadata); check(&status_vector); diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index 89b239a2af..f025da8049 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -1258,13 +1258,16 @@ namespace Why void checkCursorOpened() const { if (!statement || !statement->cursor) - Arg::Gds(isc_dsql_cursor_not_open).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_dsql_cursor_err) << + Arg::Gds(isc_dsql_cursor_not_open)).raise(); } void checkCursorClosed() const { if (statement && statement->cursor) - Arg::Gds(isc_dsql_cursor_open_err).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)).raise(); } IStatement* getInterface() From 001fd9efb0ae2dc4b31279223ee78af79558ba61 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 19 Feb 2022 00:05:27 +0000 Subject: [PATCH 086/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 550f46ef3d..bff6fb4c5d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:407 + FORMAL BUILD NUMBER:408 */ -#define PRODUCT_VER_STRING "5.0.0.407" -#define FILE_VER_STRING "WI-T5.0.0.407" -#define LICENSE_VER_STRING "WI-T5.0.0.407" -#define FILE_VER_NUMBER 5, 0, 0, 407 +#define PRODUCT_VER_STRING "5.0.0.408" +#define FILE_VER_STRING "WI-T5.0.0.408" +#define LICENSE_VER_STRING "WI-T5.0.0.408" +#define FILE_VER_NUMBER 5, 0, 0, 408 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "407" +#define FB_BUILD_NO "408" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index f74404e53f..aeeb6b15b6 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=407 +BuildNum=408 NowAt=`pwd` cd `dirname $0` From 2d6f2def9eb185f94d16c9745c137d20ec33f6ef Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Mon, 21 Feb 2022 13:52:36 +0300 Subject: [PATCH 087/187] Frontported fix for #7135: Firebird engine randomly fails when delivering mapping clear to other processes --- src/jrd/Mapping.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index 76ac4cac24..ed7b9d1222 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -867,13 +867,13 @@ private: { MappingHeader* sMem = sharedMemory->getHeader(); resetMap(sMem->databaseForReset, sMem->resetIndex); + p->flags &= ~MappingHeader::FLAG_DELIVER; MappingHeader::Process* cur = &sMem->process[sMem->currentProcess]; if (sharedMemory->eventPost(&cur->callbackEvent) != FB_SUCCESS) { (Arg::Gds(isc_map_event) << "POST").raise(); } - p->flags &= ~MappingHeader::FLAG_DELIVER; } if (startup) From 913d17ae3558c791f334969ffebe21f6f0f41f9b Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Tue, 22 Feb 2022 12:13:21 +0200 Subject: [PATCH 088/187] This should show value of _MSC_VER when build fails with new version of MSVC. Note, it is not documented so far for newly released MSVC 17.1. --- src/common/os/win32/mod_loader.cpp | 40 ++++++++++++++++++------------ 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/common/os/win32/mod_loader.cpp b/src/common/os/win32/mod_loader.cpp index 4276fccdb0..3efe30f284 100644 --- a/src/common/os/win32/mod_loader.cpp +++ b/src/common/os/win32/mod_loader.cpp @@ -87,26 +87,34 @@ public: memset(&ackd, 0, sizeof(ackd)); ackd.cbSize = sizeof(ackd); + const char* crtDll = +#if _MSC_VER == 1400 + "msvcr80.dll"; +#elif _MSC_VER == 1500 + "msvcr90.dll"; +#elif _MSC_VER == 1600 + "msvcr100.dll"; +#elif _MSC_VER == 1700 + "msvcr110.dll"; +#elif _MSC_VER == 1800 + "msvcr120.dll"; +#elif _MSC_VER >= 1900 && _MSC_VER <= 1930 + "vcruntime140.dll"; +#else + ""; + +#define TO_STR(x) #x +#define ERRSTR(x) "Unknown " #x " value: " TO_STR(x) ". Specify CRT DLL name here !" + + static_assert(false, ERRSTR(_MSC_VER)); +// #error Specify CRT DLL name here ! +#endif + // if CRT already present in some activation context then nothing to do if ((*mFindActCtxSectionString) (0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, -#if _MSC_VER == 1400 - "msvcr80.dll", -#elif _MSC_VER == 1500 - "msvcr90.dll", -#elif _MSC_VER == 1600 - "msvcr100.dll", -#elif _MSC_VER == 1700 - "msvcr110.dll", -#elif _MSC_VER == 1800 - "msvcr120.dll", -#elif _MSC_VER >= 1900 && _MSC_VER <= 1930 - "vcruntime140.dll", -#else - #error Specify CRT DLL name here ! -#endif - &ackd)) + crtDll, &ackd)) { return; } From 7edd875cb50d15e935bf22eb07079a6a0388581b Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Tue, 22 Feb 2022 12:36:50 +0200 Subject: [PATCH 089/187] Fixed build with MSVC 17.1 Assume _MSC_VER will be increased to be >= 2000 when\if VC CRT library get new version number in suffix. --- src/common/os/win32/mod_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/os/win32/mod_loader.cpp b/src/common/os/win32/mod_loader.cpp index 3efe30f284..5173b00daa 100644 --- a/src/common/os/win32/mod_loader.cpp +++ b/src/common/os/win32/mod_loader.cpp @@ -98,7 +98,7 @@ public: "msvcr110.dll"; #elif _MSC_VER == 1800 "msvcr120.dll"; -#elif _MSC_VER >= 1900 && _MSC_VER <= 1930 +#elif _MSC_VER >= 1900 && _MSC_VER < 2000 "vcruntime140.dll"; #else ""; From 24772275b20be2e982a90b7e0b177d2c12d3ab1c Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 22 Feb 2022 20:20:01 +0300 Subject: [PATCH 090/187] Improved use of shutdown error codes a little --- src/jrd/jrd.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 3e12105072..fd2edf30ee 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -430,7 +430,7 @@ public: { if (getUnloadDetector()->unloadStarted()) { - Arg::Gds(isc_shutdown).raise(); + Arg::Gds(isc_att_shut_engine).raise(); } IPluginBase* p = FB_NEW JProvider(factoryParameter); @@ -2031,11 +2031,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (!allow_access) { // Note we throw exception here when entering full-shutdown mode - Arg::Gds v(isc_shutdown); - v << Arg::Str(org_filename); - if (attachment->att_user && attachment->att_user->testFlag(USR_mapdown)) - v << Arg::Gds(isc_map_down); - ERR_post(v); + ERR_post(Arg::Gds(isc_shutdown) << org_filename); } } From 1f36112fb4704d6f65dda3e6614c0607285b820a Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 23 Feb 2022 00:05:21 +0000 Subject: [PATCH 091/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index bff6fb4c5d..22f7c81ef6 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:408 + FORMAL BUILD NUMBER:412 */ -#define PRODUCT_VER_STRING "5.0.0.408" -#define FILE_VER_STRING "WI-T5.0.0.408" -#define LICENSE_VER_STRING "WI-T5.0.0.408" -#define FILE_VER_NUMBER 5, 0, 0, 408 +#define PRODUCT_VER_STRING "5.0.0.412" +#define FILE_VER_STRING "WI-T5.0.0.412" +#define LICENSE_VER_STRING "WI-T5.0.0.412" +#define FILE_VER_NUMBER 5, 0, 0, 412 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "408" +#define FB_BUILD_NO "412" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index aeeb6b15b6..e0288a4490 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=408 +BuildNum=412 NowAt=`pwd` cd `dirname $0` From d6179920c0362c66929d19f76248d69d59662779 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 25 Feb 2022 11:16:47 +0300 Subject: [PATCH 092/187] Restore the manual sorting, as qsort() sorts equal values differently across Windows and Linux --- src/jrd/optimizer/InnerJoin.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp index 97ed2d6d76..4c4f7eb73d 100644 --- a/src/jrd/optimizer/InnerJoin.cpp +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -121,20 +121,21 @@ void InnerJoin::calculateStreamInfo() // Unless PLAN is enforced, sort the streams based on independency and cost if (!plan && (innerStreams.getCount() > 1)) { - auto compare = [] (const void* a, const void* b) + StreamInfoList tempStreams; + + for (const auto innerStream : innerStreams) { - const auto first = *static_cast(a); - const auto second = *static_cast(b); + FB_SIZE_T index = 0; + for (; index < tempStreams.getCount(); index++) + { + if (StreamInfo::cheaperThan(innerStream, tempStreams[index])) + break; + } + tempStreams.insert(index, innerStream); + } - if (StreamInfo::cheaperThan(first, second)) - return -1; - if (StreamInfo::cheaperThan(second, first)) - return 1; - - return 0; - }; - - qsort(innerStreams.begin(), innerStreams.getCount(), sizeof(StreamInfo*), compare); + // Finally update the innerStreams with the sorted streams + innerStreams = tempStreams; } } From c6ae7ff3d400fd20e97d2cecfd076c12e10c0d19 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 25 Feb 2022 11:26:13 +0300 Subject: [PATCH 093/187] This should fix #7137: Bad plan (HASH instead of JOIN) is chosen for some inner joins --- src/jrd/optimizer/InnerJoin.cpp | 55 +++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp index 4c4f7eb73d..76435cf65e 100644 --- a/src/jrd/optimizer/InnerJoin.cpp +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -95,9 +95,7 @@ void InnerJoin::calculateStreamInfo() for (auto innerStream : innerStreams) { streams.add(innerStream->stream); - - const auto tail = &csb->csb_rpt[innerStream->stream]; - tail->activate(); + csb->csb_rpt[innerStream->stream].activate(); Retrieval retrieval(tdbb, optimizer, innerStream->stream, false, false, sort, true); const auto candidate = retrieval.getInversion(); @@ -108,15 +106,40 @@ void InnerJoin::calculateStreamInfo() innerStream->baseUnique = candidate->unique; innerStream->baseNavigated = candidate->navigated; - tail->deactivate(); + csb->csb_rpt[innerStream->stream].deactivate(); } - StreamStateHolder stateHolder(csb, streams); - stateHolder.activate(); + // Collect dependencies between every pair of streams - // Collect stream inter-dependencies - for (const auto innerStream : innerStreams) - getIndexedRelationships(innerStream); + for (const auto baseStream : streams) + { + csb->csb_rpt[baseStream].activate(); + + for (const auto innerStream : innerStreams) + { + const StreamType testStream = innerStream->stream; + + if (baseStream != testStream) + { + csb->csb_rpt[testStream].activate(); + getIndexedRelationships(innerStream); + csb->csb_rpt[testStream].deactivate(); + } + } + + csb->csb_rpt[baseStream].deactivate(); + } + + // Collect more complex inter-dependencies (involving three and more streams), if any + + if (streams.getCount() > 2) + { + StreamStateHolder stateHolder(csb, streams); + stateHolder.activate(); + + for (const auto innerStream : innerStreams) + getIndexedRelationships(innerStream); + } // Unless PLAN is enforced, sort the streams based on independency and cost if (!plan && (innerStreams.getCount() > 1)) @@ -423,6 +446,20 @@ void InnerJoin::getIndexedRelationships(StreamInfo* testStream) if (baseStream->stream != testStream->stream && candidate->dependentFromStreams.exist(baseStream->stream)) { + // If the base stream already depends on the testing stream, don't store it again + bool found = false; + for (const auto& relationship : baseStream->indexedRelationships) + { + if (relationship.stream == testStream->stream) + { + found = true; + break; + } + } + + if (found) + continue; + // If we could use more conjunctions on the testing stream // with the base stream active as without the base stream // then the test stream has a indexed relationship with the base stream. From 2b2c918ae6f33132fd0b9979868cc36d515216a7 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 26 Feb 2022 00:05:26 +0000 Subject: [PATCH 094/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 22f7c81ef6..01f13dc89b 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:412 + FORMAL BUILD NUMBER:414 */ -#define PRODUCT_VER_STRING "5.0.0.412" -#define FILE_VER_STRING "WI-T5.0.0.412" -#define LICENSE_VER_STRING "WI-T5.0.0.412" -#define FILE_VER_NUMBER 5, 0, 0, 412 +#define PRODUCT_VER_STRING "5.0.0.414" +#define FILE_VER_STRING "WI-T5.0.0.414" +#define LICENSE_VER_STRING "WI-T5.0.0.414" +#define FILE_VER_NUMBER 5, 0, 0, 414 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "412" +#define FB_BUILD_NO "414" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index e0288a4490..338c658822 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=412 +BuildNum=414 NowAt=`pwd` cd `dirname $0` From 877a8443ddb72109e885c0477a4727b767860058 Mon Sep 17 00:00:00 2001 From: Paul Reeves Date: Wed, 22 Dec 2021 11:31:54 +0100 Subject: [PATCH 095/187] Forward port from v4 branch Fix #6917 Fix #6943 Fix #6979 --- .../arch-specific/win32/FirebirdInstall.iss | 35 +++++++++++++------ .../FirebirdInstallEnvironmentChecks.inc | 33 ++++++++++++++++- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/builds/install/arch-specific/win32/FirebirdInstall.iss b/builds/install/arch-specific/win32/FirebirdInstall.iss index 31a5b21954..a3ec4272c3 100644 --- a/builds/install/arch-specific/win32/FirebirdInstall.iss +++ b/builds/install/arch-specific/win32/FirebirdInstall.iss @@ -384,7 +384,7 @@ Filename: msiexec.exe; Parameters: "/qn /norestart /i ""{tmp}\vccrt{#msvc_runtim #endif ;Only register Firebird if we are installing AND configuring -Filename: {app}\instreg.exe; Parameters: "install "; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Components: ClientComponent; Flags: runminimized; Check: ConfigureFirebird; +Filename: {app}\instreg.exe; Parameters: "install "; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Components: ServerComponent; Flags: runminimized; Check: ConfigureFirebird; Filename: {app}\instclient.exe; Parameters: "install fbclient"; StatusMsg: {cm:instclientCopyFbClient}; MinVersion: {#MinVer}; Components: ClientComponent; Flags: runminimized; Check: CopyFBClientLib; Filename: {app}\instclient.exe; Parameters: "install gds32"; StatusMsg: {cm:instclientGenGds32}; MinVersion: {#MinVer}; Components: ClientComponent; Flags: runminimized; Check: CopyGds32 @@ -410,7 +410,7 @@ Filename: "{#MyAppURL}/afterinstall"; Description: "After installation - What Ne Root: HKLM; Subkey: SOFTWARE\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: Firebird; ValueData: ; Flags: uninsdeletevalue; Tasks: UseApplicationTask; Check: ConfigureFirebird; ;This doesn't seem to get cleared automatically by instreg on uninstall, so lets make sure of it -Root: HKLM; Subkey: "SOFTWARE\Firebird Project"; Flags: uninsdeletekeyifempty; Components: ClientComponent DevAdminComponent ServerComponent +Root: HKLM; Subkey: "SOFTWARE\Firebird Project"; Flags: uninsdeletekeyifempty; Components: ServerComponent ;Clean up Invalid registry entries from previous installs. Root: HKLM; Subkey: "SOFTWARE\FirebirdSQL"; ValueType: none; Flags: deletekey; @@ -476,7 +476,7 @@ Source: {#FilesDir}\firebird.exe; DestDir: {app}; Components: ServerComponent; F Source: {#FilesDir}\fb_lock_print.exe; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion Source: {#FilesDir}\ib_util.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion Source: {#FilesDir}\instclient.exe; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\instreg.exe; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\instreg.exe; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion Source: {#FilesDir}\instsvc.exe; DestDir: {app}; Components: ServerComponent; MinVersion: {#MinVer}; Flags: sharedfile ignoreversion Source: {#FilesDir}\isql.exe; DestDir: {app}; Components: DevAdminComponent; Flags: ignoreversion Source: {#FilesDir}\nbackup.exe; DestDir: {app}; Components: DevAdminComponent; Flags: ignoreversion @@ -487,14 +487,25 @@ Source: {#FilesDir}\fbclient.dll; DestDir: {app}; Components: ClientComponent; F Source: {#WOW64Dir}\fbclient.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: overwritereadonly sharedfile promptifolder {#SkipFileIfDevStatus} Source: {#WOW64Dir}\instclient.exe; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion {#SkipFileIfDevStatus} #endif -Source: {#FilesDir}\icuuc??.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\icuin??.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\icudt??.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\icudt*.dat; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icuuc??.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icuin??.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icudt??.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icudt*.dat; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +#if PlatformTarget == "x64" +Source: {#WOW64Dir}\icuuc??.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#WOW64Dir}\icuin??.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#WOW64Dir}\icudt??.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#WOW64Dir}\icudt*.dat; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +#endif + #if PlatformTarget =="Win32" Source: {#FilesDir}\fbrmclib.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion #endif -Source: {#FilesDir}\zlib1.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion + +Source: {#FilesDir}\zlib1.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +#if PlatformTarget == "x64" +Source: {#WOW64Dir}\zlib1.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +#endif ;Rules for installation of MS runtimes are simplified with MSVC10 ;We just install the runtimes into the install dir. @@ -542,9 +553,13 @@ Source: {#WOW64Dir}\lib\*.lib; DestDir: {app}\WOW64\lib; Components: DevAdminCom ;Source: {#FilesDir}\UDF\*.txt; DestDir: {app}\UDF; Components: ServerComponent; Flags: ignoreversion; Source: {#FilesDir}\plugins.conf; DestDir: {app}; Components: ServerComponent; Flags: ignoreversion; -Source: {#FilesDir}\plugins\*.dll; DestDir: {app}\plugins; Components: ServerComponent; Flags: ignoreversion; +Source: {#FilesDir}\plugins\*.dll; DestDir: {app}\plugins; Components: ServerComponent; Flags: ignoreversion; Check: IsServerInstall; +Source: {#FilesDir}\plugins\chacha.dll; DestDir: {app}\plugins; Components: ClientComponent; Flags: ignoreversion; Check: IsNotServerInstall; Source: {#FilesDir}\plugins\*.conf; DestDir: {app}\plugins; Components: ServerComponent; Flags: ignoreversion; Source: {#FilesDir}\plugins\udr\*.*; DestDir: {app}\plugins\udr; Components: ServerComponent; Flags: ignoreversion; +#if PlatformTarget == "x64" +Source: {#WOW64Dir}\plugins\chacha*.dll; DestDir: {app}\WOW64\plugins; Components: ClientComponent; Flags: ignoreversion; +#endif Source: {#FilesDir}\misc\*.*; DestDir: {app}\misc; Components: ServerComponent; Flags: ignoreversion createallsubdirs recursesubdirs ; @@ -575,7 +590,7 @@ Filename: {app}\instclient.exe; Parameters: " remove fbclient"; StatusMsg: {cm:i Filename: {app}\wow64\instclient.exe; Parameters: " remove gds32"; StatusMsg: {cm:instclientDecLibCountGds32}; MinVersion: {#MinVer}; Flags: runminimized 32bit; RunOnceId: RemoveGDS32x86 Filename: {app}\wow64\instclient.exe; Parameters: " remove fbclient"; StatusMsg: {cm:instclientDecLibCountFbClient}; MinVersion: {#MinVer}; Flags: runminimized 32bit; RunOnceId: RemoveFbClientx86 #endif -Filename: {app}\instreg.exe; Parameters: " remove"; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Flags: runminimized; RunOnceId: RemoveRegistryEntry +Filename: {app}\instreg.exe; Parameters: " remove"; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Components: ServerComponent; Flags: runminimized; RunOnceId: RemoveRegistryEntry [UninstallDelete] Type: files; Name: "{app}\*.lck" diff --git a/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc b/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc index d4a74a59ed..c4081472fd 100644 --- a/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc +++ b/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc @@ -890,9 +890,40 @@ begin end; +function IsServerInstall: Boolean; +var + SetupType: String; +begin + + // DOC NOTE - WizardSetupType is not well documented. If parameter is set to + // True the Description of the setup type is returned. (This is useless for us + // as our descriptions are I18n'ised. ) If set False the string declared in + // the TYPES section is returned. BUT LOWERCASED! Aargh!! + // To protect against future changes each side is the comparison is lowercased. + + SetupType := WizardSetupType ( false ); + if LowerCase( SetupType ) = LowerCase( 'ServerInstall' ) then + Result := true + else + Result := False; +end; + + +function IsNotServerInstall: Boolean; +begin + if IsServerInstall then + Result := False + else + Result := True; +end; + + function ConfigureFirebird: boolean; begin - result := (InstallAndConfigure AND Configure) = Configure; + if IsNotServerInstall then + Result := False + else + Result := (InstallAndConfigure AND Configure) = Configure; end; From 7f3494c9cfb1b9b74c757eed4e67333341b7b64c Mon Sep 17 00:00:00 2001 From: Paul Reeves Date: Mon, 28 Feb 2022 13:13:56 +0100 Subject: [PATCH 096/187] Add support for testing scripted client and devinst installs Update documentation Fix miscellaneous bugs. --- .../win32/test_installer/fbit-help.txt | 64 ++- .../win32/test_installer/fbit.bat | 507 +++++++++++++----- 2 files changed, 399 insertions(+), 172 deletions(-) diff --git a/builds/install/arch-specific/win32/test_installer/fbit-help.txt b/builds/install/arch-specific/win32/test_installer/fbit-help.txt index 67919143c3..7ec59d98e8 100644 --- a/builds/install/arch-specific/win32/test_installer/fbit-help.txt +++ b/builds/install/arch-specific/win32/test_installer/fbit-help.txt @@ -1,5 +1,3 @@ - - Firebird Binary Installer Test Harness HELP (Designed with TABS=4 and console width=120) fbit {PARAM [...]] @@ -11,15 +9,22 @@ will clean up from previous broken installs. CLEAN must the first parameter and subsequent parameters are ignored. Note: - It will run silently. - By default fbit installs Firebird according to the parameters passed and then - immediately uninstalls it. A copy of the install is made, along with the install and uninstall logs. + immediately uninstalls it. A copy of the install is made, along with the install and uninstall logs. + + REQUIREMENTS + ============ + The script will attempt to detect if the current cmd session has just built firebird. + If not you must set FBINST_EXEC in the environment prior to running this script. + + Be sure to check :SET_GLOBAL_ENV for hard-coded settings + Some knowledge of InnoSetup will be useful. See %FIREBIRD%/doc/installation_scripted.txt for more info. FBIT Specific Parameters ======================== - Param Name Value Passed Comment - ---------- ------------ ------- + Param Name Value Passed to fbit Comment + ---------- -------------------- ------- HELP - Displays this screen DRYRUN - Show what will be done. No changes are made NOARCHIVE - Disables copying of install dir to %USERPROFILE%\fbit @@ -28,61 +33,74 @@ SCRIPTED - Sets VERYSILENT, SP and NOMSG TESTNAME NameOfTestRun Optional. No spaces allowed. Used for storing test run details. - The following parameters are set by default. They are unset automatically when a conflicting parameter is passed. - Default Param Value Unset by - ------------- ------------- ---------- + Default Param Default Value set by fbit Unset by + ------------- ------------------------- ---------- INTERACTIVE True SCRIPTED INSTALLTYPE ServerInstall CLIENT or DEVINST SERVICE_TASK True APPTASK SUPERSERVER True CLASSICSERVER or SUPERCLASSIC - Firebird Installer specific Parameters ====================================== - Param Name Value passed Action when set + Param Name Value passed to installer Action when set ---------- ------------------------- --------------- COPYGDSLIB CopyFbClientAsGds32Task Copy fbclient to and rename to gds32 FORCE FORCE Force installation NOAUTOSTART NULL Does not set AutoStartTask - NOCOPYFBLIB CopyFbClientToSysTask Does not copy fbclient to + NOCOPYFBLIB - Does not copy fbclient to PASSWORD /SYSDBAPASSWORD=%ISC_PASSWORD% Changes SYSDBA password from masterkey See :SET_GLOBAL_ENV Installation Tasks ================== - Param Name Value passed Comment - ------------- ------------ --------------- + Param Name Value passed to /TASKS Comment + ------------- ---------------------- --------------- APPTASK UseApplicationTask Will not install as a service CLASSICSERVER UseClassicServerTask Will configure classic server SUPERCLASSIC UseSuperClassicTask Will configure super classic - Installation Types ================== - Param Name Value passed Comment - ------------ ------------ ------- + Param Name Value passed to /TYPE Comment + ------------ --------------------- ------- CLIENT ClientInstall Minimal working client install DEVINST DeveloperInstall Everything but the server. - + SERVER_INSTALL ServerInstall Uninstallation ============== - Param Name Value passed Comment - -------------- ------------ ------- + Param Name Value passed to uninstaller Comment + -------------- --------------------------- ------- CLEAN CLEAN Completely remove the firebird install Reset list of shared dll's in the registry - + Assumes installed version of Firebird matches %FIREBIRD_BASE_VER% set in fbit script. Generic InnoSetup parameters ============================ - Param Name Value passed Comment - ---------- ------------ ------- + Param Name Value passed to installer Comment + ---------- ------------------------- ------- NOMSG SUPPRESSMSGBOXES Suppress message boxes NOCANCEL NOCANCEL Prevents user cancelling install SILENT SILENT SP SP- Disables the This will install... prompt VERYSILENT VERYSILENT + + Examples + ======== + +o Run a scripted server install: + + fbit SCRIPTED + +o Clean up previous firebird install: + + fbit CLEAN + +o Test install of firebird client: + + fbit SCRIPTED CLIENT + -------------------------- End of Fbit Help Screen ---------------------------------------- diff --git a/builds/install/arch-specific/win32/test_installer/fbit.bat b/builds/install/arch-specific/win32/test_installer/fbit.bat index 92878a5a8e..f217d68792 100644 --- a/builds/install/arch-specific/win32/test_installer/fbit.bat +++ b/builds/install/arch-specific/win32/test_installer/fbit.bat @@ -18,7 +18,7 @@ :: scripted install of Firebird. It is designed to test almost all possible :: scriptable combinations and is thus far more complicated than a typical :: install script need be. However, it can be used for testing. Note that chosen -:: settings :: used for each test run are saved into an .inf file, along with a +:: settings used for each test run are saved into an .inf file, along with a :: log of the install run. @goto :MAIN %* @@ -27,26 +27,32 @@ ::======================================================= :SET_GLOBAL_ENV @call :SET_VERBOSE_IF_DEBUG_ON +::@call :SET_VERBOSE @if defined DEBUG @echo Entering %0 +:: Uncomment this if the default command prompt takes up too much space +::@PROMPT=fbit_prompt$G -:: FBINST_EXEC must point to the package we want to test... -if not defined FBINST_EXEC ( - rem - if we have just built firebird we can test the install immediately - if defined FBBUILD_FILE_ID ( - if defined FBBUILD_FILENAME_SUFFIX ( - @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%%FBBUILD_FILENAME_SUFFIX%.exe - ) else ( - @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%.exe - ) - ) +:: Set this to the location of the firebird installer you want to test. +::@set FBINST_EXEC=%USERPROFILE%\Desktop\Firebird-5.0.0.0000_x64.exe + +:: if we have just built firebird we can test the install immediately +if defined FBBUILD_FILE_ID ( + if defined FBBUILD_FILENAME_SUFFIX ( + @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%%FBBUILD_FILENAME_SUFFIX%.exe + ) else ( + @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%.exe + ) ) else ( +rem FBINST_EXEC must point to the package we want to test... + if not defined FBINST_EXEC ( rem Set the actual path and filename here - or set it in the environment before running fbit. - @set FBINST_EXEC=%USERPROFILE%\Desktop\Firebird-4.0.0.2311_0_x64_RC1.exe + set FBINST_EXEC=%USERPROFILE%\Desktop\Firebird-5.0.0.0000_0_x64_RC1.exe + ) ) :: This should be set dynamically, perhaps. But for now it is hard-coded. -@set FIREBIRD_BASE_VER=Firebird_4_0 +@set FIREBIRD_BASE_VER=Firebird_5_0 :: It is possible that successive installs into the same directory may :: generate different uninstallers but for now we hard code the default. @@ -73,14 +79,16 @@ if not defined FBINST_EXEC ( :: to read. @set SHOW_FINAL_CMD= -:: change as reqd, or comment out if ISC_PASSWORD is already set in your env -@set ISC_PASSWORD="secret" +:: change as reqd +@if not defined ISC_PASSWORD ( + @set ISC_PASSWORD="secret" +) @set TAB= & @if not defined DRYRUN ( - if not exist %FBINSTALLLOGDIR% @mkdir %FBINSTALLLOGDIR% >nul 2>nul - if not exist %FBINSTALLCOPYDIR% @mkdir %FBINSTALLCOPYDIR% >nul 2>nul + if not exist %FBINSTALLLOGDIR% ( @mkdir %FBINSTALLLOGDIR% >nul 2>nul ) + if not exist %FBINSTALLCOPYDIR% ( @mkdir %FBINSTALLCOPYDIR% >nul 2>nul ) ) @if defined DEBUG @echo Leaving %0 @@ -92,7 +100,8 @@ if not defined FBINST_EXEC ( ::======================================================= :GET_OPTS @call :SET_VERBOSE_IF_DEBUG_ON -if defined DEBUG @echo Entering %0 +::@call :SET_VERBOSE +@if defined DEBUG @echo Entering %0 :: Automatically parse the commandline and place all valid options into ENV VARS :: Courtesy of this link: @@ -111,22 +120,22 @@ if defined DEBUG @echo Entering %0 :: will end up as :: flagwithdefault=flag :: Basically all this means that these variables should not be passed to runtime: -:: INTERACTIVE INSTALL INSTALLTYPE SERVER SERVICE_TASK SUPERSERVER +:: INTERACTIVE INSTALL INSTALLTYPE SERVER_INSTALL SERVICE_TASK SUPERSERVER -set "options=APPTASK: CLASSICSERVER: CLEAN: CLIENT: CMD_PARAMS: COMPONENTS: COPYGDSLIB: DEVINST: DRYRUN: FINALCMD: FULL_CMD: FORCE: HELP: INTERACTIVE:1 INSTALL:1 INSTALLTYPE:ServerInstall NOARCHIVE: NOAUTOSTART: NOCANCEL: NOCOPYFBLIB: NOMSG: NOUNINSTALL: PASSWORD: RUN_TIMESTAMP: SCRIPTED: SERVER:1 SILENT: SP: SERVICE_TASK:1 SUPERCLASSIC: SUPERSERVER:1 TASK_LIST:UseSuperServerTask TESTNAME:"" UNINSTALL: VERYSILENT: XRESULT:0" -if defined VERBOSE @echo on +set "options=APPTASK: CLASSICSERVER: CLEAN: CLIENT: CMD_PARAMS: COMPONENTS: COPYGDSLIB: DEVINST: DRYRUN: FINALCMD: FULL_CMD: FORCE: HELP: INTERACTIVE:1 INSTALL:1 INSTALLTYPE:ServerInstall NOARCHIVE: NOAUTOSTART: NOCANCEL: NOCOPYFBLIB: NOMSG: NOUNINSTALL: PASSWORD: RUN_TIMESTAMP: SCRIPTED: SERVER_INSTALL:1 SILENT: SP: SERVICE_TASK: SUPERCLASSIC: SUPERSERVER: TASK_LIST: TESTNAME:"" UNINSTALL: VERYSILENT: XRESULT:0" +@if defined VERBOSE @echo on for %%O in (%options%) do ( for /f "tokens=1,* delims=:" %%A in ("%%O") do ( set "%%A=%%~B" ) ) -if defined VERBOSE ( - call :PRINT_VARS +@if defined VERBOSE ( + @call :PRINT_VARS if NOT defined DEBUG pause ) :loop -if not "%~1"=="" ( +@if not "%~1"=="" ( set "test=!options:*%~1:=! " if "!test!"=="!options! " ( echo Error: Invalid option %~1 @@ -144,7 +153,8 @@ if not "%~1"=="" ( goto :loop ) -if defined DEBUG @echo Leaving %0 +@if defined VERBOSE ( @call :PRINT_VARS ) +@if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF ::======================================================= @@ -198,46 +208,100 @@ rem We now have everything we need for uninstall so jump to the end goto :SET_CMD_PARAMS ) -:: Fix up any incompatible assignments +:: Fix up any incompatible assignments ::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -@if defined CLASSICSERVER ( set SUPERSERVER=& set SUPERCLASSIC=) -@if defined SUPERCLASSIC ( set SUPERSERVER=& set CLASSICSERVER=) -:: Theoretically this next line is redundant -@if defined SUPERSERVER ( set SUPERCLASSIC=& set CLASSICSERVER=) +@if defined CLIENT ( + set INSTALLTYPE=ClientInstall + set DEVINST= + set SERVER_INSTALL= + set TASK_LIST= + set SERVICE_TASK= + set CLASSICSERVER= + set SUPERCLASSIC= + set SUPERSERVER= + +) + +@if defined DEVINST ( + set INSTALLTYPE=DeveloperInstall + set CLIENT= + set SERVER_INSTALL= + set TASK_LIST= + set SERVICE_TASK= + set CLASSICSERVER= + set SUPERCLASSIC= + set SUPERSERVER= +) -@if defined CLIENT ( set INSTALLTYPE=ClientInstall & set DEVINST=& set SERVER=) -@if defined DEVINST (set INSTALLTYPE=DeveloperInstall & set SERVER=) :: Theoretically this next line is redundant -@if defined SERVER (set INSTALLTYPE=ServerInstall ) +@if defined SERVER_INSTALL ( + + set INSTALLTYPE=ServerInstall + set CLIENT= + set DEVINST= + + @if defined CLASSICSERVER ( + set SUPERSERVER= + set SUPERCLASSIC= + set TASK_LIST=UseClassicServerTask + ) else ( + @if defined SUPERCLASSIC ( + set SUPERSERVER= + set CLASSICSERVER= + set TASK_LIST=UseSuperClassicTask + ) else ( + rem @if defined SUPERSERVER ( + set SUPERCLASSIC= + set CLASSICSERVER= + set SUPERSERVER=1 + set TASK_LIST=UseSuperServerTask + ) + ) +) +::@call :SET_VERBOSE +@call :PRINT_VARS In %0 - End of Fixup +:::::::::: End Fix Up incompatible assignments ::::::::::::::::::::::::::::::::: :: Now build our task list -@if defined CLASSICSERVER ( set TASK_LIST=UseClassicServerTask) -@if defined SUPERCLASSIC ( set TASK_LIST=UseSuperClassicTask) -:: Theoretically this next line is redundant -@if defined SUPERSERVER ( set TASK_LIST=UseSuperServerTask) +:: At this stage, if TASK_LIST is not defined then we are not doing a server install +@if defined TASK_LIST ( + if defined APPTASK ( + set TASK_LIST=!TASK_LIST!,UseApplicationTask + set INSTALLTYPE=CustomInstall + ) else ( + set TASK_LIST=!TASK_LIST!,UseServiceTask + ) -@if defined APPTASK ( - set TASK_LIST=!TASK_LIST!,UseApplicationTask -) else ( - set TASK_LIST=!TASK_LIST!,UseServiceTask + if NOT defined NOAUTOSTART ( + set TASK_LIST=!TASK_LIST!,AutoStartTask + set INSTALLTYPE=CustomInstall + ) ) -@if NOT defined NOAUTOSTART ( - set TASK_LIST=!TASK_LIST!,AutoStartTask + +@if NOT defined NOCOPYFBLIB ( + if not defined TASK_LIST ( + set TASK_LIST=CopyFbClientToSysTask + ) else ( + set TASK_LIST=!TASK_LIST!,CopyFbClientToSysTask + ) set INSTALLTYPE=CustomInstall ) -if NOT defined NOCOPYFBLIB ( - set TASK_LIST=!TASK_LIST!,CopyFbClientToSysTask - set INSTALLTYPE=CustomInstall -) @if defined COPYGDSLIB ( - set TASK_LIST=!TASK_LIST!,CopyFbClientAsGds32Task + if not defined TASK_LIST ( + set TASK_LIST=CopyFbClientAsGds32Task + ) else ( + set TASK_LIST=!TASK_LIST!,CopyFbClientAsGds32Task + ) set INSTALLTYPE=CustomInstall ) +@call :PRINT_VARS In %0 - End of set TASK_LIST + :SET_CMD_PARAMS :: set up the CMD_PARAMS variable we will use @@ -248,37 +312,40 @@ if NOT defined NOCOPYFBLIB ( :: Setting PASSWORD is only relevant for a server install @if defined PASSWORD ( - @if defined SERVER ( + if defined SERVER_INSTALL ( set SYSDBAPASSWORD=%ISC_PASSWORD% set CMD_PARAMS=!CMD_PARAMS! /SYSDBAPASSWORD=%SYSDBAPASSWORD% set INSTALLTYPE=CustomInstall ) ) -if defined NOMSG set CMD_PARAMS=!CMD_PARAMS! /SUPPRESSMSGBOXES -if defined SILENT set CMD_PARAMS=!CMD_PARAMS! /SILENT -if defined SP set CMD_PARAMS=!CMD_PARAMS! /SP- -if defined VERYSILENT set CMD_PARAMS=!CMD_PARAMS! /VERYSILENT +@if defined NOMSG set CMD_PARAMS=!CMD_PARAMS! /SUPPRESSMSGBOXES +@if defined SILENT set CMD_PARAMS=!CMD_PARAMS! /SILENT +@if defined SP set CMD_PARAMS=!CMD_PARAMS! /SP- +@if defined VERYSILENT set CMD_PARAMS=!CMD_PARAMS! /VERYSILENT :: Setting CustomInstall clears the default COMPONENTS list so we :: must define it manually -@if /I %INSTALLTYPE% == "CustomInstall" ( - @if defined CLIENT ( set COMPONENTS=ClientComponent) - @if defined DEVINST ( set COMPONENTS=DevAdminComponent,ClientComponent) +::echo INSTALLTYPE %INSTALLTYPE% +@if /I "%INSTALLTYPE%" == "CustomInstall" ( + if defined CLIENT ( set COMPONENTS=ClientComponent) + if defined DEVINST ( set COMPONENTS=DevAdminComponent,ClientComponent) + if defined SERVER_INSTALL ( set COMPONENTS=ServerComponent,DevAdminComponent,ClientComponent) ) else ( set COMPONENTS=ServerComponent,DevAdminComponent,ClientComponent ) -if defined INSTALL ( - set FULL_CMD=/TYPE=!INSTALLTYPE! /TASKS="!TASK_LIST!" /COMPONENTS="!COMPONENTS!" !CMD_PARAMS! +@if defined INSTALL ( + if defined TASK_LIST ( + set FULL_CMD=/TYPE=!INSTALLTYPE! /TASKS="!TASK_LIST!" /COMPONENTS="!COMPONENTS!" !CMD_PARAMS! + ) else ( + set FULL_CMD=/TYPE=!INSTALLTYPE! /COMPONENTS="!COMPONENTS!" !CMD_PARAMS! + ) ) else ( set FULL_CMD=!CMD_PARAMS! ) +@call :PRINT_VARS In %0 - After setting COMPONENTS and FULL_CMD -@if defined VERBOSE ( - @call :PRINT_VARS - if NOT defined DEBUG pause -) @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF @@ -288,8 +355,10 @@ if defined INSTALL ( :PRINT_VARS :: if a variable is not defined we don't print it, except for critical :: variables such as FINALCMD that MUST be defined. +@if not defined VERBOSE goto :EOF +@if not "%~1" == "" ( echo %* ) @echo Variables set during script execution are: -@set ADIRNAME +@set ADIRNAME 2>nul @set APPTASK 2>nul @set CLASSICSERVER 2>nul @set CLEAN 2>nul @@ -299,12 +368,15 @@ if defined INSTALL ( @set COPYGDSLIB 2>nul @set DEVINST 2>nul @set DRYRUN 2>nul -@set FBINST_EXEC +@set FBINST_EXEC 2>nul +@set FIREBIRD 2>nul @set FINALCMD 2>nul -@set FULL_CMD +@set FULL_CMD 2>nul @set FORCE 2>nul @set INTERACTIVE 2>nul @set INSTALL 2>nul +@set ISC_USER 2>nul +@set ISC_PASSWORD 2>nul @set MERGE_TASKS 2>nul @set NOARCHIVE= 2>nul @set NOAUTOSTART 2>nul @@ -315,8 +387,7 @@ if defined INSTALL ( @set PASSWORD 2>nul @set RUN_TIMESTAMP 2>nul @set SCRIPTED 2>nul -@set SERVICE_TASK 2>nul -@set SERVER 2>nul +@set SERVER_INSTALL 2>nul @set SERVICE_TASK 2>nul @set SILENT 2>nul @set SP 2>nul @@ -327,7 +398,7 @@ if defined INSTALL ( @set UNINSTALL 2>nul @set VERYSILENT 2>nul @echo. - +@if NOT defined DEBUG pause @goto :EOF ::======================================================= @@ -336,56 +407,65 @@ if defined INSTALL ( @echo. @echo. @call :SET_VERBOSE_IF_DEBUG_ON -if defined DEBUG @echo Entering %0 %* -::@call :CHECK_ENV || (@echo Check the values in SET_ENV & goto :EOF ) +@if defined DEBUG @echo Entering %~0 %* @call :CHECK_ENV -::@call :RESET_INSTALL_ENV +@if defined _err ( goto :EOF) + @call :GET_OPTS %* @call :SET_PARAMS %* ::@call :SET_VERBOSE -@if defined VERBOSE @echo on @if defined VERBOSE @echo FULL_CMD is %FULL_CMD% @if defined VERBOSE ( if NOT defined DEBUG pause ) @call :TIMESTAMP +@if defined DEBUG echo After call TIMESTAMP @set RUN_TIMESTAMP=%TIMESTAMP% @set INSTALL_TIMESTAMP=%TIMESTAMP% +@if defined DEBUG echo Before set FINALCMD +@set FINALCMD=%FBINST_EXEC% %FULL_CMD% /DIR=%FIREBIRD% /LOG=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%.log /SAVEINF=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%-saved.inf +@if defined DEBUG echo After set FINALCMD -set FINALCMD=%FBINST_EXEC% %FULL_CMD% /DIR=%FIREBIRD% /LOG=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%.log /SAVEINF=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%-saved.inf -if defined DRYRUN ( +@if defined DRYRUN ( @echo DRYRUN - Not executing call %FINALCMD% ) else ( - @if defined SHOW_FINAL_CMD @echo Executing %FINALCMD% - call %FINALCMD% + @if defined DEBUG @echo DRYRUN not set + @if defined SHOW_FINAL_CMD (@echo Executing %FINALCMD%) + @call %FINALCMD% - @if errorlevel 1 ( + @if ERRORLEVEL 1 ( rem @echo Calling %FBINST_EXEC% failed with %ERRORLEVEL% set _err=%ERRORLEVEL% - call :ISS_ERROR %_err% %FBINST_EXEC% %FULL_CMD% + @call :ISS_ERROR %_err% %FBINST_EXEC% %FULL_CMD% set /A XRESULT+=1 + @goto :EOF ) else ( @echo Calling %FBINST_EXEC%......................SUCCESS! ) - @echo. @echo Now checking system state... - if not defined NOCOPYFBLIB ( + + @if defined SERVER_INSTALL ( + call :CHECKSERVICECONFIGURED + call :CHECKSERVICEINSTALLED + ) + @if not defined NOCOPYFBLIB ( call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll good bad err_is_fail ) else ( call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll bad good no_err_is_fail ) - if not defined COPYGDSLIB ( + @if not defined COPYGDSLIB ( call :CHECKFILEEXISTS c:\windows\system32\gds32.dll bad good no_err_is_fail ) else ( call :CHECKFILEEXISTS c:\windows\system32\gds32.dll good bad err_is_fail ) - @echo. + @echo Calling COPY_INSTALL @call :COPY_INSTALL @echo. ) - @echo. +@echo. @echo %0 completed with %XRESULT% errors @set XRESULT=0 + @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @echo. @@ -401,12 +481,14 @@ if defined DRYRUN ( @call :SET_VERBOSE_IF_DEBUG_ON ::@call :SET_VERBOSE @if defined DEBUG @echo Entering %0 %* +@if defined NOUNINSTALL ( echo NOUNINSTALL set. Not running uninstaller & exit /b 1) + ::@call :RESET_INSTALL_ENV @call :GET_OPTS %* UNINSTALL @call :SET_PARAMS ::@call :SET_VERBOSE @if defined VERBOSE @echo on -@if defined VERBOSE call :PRINT_VARS +@if defined VERBOSE @call :PRINT_VARS @if defined VERBOSE @echo FULL_CMD is %FULL_CMD% @if defined NOUNINSTALL ( @echo NOUNINSTALL was passed. Exiting %0. @@ -418,14 +500,14 @@ if defined DRYRUN ( @if defined VERBOSE ( if NOT defined DEBUG (pause) ) @if defined DRYRUN ( echo DRYRUN - Not executing call %FINALCMD% - if defined DEBUG @echo Leaving %0 + @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @echo. @echo. goto :EOF ) @if defined SHOW_FINAL_CMD @echo Executing %FINALCMD% - call %FINALCMD% 2>nul + @call %FINALCMD% 2>nul if errorlevel 1 ( set _err=%ERRORLEVEL% ) else ( @@ -433,7 +515,7 @@ if defined DRYRUN ( ) if %_err% GEQ 1 ( set _err=%ERRORLEVEL% - call :ISS_ERROR %_err% %UNINSTALLEXE% %FULL_CMD% + @call :ISS_ERROR %_err% %UNINSTALLEXE% %FULL_CMD% set /A XRESULT+=1 ) else ( echo Calling %FIREBIRD%\%UNINSTALLEXE% ................SUCCESS! @@ -452,23 +534,23 @@ if defined DRYRUN ( echo. echo Now checking system state... - call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll bad good no_err_is_fail - call :CHECKFILEEXISTS c:\windows\system32\gds32.dll bad good no_err_is_fail + @call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll bad good no_err_is_fail + @call :CHECKFILEEXISTS c:\windows\system32\gds32.dll bad good no_err_is_fail if defined CLEAN ( - call :CHECKSHAREDDLLS - call :CHECKFILEEXISTS %FIREBIRD% bad good no_err_is_fail + @call :CHECKSHAREDDLLS + @call :CHECKFILEEXISTS %FIREBIRD% bad good no_err_is_fail ) echo. - call :COPY_INSTALL + @call :COPY_INSTALL echo. ) echo. echo %0 completed with %XRESULT% errors set XRESULT=0 -if defined DEBUG @echo Leaving %0 -call :UNSET_VERBOSE +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE @echo. @echo. @goto :EOF @@ -477,7 +559,7 @@ call :UNSET_VERBOSE ::===================================== :CHECKFILEEXISTS -if defined DEBUG @echo Entering %0 +@if defined DEBUG @echo Entering %0 @call :SET_VERBOSE_IF_DEBUG_ON :: DIR returns an error if file not found and zero if file is returned so we @@ -491,66 +573,108 @@ if defined DEBUG @echo Entering %0 :: - %3 - string to output if DIR throws an error :: - %4 - flag to indicate if 0 is an error or not ::@call :SET_VERBOSE -if defined VERBOSE @echo on -if defined VERBOSE @echo %* +@if defined VERBOSE @echo on +@if defined VERBOSE @echo %* dir %1 >nul 2>nul -if errorlevel 1 ( +@if errorlevel 1 ( set _err=%ERRORLEVEL% ) else ( set _err=0 ) -if %_err% EQU 0 ( +@if %_err% EQU 0 ( @echo %TAB% %1 exists - %2 ! ) else ( @echo %TAB% %1 not found - %3 ! ) -if "%4"=="err_is_fail" ( +@if "%4"=="err_is_fail" ( if %_err% GTR 0 ( set /A XRESULT+=1 @echo XRESULT++ ) ) -if "%4"=="no_err_is_fail" ( +@if "%4"=="no_err_is_fail" ( if %_err% EQU 0 ( set /A XRESULT+=1 @echo XRESULT++ ) ) -call :RESET_ERRORLEVEL +@call :RESET_ERRORLEVEL @call :UNSET_VERBOSE -if defined DEBUG @echo Leaving %0 +@if defined DEBUG @echo Leaving %0 @goto :EOF ::===CHECKFILEEXISTS================================== ::===================================== :CHECKSHAREDDLLS -if defined DEBUG @echo Entering %0 +@if defined DEBUG @echo Entering %0 @call :SET_VERBOSE_IF_DEBUG_ON @reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs > %TEMP%\shareddlls.txt -@grep --ignore-case --count firebird %TEMP%\shareddlls.txt > %TEMP%\shareddllscount.txt +::@grep --ignore-case --count firebird %TEMP%\shareddlls.txt > %TEMP%\shareddllscount.txt +type %TEMP%\shareddlls.txt | find /C /I "firebird" > %TEMP%\shareddllscount.txt set /p SHAREDDLLSCOUNT= < %TEMP%\shareddllscount.txt -if NOT defined DEBUG del /q %TEMP%\shareddll*.txt -if %SHAREDDLLSCOUNT% GTR 0 ( +@if NOT defined DEBUG del /q %TEMP%\shareddll*.txt +@if %SHAREDDLLSCOUNT% GTR 0 ( @echo %TAB% Oops - residue in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs set /A XRESULT+=1 @echo XRESULT++ ) -call :RESET_ERRORLEVEL -if defined DEBUG @echo Leaving %0 +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF ::===CHECKSHAREDDLLS=================== +::===================================== +:CHECKSERVICECONFIGURED +@if defined DEBUG @echo Entering %0 +@call :SET_VERBOSE_IF_DEBUG_ON + +:: Add test for server arch set in firebird.conf + + if defined CLASSICSERVER ( set STR_TO_TEST="servermode = classic" ) + if defined SUPERCLASSIC ( set STR_TO_TEST="servermode = superclassic" ) + if defined SUPERSERVER ( set STR_TO_TEST="servermode = super" ) + + call :CHECKSTRING %STR_TO_TEST% %FIREBIRD%\\firebird.conf + if ERRORLEVEL 1 ( + @echo %TAB% %STR_TO_TEST% not set in %FIREBIRD%\\firebird.conf + set /A XRESULT+=1 + @echo XRESULT++ + ) + +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE +@goto :EOF +::===CHECKSERVICECONFIGURED=================== + + + +::===================================== +:CHECKSERVICEINSTALLED +@if defined DEBUG @echo Entering %0 +@call :SET_VERBOSE_IF_DEBUG_ON + + %FIREBIRD%\\instsvc.exe q + + +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE +@goto :EOF +::===CHECKSERVICEINSTALLED=================== + + ::===================================== :COPY_INSTALL @call :SET_VERBOSE_IF_DEBUG_ON -if defined DEBUG @echo Entering %0 +@if defined DEBUG @echo Entering %0 ::@call :SET_VERBOSE -if defined VERBOSE @echo on +@if defined VERBOSE @echo on :: ADIRNAME should normally be set during install and persist for uninstall @if not defined ADIRNAME ( @@ -590,10 +714,13 @@ if defined VERBOSE @echo on :CHECK_ENV :: TODO - add more checks for the environment declared in SET_GLOBAL_ENV if not exist %FBINST_EXEC% ( + echo %~0 failed echo Cannot find %FBINST_EXEC% - exit /b 1 + echo Check the setting of FBINST_EXEC + set _err=1 + exit /b %_err% ) -@goto :EOF +goto :EOF ::===CHECK_ENV========================= ::===================================== @@ -604,6 +731,7 @@ if not exist %FBINST_EXEC% ( :: not have the same value as the internal ERRORLEVEL. We have to execute an :: arbitrary command that cannot fail if we want to really reset the internal :: variable. +@set _err= @ver > nul @goto :EOF ::===RESET_ERRORLEVEL================== @@ -620,12 +748,15 @@ if not exist %FBINST_EXEC% ( @goto :EOF ::===SET_VERBOSE_IF_DEBUG_ON================ + ::===================================== :SET_VERBOSE @set VERBOSE=1 +@echo on @goto :EOF ::===SET_VERBOSE================ + ::===================================== :UNSET_VERBOSE :: Unset VERBOSE before exiting each sub-routine. @@ -651,50 +782,131 @@ goto :EOF :ISS_ERROR @echo. @echo InnoSetup ErrorCode %1 from calling %~2 %~3 %~4 %~5 %~6 %~7 %~8 %~9 -@echo Definition of Innosetup errorcode %1 is to be added later +@echo. +@if "%1"=="1" ( + @echo Setup failed to initialize. + @echo. + @goto :EOF +) + +@if "%1"=="2" ( + @echo The user clicked Cancel in the wizard before the actual installation + @echo started, or chose 'No' on the opening 'This will install...' message box. + @echo. + @goto :EOF +) + +@if "%1" == "3" ( + @echo A fatal error occurred while preparing to move to the next + @echo installation phase (for example, from displaying the pre-installation + @echo wizard pages to the actual installation process). This should never + @echo happen except under the most unusual of circumstances, such as + @echo running out of memory or Windows resources. + @echo. + @goto :EOF +) + +@if "%1" == "4" ( + @echo A fatal error occurred during the actual installation process. + @echo. + @goto :EOF +) + +@if "%1" == "5" ( + @echo The user clicked Cancel during the actual installation process, + @echo or chose Abort at an Abort-Retry-Ignore box. + @echo. + @goto :EOF +) + +@if "%1" == "6" ( + @echo The Setup process was forcefully terminated by the debugger + @echo (Run | Terminate was used in the Compiler IDE). + @echo. + @goto :EOF +) + +@if "%1" == "7" ( + @echo The Preparing to Install stage determined that Setup cannot proceed + @echo with installation. (First introduced in Inno Setup 5.4.1.) + @echo. + @goto :EOF +) + +@if "%1" == "8" ( + @echo The Preparing to Install stage determined that Setup cannot proceed + @echo with installation, and that the system needs to be restarted in + @echo order to correct the problem. (First introduced in Inno Setup 5.4.1.) + @echo. + @goto :EOF +) @echo. @goto :EOF ::====================================== -::================================================================================================================ -:: GENERIC SUPPORT ROUTINES FROM HERE TO END ====================================================================== -::================================================================================================================ +::============================================================================== +:: GENERIC SUPPORT ROUTINES FROM HERE TO END ================================== +::============================================================================== + + +::===================================== +:CHECKSTRING +@if defined DEBUG @echo Entering %0 +@call :SET_VERBOSE_IF_DEBUG_ON +::@if defined VERBOSE echo %0 - %* +for %%v in ( %* ) do ( + if /I "%%v"=="" ( @goto :EOF ) +) +:: === NOTE ==== +:: We use the /x flag here for an exact match! +@if defined VERBOSE echo calling findstr /x /c:%1 /i %2 +:: findstr /x /c:"servermode = superclassic" /i Firebird_4_0\firebird.conf +findstr /x /c:%1 /i %2 + +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE +@goto :EOF +::===CHECKSTRING=================== + + ::===================================== :GET_DATE -if NOT defined DEBUG @echo off -echo. | date | FIND "(mm" > NUL -if errorlevel 1 ((set MD=0) & call :ParseDate DD MM) else ((set MD=1) & call :ParseDate MM DD) +@if NOT defined DEBUG @echo off +@echo. | date | FIND "(mm" > NUL +@if errorlevel 1 ((set MD=0) & call :ParseDate DD MM) else ((set MD=1) & call :ParseDate MM DD) @goto :EOF :ParseDate -for /f "tokens=1-3 delims=/.- " %%a in ("%DATE%") do ( -set %1=%%a -set %2=%%b -set YYYY=%%c +@for /f "tokens=1-3 delims=/.- " %%a in ("%DATE%") do ( +@set %1=%%a +@set %2=%%b +@set YYYY=%%c @goto:EOF) ::===================================== ::===================================== :GET_TIME -if NOT defined DEBUG @echo off -for /f "tokens=1-4 delims=:. " %%a in ("%TIME%") do ( -set hh=%%a -set nn=%%b -set ss=%%c -set ms=%%d +@if NOT defined DEBUG @echo off +@for /f "tokens=1-4 delims=:. " %%a in ("%TIME%") do ( +@set hh=%%a +@set nn=%%b +@set ss=%%c +@set ms=%%d @goto :EOF) ::===================================== ::===================================== :CLEAR_DT_VARS -if NOT defined DEBUG @echo off -set MM= -set DD= -set YYYY= -set hh= -set nn= -set ss= -set ms= +@if NOT defined DEBUG @echo off +@set MM= +@set DD= +@set YYYY= +@set hh= +@set nn= +@set ss= +@set ms= +@if defined DEBUG @echo Leaving CLEAR_DT_VARS @goto :EOF ::===================================== @@ -703,8 +915,8 @@ set ms= @call :GET_DATE @call :GET_TIME @set TIMESTAMP=%YYYY%%MM%%DD%%hh%%nn%%ss% -if defined DEBUG (@echo Timestamp set to %TIMESTAMP%) -call :CLEAR_DT_VARS +@if defined DEBUG (@echo Timestamp set to %TIMESTAMP%) +@call :CLEAR_DT_VARS @goto :EOF ::===================================== @@ -724,25 +936,22 @@ call :CLEAR_DT_VARS :: sometimes it is useful to just tidy up! @if /I "%1"=="clean" ( - @call :RUN_UNINSTALLER SCRIPTED CLEAN - @goto :EOF + @set NOUNINSTALL= + call :RUN_UNINSTALLER SCRIPTED CLEAN + goto :EOF ) @call :RUN_INSTALLER %* +@if defined _err ( echo _err is %_err% - quitting. && @goto :EOF) + @call :RUN_UNINSTALLER %* + @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF ::===MAIN============================== -:: CONTINUE with -:: - look at setting up different test recipes and naming them. -:: - Integrate innosetup exit codes and look at error handling -:: - Tidy up the relationship between DEBUG and VERBOSE. -:: - DEBUG should turn on VERBOSE? -:: - VERBOSE should be routine specific.? -:: - DEBUG should persist for entire script run? :: NOTHING BEYOND THIS POINT=========== From 44fc1289ddbd1f5d634a3112f50c6d6990cbd094 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Mon, 28 Feb 2022 16:40:51 +0300 Subject: [PATCH 097/187] Fixed #7138: Problems accessing database, copied from another host. --- src/jrd/dfw.epp | 1 + src/jrd/jrd.cpp | 20 +++++++---- src/jrd/scl.epp | 95 ++++++++++++++++++++++++++++--------------------- src/jrd/scl.h | 7 ++-- 4 files changed, 72 insertions(+), 51 deletions(-) diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index ac11591c31..3d6d9a5ee3 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -3998,6 +3998,7 @@ void DFW_reset_icu(thread_db* tdbb) "join RDB$CHARACTER_SETS cs on coll.RDB$CHARACTER_SET_ID = cs.RDB$CHARACTER_SET_ID " "where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%' " " and coalesce(ind.RDB$INDEX_INACTIVE, 0) = 0 " + " and coalesce(rel.RDB$RELATION_TYPE, 0) = 0 " // rel_persistent "group by ind.RDB$INDEX_NAME, rel.RDB$RELATION_ID, coll.RDB$BASE_COLLATION_NAME, " " coll.RDB$COLLATION_NAME, cs.RDB$CHARACTER_SET_NAME, coll.RDB$SPECIFIC_ATTRIBUTES"; diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index fd2edf30ee..0ac62ad714 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1331,7 +1331,7 @@ static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tr static void rollback(thread_db*, jrd_tra*, const bool); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); static void getUserInfo(UserId&, const DatabaseOptions&, const char*, - const RefPtr*, bool, Mapping& mapping); + const RefPtr*, bool, Mapping& mapping, bool); static void waitForShutdown(Semaphore&); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1347,7 +1347,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas { Mapping mapping(Mapping::MAP_ERROR_HANDLER, NULL); mapping.setAuthBlock(m_options->dpb_auth_block); - getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping); + getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping, false); } @@ -1947,7 +1947,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch try { mapping.setDb(filename, expanded_name.c_str(), jAtt); - getUserInfo(userId, options, filename, &config, false, mapping); + getUserInfo(userId, options, filename, &config, false, mapping, options.dpb_reset_icu); } catch(const Exception&) { @@ -1959,7 +1959,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch } userId.makeRoleName(options.dpb_sql_dialect); - UserId::sclInit(tdbb, false, userId); + userId.sclInit(tdbb, false); Monitoring::publishAttachment(tdbb); @@ -2079,6 +2079,10 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch { validateAccess(tdbb, attachment, USE_GFIX_UTILITY); DFW_reset_icu(tdbb); + + // force system privileges recheck for sysdba + fb_assert(attachment->att_user); // set by UserId::sclInit() + attachment->att_user->setFlag(USR_newrole); } if (options.dpb_journal.hasData()) @@ -2838,7 +2842,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch // Check for correct credentials supplied mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr); - getUserInfo(userId, options, filename, &config, true, mapping); + getUserInfo(userId, options, filename, &config, true, mapping, false); #ifdef WIN_NT guardDbInit.enter(); // Required to correctly expand name of just created database @@ -3034,7 +3038,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch INI_init(tdbb); PAG_init(tdbb); - UserId::sclInit(tdbb, true, userId); + userId.sclInit(tdbb, true); if (options.dpb_set_page_buffers) dbb->dbb_page_buffers = options.dpb_page_buffers; @@ -8406,7 +8410,7 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status **/ static void getUserInfo(UserId& user, const DatabaseOptions& options, const char* aliasName, - const RefPtr* config, bool creating, Mapping& mapping) + const RefPtr* config, bool creating, Mapping& mapping, bool icuReset) { bool wheel = false; int id = -1, group = -1; // CVC: This var contained trash @@ -8472,6 +8476,8 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, const char if (wheel) { name = DBA_USER_NAME; + if (icuReset) + user.setFlag(USR_sysdba); } if (name.length() > USERNAME_LENGTH) diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index e64307c62e..b91f6ce2d1 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -986,44 +986,59 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) void UserId::findGrantedRoles(thread_db* tdbb) const { - SET_TDBB(tdbb); - Jrd::Attachment* const attachment = tdbb->getAttachment(); - - PreparedStatement::Builder sql; - MetaName usr_get_role; - string usr_get_priv; - sql << "with recursive role_tree as ( " - << " select rdb$relation_name as nm, 0 as ur from rdb$user_privileges " - << " where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user = " << usr_user_name << " and rdb$user_type = 8 " - << " union all " - << " select rdb$role_name as nm, 1 as ur from rdb$roles " - << " where rdb$role_name = " << usr_sql_role_name - << " union all " - << " select p.rdb$relation_name as nm, t.ur from rdb$user_privileges p " - << " join role_tree t on t.nm = p.rdb$user " - << " where p.rdb$privilege = 'M' and (p.rdb$field_name = 'D' or t.ur = 1)) " - << "select " << sql("r.rdb$role_name, ", usr_get_role) - << sql("r.rdb$system_privileges ", usr_get_priv) - << " from role_tree t join rdb$roles r on t.nm = r.rdb$role_name "; - - AutoPreparedStatement stmt(attachment->prepareStatement(tdbb, attachment->getSysTransaction(), sql)); - AutoResultSet rs(stmt->executeQuery(tdbb, attachment->getSysTransaction())); - - usr_granted_roles.clear(); - usr_privileges.clearAll(); - - while (rs->fetch(tdbb)) + try { - if (!usr_granted_roles.exist(usr_get_role)) // SQL request can return duplicates + SET_TDBB(tdbb); + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + PreparedStatement::Builder sql; + MetaName usr_get_role; + string usr_get_priv; + sql << "with recursive role_tree as ( " + << " select rdb$relation_name as nm, 0 as ur from rdb$user_privileges " + << " where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user = " << usr_user_name << " and rdb$user_type = 8 " + << " union all " + << " select rdb$role_name as nm, 1 as ur from rdb$roles " + << " where rdb$role_name = " << usr_sql_role_name + << " union all " + << " select p.rdb$relation_name as nm, t.ur from rdb$user_privileges p " + << " join role_tree t on t.nm = p.rdb$user " + << " where p.rdb$privilege = 'M' and (p.rdb$field_name = 'D' or t.ur = 1)) " + << "select " << sql("r.rdb$role_name, ", usr_get_role) + << sql("r.rdb$system_privileges ", usr_get_priv) + << " from role_tree t join rdb$roles r on t.nm = r.rdb$role_name "; + + AutoPreparedStatement stmt(attachment->prepareStatement(tdbb, attachment->getSysTransaction(), sql)); + AutoResultSet rs(stmt->executeQuery(tdbb, attachment->getSysTransaction())); + + usr_granted_roles.clear(); + usr_privileges.clearAll(); + + while (rs->fetch(tdbb)) { - usr_granted_roles.add(usr_get_role); - Privileges p; - p.load(usr_get_priv.c_str()); - usr_privileges |= p; + if (!usr_granted_roles.exist(usr_get_role)) // SQL request can return duplicates + { + usr_granted_roles.add(usr_get_role); + Privileges p; + p.load(usr_get_priv.c_str()); + usr_privileges |= p; + } } } + catch (const Exception& e) + { + if (!(usr_flags & USR_sysdba)) + throw; - usr_flags &= ~USR_newrole; + e.stuffException(tdbb->tdbb_status_vector); + if (!fb_utils::containsErrorCode(tdbb->tdbb_status_vector->getErrors(), isc_collation_not_installed)) + throw; + + usr_privileges.setAll(); + tdbb->tdbb_status_vector->init(); + } + + usr_flags &= ~(USR_newrole | USR_sysdba); } @@ -1035,7 +1050,7 @@ void UserId::setRoleTrusted() } -void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) +void UserId::sclInit(thread_db* tdbb, bool create) { /************************************** * @@ -1054,13 +1069,13 @@ void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); - const TEXT* sql_role = tempId.getSqlRole().nullStr(); + const TEXT* sql_role = getSqlRole().nullStr(); // CVC: We'll verify the role and wipe it out when it doesn't exist - if (tempId.getUserName().hasData() && !create) + if (getUserName().hasData() && !create) { - const TEXT* login_name = tempId.getUserName().c_str(); + const TEXT* login_name = getUserName().c_str(); AutoCacheRequest request(tdbb, irq_get_role_name, IRQ_REQUESTS); @@ -1077,17 +1092,17 @@ void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) if (!create && sql_role && *sql_role) { - if (!SCL_role_granted(tdbb, tempId, sql_role)) + if (!SCL_role_granted(tdbb, *this, sql_role)) sql_role = NULL; } if (!sql_role) - sql_role = tempId.getTrustedRole().nullStr(); + sql_role = getTrustedRole().nullStr(); MetaString role_name(sql_role ? sql_role : NULL_ROLE); MemoryPool& pool = *attachment->att_pool; - UserId* const user = FB_NEW_POOL(pool) UserId(pool, tempId); + UserId* const user = FB_NEW_POOL(pool) UserId(pool, *this); user->setSqlRole(role_name); user->usr_init_role = role_name; attachment->att_user = user; diff --git a/src/jrd/scl.h b/src/jrd/scl.h index d6b9a8a4b0..14fc38eefb 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -94,8 +94,7 @@ const SecurityClass::flags_t SCL_MODIFY_ANY = SCL_create | SCL_alter | SCL_contr const USHORT USR_mapdown = 1; // Mapping failed when getting context const USHORT USR_newrole = 2; // usr_granted_roles array needs refresh - -const USHORT USR_external = USR_mapdown; +const USHORT USR_sysdba = 4; // User detected as SYSDBA class UserId { @@ -311,7 +310,7 @@ public: return usr_privileges.test(sp); } - static void sclInit(thread_db* tdbb, bool create, const UserId& tempId); + void sclInit(thread_db* tdbb, bool create); void setUserName(const Firebird::MetaString& userName) { @@ -380,7 +379,7 @@ public: void setFlag(USHORT mask) { - usr_flags |= (mask & USR_external); + usr_flags |= mask; } static void makeRoleName(Firebird::MetaString& role, const int dialect); From b63707d06af38ef7892ccf9445ade4983c70768d Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 1 Mar 2022 00:05:22 +0000 Subject: [PATCH 098/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 01f13dc89b..41431689d1 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:414 + FORMAL BUILD NUMBER:417 */ -#define PRODUCT_VER_STRING "5.0.0.414" -#define FILE_VER_STRING "WI-T5.0.0.414" -#define LICENSE_VER_STRING "WI-T5.0.0.414" -#define FILE_VER_NUMBER 5, 0, 0, 414 +#define PRODUCT_VER_STRING "5.0.0.417" +#define FILE_VER_STRING "WI-T5.0.0.417" +#define LICENSE_VER_STRING "WI-T5.0.0.417" +#define FILE_VER_NUMBER 5, 0, 0, 417 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "414" +#define FB_BUILD_NO "417" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 338c658822..8ae2691855 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=414 +BuildNum=417 NowAt=`pwd` cd `dirname $0` From 9814fd6145134b5c72e6881e355b8c28775afb0b Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 1 Mar 2022 18:14:13 +0300 Subject: [PATCH 099/187] Fixed #7139: With multiple trace sessions user may receive trace events related to engine's requests --- src/jrd/trace/TraceManager.cpp | 8 +++++++- src/jrd/trace/TraceManager.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 9d8e6a5e9e..58afdfe193 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -222,6 +222,8 @@ void TraceManager::update_sessions() } // add new sessions + new_needs = trace_needs; + trace_needs = 0; while (newSessions.hasData()) { TraceSession* s = newSessions.pop(); @@ -234,6 +236,10 @@ void TraceManager::update_sessions() { trace_needs = 0; } + else + { + trace_needs = new_needs; + } } void TraceManager::update_session(const TraceSession& session) @@ -344,7 +350,7 @@ void TraceManager::update_session(const TraceSession& session) sesInfo.ses_id = session.ses_id; trace_sessions.add(sesInfo); - trace_needs |= info->factory->trace_needs(); + new_needs |= info->factory->trace_needs(); } else if (status->getState() & IStatus::STATE_ERRORS) { diff --git a/src/jrd/trace/TraceManager.h b/src/jrd/trace/TraceManager.h index 0c581caa19..924ed7f18b 100644 --- a/src/jrd/trace/TraceManager.h +++ b/src/jrd/trace/TraceManager.h @@ -163,7 +163,7 @@ private: Attachment* attachment; Service* service; const char* filename; - NotificationNeeds trace_needs; + NotificationNeeds trace_needs, new_needs; // This structure should be POD-like to be stored in Array struct FactoryInfo From d77baae3a31db080eac5841781ae8ad6b352f0fe Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 2 Mar 2022 00:06:32 +0000 Subject: [PATCH 100/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 41431689d1..74656c78d3 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:417 + FORMAL BUILD NUMBER:418 */ -#define PRODUCT_VER_STRING "5.0.0.417" -#define FILE_VER_STRING "WI-T5.0.0.417" -#define LICENSE_VER_STRING "WI-T5.0.0.417" -#define FILE_VER_NUMBER 5, 0, 0, 417 +#define PRODUCT_VER_STRING "5.0.0.418" +#define FILE_VER_STRING "WI-T5.0.0.418" +#define LICENSE_VER_STRING "WI-T5.0.0.418" +#define FILE_VER_NUMBER 5, 0, 0, 418 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "417" +#define FB_BUILD_NO "418" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 8ae2691855..f565dd66d7 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=417 +BuildNum=418 NowAt=`pwd` cd `dirname $0` From adc816bfc5447c1504f0afcc086192cd3b870798 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 3 Mar 2022 20:07:22 +0300 Subject: [PATCH 101/187] Fixed #7141: Services manager breaks long lines into 1023 bytes portions when using isc_info_svc_line in Service::query() --- src/jrd/svc.cpp | 24 +++++++++++++++++------- src/jrd/svc.h | 3 +++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 7c67dd1fd8..84c03981b9 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -1972,7 +1972,7 @@ THREAD_ENTRY_DECLARE Service::run(THREAD_ENTRY_PARAM arg) Thread::Handle thrHandle = svc->svc_thread; svc->started(); - svc->svc_sem_full.release(); + svc->unblockQueryGet(); svc->finish(SVC_finished); threadCollect->add(thrHandle); @@ -2292,7 +2292,7 @@ void Service::enqueue(const UCHAR* s, ULONG len) { if (checkForShutdown() || (svc_flags & SVC_detached)) { - svc_sem_full.release(); + unblockQueryGet(); return; } @@ -2304,13 +2304,13 @@ void Service::enqueue(const UCHAR* s, ULONG len) { if (flagFirst) { - svc_sem_full.release(); + unblockQueryGet(true); flagFirst = false; } svc_sem_empty.tryEnter(1, 0); if (checkForShutdown() || (svc_flags & SVC_detached)) { - svc_sem_full.release(); + unblockQueryGet(); return; } } @@ -2332,6 +2332,13 @@ void Service::enqueue(const UCHAR* s, ULONG len) s += cnt; len -= cnt; } + unblockQueryGet(); +} + + +void Service::unblockQueryGet(bool over) +{ + svc_output_overflow = over; svc_sem_full.release(); } @@ -2422,8 +2429,11 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US buffer[(*return_length)++] = ch; } - if (!(flags & GET_LINE)) + if (svc_output_overflow || !(flags & GET_LINE)) + { + svc_output_overflow = false; svc_stdout_head = head; + } } if (flags & GET_LINE) @@ -2522,7 +2532,7 @@ ULONG Service::getBytes(UCHAR* buffer, ULONG size) svc_stdin_size_requested = size; svc_stdin_buffer = buffer; // Wakeup Service::query() if it waits for data from service - svc_sem_full.release(); + unblockQueryGet(); } // Wait for data from client @@ -2562,7 +2572,7 @@ void Service::finish(USHORT flag) if (svc_flags & SVC_finished) { - svc_sem_full.release(); + unblockQueryGet(); } else { diff --git a/src/jrd/svc.h b/src/jrd/svc.h index dd528101dc..3f2e1d8647 100644 --- a/src/jrd/svc.h +++ b/src/jrd/svc.h @@ -347,6 +347,9 @@ public: private: Firebird::Semaphore svc_sem_empty, svc_sem_full; + bool svc_output_overflow; + + void unblockQueryGet(bool over = false); class Validate { From 3f55221fda58cfbf7c7a16ecf2b0c90ad93bd6bd Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 4 Mar 2022 00:06:19 +0000 Subject: [PATCH 102/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 74656c78d3..5ee61f9bee 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:418 + FORMAL BUILD NUMBER:419 */ -#define PRODUCT_VER_STRING "5.0.0.418" -#define FILE_VER_STRING "WI-T5.0.0.418" -#define LICENSE_VER_STRING "WI-T5.0.0.418" -#define FILE_VER_NUMBER 5, 0, 0, 418 +#define PRODUCT_VER_STRING "5.0.0.419" +#define FILE_VER_STRING "WI-T5.0.0.419" +#define LICENSE_VER_STRING "WI-T5.0.0.419" +#define FILE_VER_NUMBER 5, 0, 0, 419 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "418" +#define FB_BUILD_NO "419" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index f565dd66d7..ba91f8b985 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=418 +BuildNum=419 NowAt=`pwd` cd `dirname $0` From b9c30aa9fae58772ff6a1da9dc126cad4a1b713d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 14:10:54 -0300 Subject: [PATCH 103/187] Make DeclareLocalTableNode::execute truncate the table if it already had data from a previous execution. --- src/dsql/StmtNodes.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index f8b3b19573..fcf0bc36f4 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -1439,10 +1439,18 @@ DeclareLocalTableNode* DeclareLocalTableNode::pass2(thread_db* /*tdbb*/, Compile return this; } -const StmtNode* DeclareLocalTableNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const +const StmtNode* DeclareLocalTableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { if (request->req_operation == Request::req_evaluate) + { + if (auto& recordBuffer = getImpure(tdbb, request, false)->recordBuffer) + { + delete recordBuffer; + recordBuffer = nullptr; + } + request->req_operation = Request::req_return; + } return parentStmt; } From f1c79bde15c1b90cc85bd491e33e6efe84e12507 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 17:53:15 -0300 Subject: [PATCH 104/187] Unset req_in_use in EXE_release. --- src/jrd/exe.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 5dac7198b9..b9284618d5 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -765,6 +765,8 @@ void EXE_release(thread_db* tdbb, Request* request) request->req_attachment = NULL; } + + request->req_flags &= ~req_in_use; } From 917dcd36c38a34a7782840de3a8bffb1b05abfb5 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 17:53:15 -0300 Subject: [PATCH 105/187] Simplify Statement::verifyAccess for internal statements. --- src/jrd/Statement.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index ac1d05c235..0ac1683f8d 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -422,6 +422,9 @@ Request* Statement::getRequest(thread_db* tdbb, USHORT level) // resources it used indirectly via procedures or triggers. void Statement::verifyAccess(thread_db* tdbb) { + if (flags & FLAG_INTERNAL) + return; + SET_TDBB(tdbb); ExternalAccessList external; From be357e2d95b6dabfe37e56949bebc3bf31534e7f Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 5 Mar 2022 00:06:20 +0000 Subject: [PATCH 106/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 5ee61f9bee..843849504c 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:419 + FORMAL BUILD NUMBER:422 */ -#define PRODUCT_VER_STRING "5.0.0.419" -#define FILE_VER_STRING "WI-T5.0.0.419" -#define LICENSE_VER_STRING "WI-T5.0.0.419" -#define FILE_VER_NUMBER 5, 0, 0, 419 +#define PRODUCT_VER_STRING "5.0.0.422" +#define FILE_VER_STRING "WI-T5.0.0.422" +#define LICENSE_VER_STRING "WI-T5.0.0.422" +#define FILE_VER_NUMBER 5, 0, 0, 422 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "419" +#define FB_BUILD_NO "422" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index ba91f8b985..e1c8107ddd 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=419 +BuildNum=422 NowAt=`pwd` cd `dirname $0` From dd174d1fe486f647c6df30db8f490d610354e750 Mon Sep 17 00:00:00 2001 From: Roman Simakov Date: Fri, 4 Mar 2022 13:25:18 +0300 Subject: [PATCH 107/187] Reserve some constants for features Red Soft are going to merge into Firebird from RedDatabase. It keeps compatibility. --- src/jrd/obj.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/jrd/obj.h b/src/jrd/obj.h index f836d592b1..887dfd9a56 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -69,9 +69,11 @@ const ObjectType obj_filters = 33; // Add new codes here if they are used in RDB$DEPENDENCIES or RDB$USER_PRIVILEGES or stored in backup // Codes for DDL operations add in isDdlObject function as well (find it below). -// etc. obj_tablespaces, +const ObjectType obj_jobs = 34; +const ObjectType obj_tablespace = 35; +const ObjectType obj_tablespaces = 36; -const ObjectType obj_type_MAX = 34; +const ObjectType obj_type_MAX = 37; // used in the parser only / no relation with obj_type_MAX (should be greater) const ObjectType obj_user_or_role= 100; @@ -98,6 +100,8 @@ inline bool isDdlObject(ObjectType object_type) case obj_roles: case obj_charsets: case obj_collations: + case obj_jobs: + case obj_tablespaces: return true; default: return false; @@ -135,6 +139,10 @@ inline const char* getSecurityClassName(ObjectType object_type) return "SQL$CHARSETS"; case obj_collations: return "SQL$COLLATIONS"; + case obj_tablespaces: + return "SQL$TABLESPACES"; + case obj_jobs: + return "SQL$JOBS"; default: return ""; } @@ -173,6 +181,10 @@ inline const char* getDdlObjectName(ObjectType object_type) return "ROLE"; case obj_filters: return "FILTER"; + case obj_tablespaces: + return "TABLESPACE"; + case obj_jobs: + return "JOB"; default: fb_assert(false); return ""; From 167f13cbcc7930da6e1b76dcd181e861e0fb729e Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Mon, 7 Mar 2022 00:06:06 +0000 Subject: [PATCH 108/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 843849504c..a2f0245d00 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:422 + FORMAL BUILD NUMBER:423 */ -#define PRODUCT_VER_STRING "5.0.0.422" -#define FILE_VER_STRING "WI-T5.0.0.422" -#define LICENSE_VER_STRING "WI-T5.0.0.422" -#define FILE_VER_NUMBER 5, 0, 0, 422 +#define PRODUCT_VER_STRING "5.0.0.423" +#define FILE_VER_STRING "WI-T5.0.0.423" +#define LICENSE_VER_STRING "WI-T5.0.0.423" +#define FILE_VER_NUMBER 5, 0, 0, 423 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "422" +#define FB_BUILD_NO "423" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index e1c8107ddd..c533fcff8c 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=422 +BuildNum=423 NowAt=`pwd` cd `dirname $0` From af6a227a71cf859a90b1dac776bb440468bdb354 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 7 Mar 2022 09:20:37 -0300 Subject: [PATCH 109/187] Do not checkCancelState twice when there is no contention in thread_db::reschedule(). Remove unused variable 'dbb'. --- src/jrd/jrd.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 0ac62ad714..9dcecb17ad 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -9006,7 +9006,6 @@ void thread_db::reschedule() checkCancelState(); StableAttachmentPart::Sync* sync = this->getAttachment()->getStable()->getSync(); - Database* dbb = this->getDatabase(); if (sync->hasContention()) { @@ -9017,9 +9016,9 @@ void thread_db::reschedule() while (sync->hasContention() && (sync->getLockCounter() == cnt)) Thread::sleep(1); - } - checkCancelState(); + checkCancelState(); + } Monitoring::checkState(this); From 98ba60e76315262f124db68649c95430fe7ec6ac Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 7 Mar 2022 09:51:12 -0300 Subject: [PATCH 110/187] Correction for my last commit, thanks to Vlad. --- src/jrd/jrd.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 9dcecb17ad..af4b5a2302 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -9011,11 +9011,13 @@ void thread_db::reschedule() { FB_UINT64 cnt = sync->getLockCounter(); - EngineCheckout cout(this, FB_FUNCTION); - Thread::yield(); + { // scope + EngineCheckout cout(this, FB_FUNCTION); + Thread::yield(); - while (sync->hasContention() && (sync->getLockCounter() == cnt)) - Thread::sleep(1); + while (sync->hasContention() && (sync->getLockCounter() == cnt)) + Thread::sleep(1); + } checkCancelState(); } From 68bcf9abd5dd9340fd90bd91517702ce49ac48d3 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 8 Mar 2022 00:06:17 +0000 Subject: [PATCH 111/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index a2f0245d00..65203e30bb 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:423 + FORMAL BUILD NUMBER:425 */ -#define PRODUCT_VER_STRING "5.0.0.423" -#define FILE_VER_STRING "WI-T5.0.0.423" -#define LICENSE_VER_STRING "WI-T5.0.0.423" -#define FILE_VER_NUMBER 5, 0, 0, 423 +#define PRODUCT_VER_STRING "5.0.0.425" +#define FILE_VER_STRING "WI-T5.0.0.425" +#define LICENSE_VER_STRING "WI-T5.0.0.425" +#define FILE_VER_NUMBER 5, 0, 0, 425 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "423" +#define FB_BUILD_NO "425" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index c533fcff8c..c746d9dfe3 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=423 +BuildNum=425 NowAt=`pwd` cd `dirname $0` From 48f8bd4a9d1090729d8bb16dd7ff74914c3795f0 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 8 Mar 2022 13:33:30 +0300 Subject: [PATCH 112/187] This should fix #7150: Replication not restarting after network failure. Minor adjustments regarding replication startup errors --- src/jrd/replication/Config.cpp | 1 - src/remote/server/os/posix/inet_server.cpp | 10 ++++------ src/remote/server/os/win32/srvr_w32.cpp | 9 +++++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/jrd/replication/Config.cpp b/src/jrd/replication/Config.cpp index e441d64896..d66464c1a1 100644 --- a/src/jrd/replication/Config.cpp +++ b/src/jrd/replication/Config.cpp @@ -384,7 +384,6 @@ void Config::enumerate(Firebird::Array& replicas) { config->sourceDirectory = value.c_str(); PathUtils::ensureSeparator(config->sourceDirectory); - checkAccess(config->sourceDirectory, key); } else if (key == "source_guid") { diff --git a/src/remote/server/os/posix/inet_server.cpp b/src/remote/server/os/posix/inet_server.cpp index 2463360033..8de5a57506 100644 --- a/src/remote/server/os/posix/inet_server.cpp +++ b/src/remote/server/os/posix/inet_server.cpp @@ -41,6 +41,7 @@ #include "../common/config/config.h" #include "../common/os/fbsyslog.h" #include "../common/os/os_utils.h" +#include "../common/status.h" #include #ifdef HAVE_SYS_TYPES_H @@ -468,15 +469,12 @@ int CLIB_ROUTINE main( int argc, char** argv) fb_shutdown_callback(NULL, closePort, fb_shut_exit, port); - Firebird::LocalStatus localStatus; - Firebird::CheckStatusWrapper statusWrapper(&localStatus); - - if (!REPL_server(&statusWrapper, false, &serverClosing)) + Firebird::FbLocalStatus localStatus; + if (!REPL_server(&localStatus, false, &serverClosing)) { const char* errorMsg = "Replication server initialization error"; - gds__log_status(errorMsg, localStatus.getErrors()); + iscLogStatus(errorMsg, localStatus->getErrors()); Firebird::Syslog::Record(Firebird::Syslog::Error, errorMsg); - exit(STARTUP_ERROR); } SRVR_multi_thread(port, INET_SERVER_flag); diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index 02801058e6..680693333f 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -501,7 +501,6 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) * **************************************/ ThreadCounter counter; - FbLocalStatus localStatus; if (server_flag & SRVR_inet) { @@ -527,7 +526,13 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) } } - REPL_server(&localStatus, false, &server_shutdown); + FbLocalStatus localStatus; + if (!REPL_server(&localStatus, false, &server_shutdown)) + { + const char* errorMsg = "Replication server initialization error"; + iscLogStatus(errorMsg, localStatus->getErrors()); + Syslog::Record(Syslog::Error, errorMsg); + } return 0; } From faf2523be0c7e50422f7a5ed7ba17ea261571915 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 11 Mar 2022 00:05:57 +0000 Subject: [PATCH 113/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 65203e30bb..842b0eed15 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:425 + FORMAL BUILD NUMBER:426 */ -#define PRODUCT_VER_STRING "5.0.0.425" -#define FILE_VER_STRING "WI-T5.0.0.425" -#define LICENSE_VER_STRING "WI-T5.0.0.425" -#define FILE_VER_NUMBER 5, 0, 0, 425 +#define PRODUCT_VER_STRING "5.0.0.426" +#define FILE_VER_STRING "WI-T5.0.0.426" +#define LICENSE_VER_STRING "WI-T5.0.0.426" +#define FILE_VER_NUMBER 5, 0, 0, 426 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "425" +#define FB_BUILD_NO "426" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index c746d9dfe3..2153695e2f 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=425 +BuildNum=426 NowAt=`pwd` cd `dirname $0` From b64bbba179a0b36f92602d9ad92ae8005d2d6300 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 11 Jan 2022 09:49:34 -0300 Subject: [PATCH 114/187] Add constructor to Array. --- src/common/classes/array.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/classes/array.h b/src/common/classes/array.h index 5121bde6ed..b9e95730e5 100644 --- a/src/common/classes/array.h +++ b/src/common/classes/array.h @@ -110,6 +110,12 @@ public: ensureCapacity(InitialCapacity); } + Array(const T* items, const size_type itemsCount) + : Storage(), count(0), capacity(this->getStorageSize()), data(this->getStorage()) + { + add(items, itemsCount); + } + Array(const Array& source) : Storage(), count(0), capacity(this->getStorageSize()), data(this->getStorage()) { From 171cb7eebc365e301a7384eff96c0e3e069c95cc Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 11 Jan 2022 09:49:34 -0300 Subject: [PATCH 115/187] Improvement #7093 and fix for #7094 and #7140. - #7093 - Improve indexed lookup speed of strings when the last keys characters are part of collated contractions. - #7094 - Incorrect indexed lookup of strings when the last keys characters are part of collated contractions and there is multi-segment insensitive descending index. - #7140 - Wrong select result in case of special sort character. --- src/common/intlobj_new.h | 14 +- src/common/unicode_util.cpp | 262 ++++++++++--- src/common/unicode_util.h | 43 ++- src/jrd/btr.cpp | 594 ++++++++++++++++++------------ src/jrd/btr.h | 2 + src/jrd/btr_proto.h | 6 +- src/jrd/idx.cpp | 37 +- src/jrd/intl.cpp | 2 + src/jrd/optimizer/Optimizer.h | 3 +- src/jrd/optimizer/Retrieval.cpp | 36 +- src/jrd/recsrc/IndexTableScan.cpp | 290 +++++++++------ src/jrd/recsrc/RecordSource.h | 6 + 12 files changed, 865 insertions(+), 430 deletions(-) diff --git a/src/common/intlobj_new.h b/src/common/intlobj_new.h index dee96f0705..8ba33c079a 100644 --- a/src/common/intlobj_new.h +++ b/src/common/intlobj_new.h @@ -57,9 +57,15 @@ typedef USHORT (*pfn_INTL_keylength) (texttype* tt, USHORT len); /* Types of the keys which may be returned by str2key routine */ -#define INTL_KEY_SORT 0 /* Full sort key */ -#define INTL_KEY_PARTIAL 1 /* Starting portion of sort key for equality class */ -#define INTL_KEY_UNIQUE 2 /* Full key for the equality class of the string */ +#define INTL_KEY_SORT 0 /* Full sort key */ +#define INTL_KEY_PARTIAL 1 /* Starting portion of sort key for equality class */ +#define INTL_KEY_UNIQUE 2 /* Full key for the equality class of the string */ +#define INTL_KEY_MULTI_STARTING 3 /* Multiple starting keys */ + +/* INTL_KEY_MULTI_STARTING format: + key ::= { }... + key_length ::= +*/ /* Returned value of INTL_BAD_KEY_LENGTH means that key error happened during key construction. When partial key is requested returned string should @@ -130,6 +136,8 @@ typedef void (*pfn_INTL_tt_destroy) (texttype* tt); (char, case, accent) which is case-insensitive, but accent-sensitive */ +#define TEXTTYPE_MULTI_STARTING_KEY 8 /* Supports INTL_KEY_MULTI_STARTING */ + struct texttype { diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 3c2a17e914..417ac92d96 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -272,8 +272,10 @@ public: USet* (U_EXPORT2 *usetOpen)(UChar32 start, UChar32 end); void (U_EXPORT2 *ucolClose)(UCollator* coll); - int32_t (U_EXPORT2 *ucolGetContractions)(const UCollator* coll, USet* conts, UErrorCode* status); + int32_t (U_EXPORT2 *ucolGetContractionsAndExpansions)(const UCollator* coll, USet* contractions, USet* expansions, + UBool addPrefixes, UErrorCode* status); const UChar* (U_EXPORT2 *ucolGetRules)(const UCollator* coll, int32_t* length); + int32_t (U_EXPORT2 *ucolGetSortKey)(const UCollator* coll, const UChar* source, int32_t sourceLength, uint8_t* result, int32_t resultLength); UCollator* (U_EXPORT2 *ucolOpen)(const char* loc, UErrorCode* status); @@ -1241,7 +1243,8 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c icu->getEntryPoint("uset_open", icu->ucModule, icu->usetOpen); icu->getEntryPoint("ucol_close", icu->inModule, icu->ucolClose); - icu->getEntryPoint("ucol_getContractions", icu->inModule, icu->ucolGetContractions); + icu->getEntryPoint("ucol_getContractionsAndExpansions", icu->inModule, + icu->ucolGetContractionsAndExpansions); icu->getEntryPoint("ucol_getRules", icu->inModule, icu->ucolGetRules); icu->getEntryPoint("ucol_getSortKey", icu->inModule, icu->ucolGetSortKey); icu->getEntryPoint("ucol_open", icu->inModule, icu->ucolOpen); @@ -1617,35 +1620,145 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( USet* contractions = icu->usetOpen(1, 0); // status not verified here. - icu->ucolGetContractions(partialCollator, contractions, &status); + icu->ucolGetContractionsAndExpansions(partialCollator, contractions, NULL, false, &status); int contractionsCount = icu->usetGetItemCount(contractions); for (int contractionIndex = 0; contractionIndex < contractionsCount; ++contractionIndex) { - UChar str[10]; + UChar strChars[10]; UChar32 start, end; status = U_ZERO_ERROR; - int len = icu->usetGetItem(contractions, contractionIndex, &start, &end, str, sizeof(str), &status); + int len = icu->usetGetItem(contractions, contractionIndex, &start, &end, strChars, sizeof(strChars), &status); if (len >= 2) { obj->maxContractionsPrefixLength = len - 1 > obj->maxContractionsPrefixLength ? len - 1 : obj->maxContractionsPrefixLength; - for (int currentLen = 1; currentLen < len; ++currentLen) - { - string s(reinterpret_cast(str), currentLen * 2); + UCHAR key[100]; + int keyLen = icu->ucolGetSortKey(partialCollator, strChars, len, key, sizeof(key)); - if (!obj->contractionsPrefix.exist(s)) - obj->contractionsPrefix.push(s); + for (int prefixLen = 1; prefixLen < len; ++prefixLen) + { + const Array str(reinterpret_cast(strChars), prefixLen); + SortKeyArray* keySet = obj->contractionsPrefix.get(str); + + if (!keySet) + { + keySet = obj->contractionsPrefix.put(str); + + UCHAR prefixKey[100]; + int prefixKeyLen = icu->ucolGetSortKey(partialCollator, + strChars, prefixLen, prefixKey, sizeof(prefixKey)); + + keySet->add(Array(prefixKey, prefixKeyLen)); + } + + keySet->add(Array(key, keyLen)); } } } icu->usetClose(contractions); + ContractionsPrefixMap::Accessor accessor(&obj->contractionsPrefix); + + for (bool found = accessor.getFirst(); found; found = accessor.getNext()) + { + SortKeyArray& keySet = accessor.current()->second; + + if (keySet.getCount() <= 1) + continue; + + fb_assert(accessor.current()->first.hasData()); + USHORT ch = accessor.current()->first[0]; + + if (ch >= 0xFDD0 && ch <= 0xFDEF) + { + keySet.clear(); + keySet.add(Array()); + continue; + } + + SortKeyArray::iterator firstKeyIt = keySet.begin(); + SortKeyArray::iterator lastKeyIt = --keySet.end(); + + const UCHAR* firstKeyDataIt = firstKeyIt->begin(); + const UCHAR* lastKeyDataIt = lastKeyIt->begin(); + const UCHAR* firstKeyDataEnd = firstKeyIt->end(); + const UCHAR* lastKeyDataEnd = lastKeyIt->end(); + + if (*firstKeyDataIt == *lastKeyDataIt) + { + unsigned common = 0; + + do + { + ++common; + } while (++firstKeyDataIt != firstKeyDataEnd && ++lastKeyDataIt != lastKeyDataEnd && + *firstKeyDataIt == *lastKeyDataIt); + + Array commonKey(firstKeyIt->begin(), common); + keySet.clear(); + keySet.add(commonKey); + } + else + { + SortKeyArray::iterator secondKeyIt = ++keySet.begin(); + const UCHAR* secondKeyDataIt = secondKeyIt->begin(); + const UCHAR* secondKeyDataEnd = secondKeyIt->end(); + + ObjectsArray > commonKeys; + commonKeys.add(*firstKeyIt); + + while (secondKeyIt != keySet.end()) + { + unsigned common = 0; + + while (firstKeyDataIt != firstKeyDataEnd && secondKeyDataIt != secondKeyDataEnd && + *firstKeyDataIt == *secondKeyDataIt) + { + ++common; + ++firstKeyDataIt; + ++secondKeyDataIt; + } + + unsigned backSize = commonKeys.back()->getCount(); + + if (common > backSize) + commonKeys.back()->append(secondKeyIt->begin() + backSize, common - backSize); + else if (common < backSize) + { + if (common == 0) + commonKeys.push(*secondKeyIt); + else + commonKeys.back()->resize(common); + } + + if (++secondKeyIt != keySet.end()) + { + ++firstKeyIt; + + firstKeyDataIt = firstKeyIt->begin(); + secondKeyDataIt = secondKeyIt->begin(); + + firstKeyDataEnd = firstKeyIt->end(); + secondKeyDataEnd = secondKeyIt->end(); + } + } + + keySet.clear(); + + for (ObjectsArray >::iterator ckIt = commonKeys.begin(); ckIt != commonKeys.end(); ++ckIt) + keySet.add(*ckIt); + } + } + + if (obj->maxContractionsPrefixLength) + tt->texttype_flags |= TEXTTYPE_MULTI_STARTING_KEY; + return obj; } @@ -1696,41 +1809,17 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src srcLenLong = pad - src + 1; } + if (srcLenLong == 0) + return 0; + HalfStaticArray buffer; const UCollator* coll = NULL; switch (key_type) { case INTL_KEY_PARTIAL: + case INTL_KEY_MULTI_STARTING: coll = partialCollator; - - // Remove last bytes of key if they are start of a contraction - // to correctly find in the index. - - for (int i = MIN(maxContractionsPrefixLength, srcLenLong); i > 0; --i) - { - if (contractionsPrefix.exist(string(reinterpret_cast(src + srcLenLong - i), i * 2))) - { - srcLenLong -= i; - break; - } - } - - if (numericSort) - { - // ASF: Wee need to remove trailing numbers to return sub key that - // matches full key. Example: "abc1" becomes "abc" to match "abc10". - const USHORT* p = src + srcLenLong - 1; - - for (; p >= src; --p) - { - if (!(*p >= '0' && *p <= '9')) - break; - } - - srcLenLong = p - src + 1; - } - break; case INTL_KEY_UNIQUE: @@ -1749,11 +1838,102 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src return INTL_BAD_KEY_LENGTH; } - if (srcLenLong == 0) - return 0; + if (key_type == INTL_KEY_MULTI_STARTING) + { + bool trailingNumbersRemoved = false; - return icu->ucolGetSortKey(coll, + if (numericSort) + { + // ASF: Wee need to remove trailing numbers to return sub key that + // matches full key. Example: "abc1" becomes "abc" to match "abc10". + const USHORT* p = src + srcLenLong - 1; + + for (; p >= src; --p) + { + if (!(*p >= '0' && *p <= '9')) + break; + + trailingNumbersRemoved = true; + } + + srcLenLong = p - src + 1; + } + + if (!trailingNumbersRemoved) + { + for (int i = MIN(maxContractionsPrefixLength, srcLenLong); i > 0; --i) + { + SortKeyArray* keys = contractionsPrefix.get(Array(src + srcLenLong - i, i)); + + if (keys) + { + const UCHAR* dstStart = dst; + ULONG prefixLen; + + srcLenLong -= i; + + if (srcLenLong != 0) + { + prefixLen = icu->ucolGetSortKey(coll, + reinterpret_cast(src), srcLenLong, dst + 2, dstLen - 2); + + if (prefixLen == 0 || prefixLen > dstLen - 2 || prefixLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + fb_assert(dst[2 + prefixLen - 1] == '\0'); + --prefixLen; + dstLen -= 2 + prefixLen; + } + else + prefixLen = 0; + + for (SortKeyArray::const_iterator keyIt = keys->begin(); + keyIt != keys->end(); + ++keyIt) + { + const ULONG keyLen = prefixLen + keyIt->getCount(); + + if (keyLen > dstLen - 2 || keyLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + dst[0] = UCHAR(keyLen & 0xFF); + dst[1] = UCHAR(keyLen >> 8); + + if (dst != dstStart) + memcpy(dst + 2, dstStart + 2, prefixLen); + + memcpy(dst + 2 + prefixLen, keyIt->begin(), keyIt->getCount()); + dst += 2 + keyLen; + dstLen -= 2 + keyLen; + } + + return dst - dstStart; + } + } + } + + ULONG keyLen = icu->ucolGetSortKey(coll, + reinterpret_cast(src), srcLenLong, dst + 2, dstLen - 3); + + if (keyLen == 0 || keyLen > dstLen - 3 || keyLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + fb_assert(dst[2 + keyLen - 1] == '\0'); + --keyLen; + + dst[0] = UCHAR(keyLen & 0xFF); + dst[1] = UCHAR(keyLen >> 8); + + return keyLen + 2; + } + + const ULONG keyLen = icu->ucolGetSortKey(coll, reinterpret_cast(src), srcLenLong, dst, dstLen); + + if (keyLen == 0 || keyLen > dstLen || keyLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + return keyLen; } diff --git a/src/common/unicode_util.h b/src/common/unicode_util.h index 59bf4ade39..c7568a13e0 100644 --- a/src/common/unicode_util.h +++ b/src/common/unicode_util.h @@ -30,7 +30,9 @@ #include "intlobj_new.h" #include "../common/IntlUtil.h" #include "../common/os/mod_loader.h" +#include "../common/classes/array.h" #include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" #include "../common/classes/objects_array.h" #include #include @@ -200,6 +202,45 @@ public: ULONG canonical(ULONG srcLen, const USHORT* src, ULONG dstLen, ULONG* dst, const ULONG* exceptions); private: + template + class ArrayComparator + { + public: + static bool greaterThan(const Firebird::Array& i1, const Firebird::Array& i2) + { + FB_SIZE_T minCount = MIN(i1.getCount(), i2.getCount()); + int cmp = memcmp(i1.begin(), i2.begin(), minCount * sizeof(T)); + + if (cmp != 0) + return cmp > 0; + + return i1.getCount() > i2.getCount(); + } + + static bool greaterThan(const Firebird::Array* i1, const Firebird::Array* i2) + { + return greaterThan(*i1, *i2); + } + }; + + typedef Firebird::SortedObjectsArray< + Firebird::Array, + Firebird::InlineStorage*, 3>, + Firebird::Array, + Firebird::DefaultKeyValue*>, + ArrayComparator + > SortKeyArray; + + typedef Firebird::GenericMap< + Firebird::Pair< + Firebird::Full< + Firebird::Array, // UTF-16 string + SortKeyArray // sort keys + > + >, + ArrayComparator + > ContractionsPrefixMap; + static ICU* loadICU(const Firebird::string& collVersion, const Firebird::string& locale, const Firebird::string& configInfo); @@ -212,7 +253,7 @@ public: UCollator* compareCollator; UCollator* partialCollator; UCollator* sortCollator; - Firebird::SortedObjectsArray contractionsPrefix; // UTF-16 string + ContractionsPrefixMap contractionsPrefix; unsigned maxContractionsPrefixLength; // number of characters bool numericSort; }; diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 2babf8bc52..2908828d71 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -387,9 +387,12 @@ void BTR_complement_key(temporary_key* key) * Negate a key for descending index. * **************************************/ - UCHAR* p = key->key_data; - for (const UCHAR* const end = p + key->key_length; p < end; p++) - *p ^= -1; + do + { + UCHAR* p = key->key_data; + for (const UCHAR* const end = p + key->key_length; p < end; p++) + *p ^= -1; + } while (key = key->key_next.get()); } @@ -694,149 +697,158 @@ void BTR_evaluate(thread_db* tdbb, const IndexRetrieval* retrieval, RecordBitmap index_desc idx; RelationPages* relPages = retrieval->irb_relation->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); - temporary_key lower, upper; - lower.key_flags = 0; - lower.key_length = 0; - upper.key_flags = 0; - upper.key_length = 0; - btree_page* page = BTR_find_page(tdbb, retrieval, &window, &idx, &lower, &upper); + temporary_key lowerKey, upperKey; + lowerKey.key_flags = 0; + lowerKey.key_length = 0; + upperKey.key_flags = 0; + upperKey.key_length = 0; - const bool descending = (idx.idx_flags & idx_descending); - bool skipLowerKey = (retrieval->irb_generic & irb_exclude_lower); - const bool partLower = (retrieval->irb_lower_count < idx.idx_count); + temporary_key* lower = &lowerKey; + temporary_key* upper = &upperKey; + bool first = true; - // If there is a starting descriptor, search down index to starting position. - // This may involve sibling buckets if splits are in progress. If there - // isn't a starting descriptor, walk down the left side of the index. - USHORT prefix; - UCHAR* pointer; - if (retrieval->irb_lower_count) + do { - while (!(pointer = find_node_start_point(page, &lower, 0, &prefix, - idx.idx_flags & idx_descending, (retrieval->irb_generic & (irb_starting | irb_partial))))) - { - page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); - } + btree_page* page = BTR_find_page(tdbb, retrieval, &window, &idx, lower, upper, first); + first = false; - // Compute the number of matching characters in lower and upper bounds - if (retrieval->irb_upper_count) - { - prefix = IndexNode::computePrefix(upper.key_data, upper.key_length, - lower.key_data, lower.key_length); - } + const bool descending = (idx.idx_flags & idx_descending); + bool skipLowerKey = (retrieval->irb_generic & irb_exclude_lower); + const bool partLower = (retrieval->irb_lower_count < idx.idx_count); - if (skipLowerKey) + // If there is a starting descriptor, search down index to starting position. + // This may involve sibling buckets if splits are in progress. If there + // isn't a starting descriptor, walk down the left side of the index. + USHORT prefix; + UCHAR* pointer; + if (retrieval->irb_lower_count) { - IndexNode node; - node.readNode(pointer, true); - - if ((lower.key_length == node.prefix + node.length) || - ((lower.key_length <= node.prefix + node.length) && partLower)) + while (!(pointer = find_node_start_point(page, lower, 0, &prefix, + idx.idx_flags & idx_descending, (retrieval->irb_generic & (irb_starting | irb_partial))))) { - const UCHAR* p = node.data, *q = lower.key_data + node.prefix; - const UCHAR* const end = lower.key_data + lower.key_length; - while (q < end) + page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + } + + // Compute the number of matching characters in lower and upper bounds + if (retrieval->irb_upper_count) + { + prefix = IndexNode::computePrefix(upper->key_data, upper->key_length, + lower->key_data, lower->key_length); + } + + if (skipLowerKey) + { + IndexNode node; + node.readNode(pointer, true); + + if ((lower->key_length == node.prefix + node.length) || + ((lower->key_length <= node.prefix + node.length) && partLower)) { - if (*p++ != *q++) + const UCHAR* p = node.data, *q = lower->key_data + node.prefix; + const UCHAR* const end = lower->key_data + lower->key_length; + while (q < end) { - skipLowerKey = false; - break; + if (*p++ != *q++) + { + skipLowerKey = false; + break; + } + } + + if ((q >= end) && (p < node.data + node.length) && skipLowerKey && partLower) + { + // since key length always is multiplier of (STUFF_COUNT + 1) (for partial + // compound keys) and we passed lower key completely then p pointed + // us to the next segment number and we can use this fact to calculate + // how many segments is equal to lower key + const USHORT segnum = idx.idx_count - (UCHAR) (descending ? ((*p) ^ -1) : *p); + + if (segnum < retrieval->irb_lower_count) + skipLowerKey = false; } } - - if ((q >= end) && (p < node.data + node.length) && skipLowerKey && partLower) - { - // since key length always is multiplier of (STUFF_COUNT + 1) (for partial - // compound keys) and we passed lower key completely then p pointed - // us to the next segment number and we can use this fact to calculate - // how many segments is equal to lower key - const USHORT segnum = idx.idx_count - (UCHAR) (descending ? ((*p) ^ -1) : *p); - - if (segnum < retrieval->irb_lower_count) - skipLowerKey = false; - } + else + skipLowerKey = false; } - else - skipLowerKey = false; } - } - else - { - pointer = page->btr_nodes + page->btr_jump_size; - prefix = 0; - skipLowerKey = false; - } - - // if there is an upper bound, scan the index pages looking for it - if (retrieval->irb_upper_count) - { - while (scan(tdbb, pointer, bitmap, bitmap_and, &idx, retrieval, prefix, &upper, - skipLowerKey, lower)) + else { - page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); pointer = page->btr_nodes + page->btr_jump_size; prefix = 0; + skipLowerKey = false; } - } - else - { - // if there isn't an upper bound, just walk the index to the end of the level - const UCHAR* endPointer = (UCHAR*) page + page->btr_length; - const bool ignoreNulls = - (retrieval->irb_generic & irb_ignore_null_value_key) && (idx.idx_count == 1); - IndexNode node; - pointer = node.readNode(pointer, true); - - // Check if pointer is still valid - if (pointer > endPointer) - BUGCHECK(204); // msg 204 index inconsistent - - while (true) + // if there is an upper bound, scan the index pages looking for it + if (retrieval->irb_upper_count) { - if (node.isEndLevel) - break; - - if (!node.isEndBucket) + while (scan(tdbb, pointer, bitmap, bitmap_and, &idx, retrieval, prefix, upper, + skipLowerKey, *lower)) { - // If we're walking in a descending index and we need to ignore NULLs - // then stop at the first NULL we see (only for single segment!) - if (descending && ignoreNulls && node.prefix == 0 && - node.length >= 1 && node.data[0] == 255) - { - break; - } - - if (skipLowerKey) - checkForLowerKeySkip(skipLowerKey, partLower, node, lower, idx, retrieval); - - if (!skipLowerKey) - { - if (!bitmap_and || bitmap_and->test(node.recordNumber.getValue())) - RBM_SET(tdbb->getDefaultPool(), bitmap, node.recordNumber.getValue()); - } - - pointer = node.readNode(pointer, true); - - // Check if pointer is still valid - if (pointer > endPointer) - BUGCHECK(204); // msg 204 index inconsistent - - continue; + page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + pointer = page->btr_nodes + page->btr_jump_size; + prefix = 0; } + } + else + { + // if there isn't an upper bound, just walk the index to the end of the level + const UCHAR* endPointer = (UCHAR*) page + page->btr_length; + const bool ignoreNulls = + (retrieval->irb_generic & irb_ignore_null_value_key) && (idx.idx_count == 1); - page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); - endPointer = (UCHAR*) page + page->btr_length; - pointer = page->btr_nodes + page->btr_jump_size; + IndexNode node; pointer = node.readNode(pointer, true); // Check if pointer is still valid if (pointer > endPointer) BUGCHECK(204); // msg 204 index inconsistent - } - } - CCH_RELEASE(tdbb, &window); + while (true) + { + if (node.isEndLevel) + break; + + if (!node.isEndBucket) + { + // If we're walking in a descending index and we need to ignore NULLs + // then stop at the first NULL we see (only for single segment!) + if (descending && ignoreNulls && node.prefix == 0 && + node.length >= 1 && node.data[0] == 255) + { + break; + } + + if (skipLowerKey) + checkForLowerKeySkip(skipLowerKey, partLower, node, *lower, idx, retrieval); + + if (!skipLowerKey) + { + if (!bitmap_and || bitmap_and->test(node.recordNumber.getValue())) + RBM_SET(tdbb->getDefaultPool(), bitmap, node.recordNumber.getValue()); + } + + pointer = node.readNode(pointer, true); + + // Check if pointer is still valid + if (pointer > endPointer) + BUGCHECK(204); // msg 204 index inconsistent + + continue; + } + + page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + endPointer = (UCHAR*) page + page->btr_length; + pointer = page->btr_nodes + page->btr_jump_size; + pointer = node.readNode(pointer, true); + + // Check if pointer is still valid + if (pointer > endPointer) + BUGCHECK(204); // msg 204 index inconsistent + } + } + + CCH_RELEASE(tdbb, &window); + } while ((lower = lower->key_next.get()) && (upper = upper->key_next.get())); } @@ -864,7 +876,8 @@ btree_page* BTR_find_page(thread_db* tdbb, WIN* window, index_desc* idx, temporary_key* lower, - temporary_key* upper) + temporary_key* upper, + bool makeKeys) { /************************************** * @@ -884,19 +897,26 @@ btree_page* BTR_find_page(thread_db* tdbb, // are looking for an equality if (retrieval->irb_key) { + fb_assert(makeKeys); copy_key(retrieval->irb_key, lower); copy_key(retrieval->irb_key, upper); } - else + else if (makeKeys) { idx_e errorCode = idx_e_ok; + const USHORT keyType = + (retrieval->irb_generic & irb_multi_starting) ? INTL_KEY_MULTI_STARTING : + (retrieval->irb_generic & irb_starting) ? INTL_KEY_PARTIAL : + (retrieval->irb_desc.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : + INTL_KEY_SORT; + if (retrieval->irb_upper_count) { errorCode = BTR_make_key(tdbb, retrieval->irb_upper_count, retrieval->irb_value + retrieval->irb_desc.idx_count, &retrieval->irb_desc, upper, - (retrieval->irb_generic & irb_starting) != 0); + keyType); } if (errorCode == idx_e_ok) @@ -905,7 +925,7 @@ btree_page* BTR_find_page(thread_db* tdbb, { errorCode = BTR_make_key(tdbb, retrieval->irb_lower_count, retrieval->irb_value, &retrieval->irb_desc, lower, - (retrieval->irb_generic & irb_starting) != 0); + keyType); } } @@ -1085,7 +1105,11 @@ void BTR_insert(thread_db* tdbb, WIN* root_window, index_insertion* insertion) window.win_page = root->irt_rpt[idx->idx_id].getRoot(); bucket = (btree_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_index); - key = ret_key; + key.key_length = ret_key.key_length; + memcpy(key.key_data, ret_key.key_data, ret_key.key_length); + key.key_flags = ret_key.key_flags; + key.key_nulls = ret_key.key_nulls; + key.key_next.reset(ret_key.key_next.release()); } // the original page was marked as not garbage-collectable, but @@ -1172,7 +1196,7 @@ void BTR_insert(thread_db* tdbb, WIN* root_window, index_insertion* insertion) idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* idx, - temporary_key* key, const bool fuzzy, USHORT count) + temporary_key* key, const USHORT keyType, USHORT count) { /************************************** * @@ -1209,11 +1233,6 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id const USHORT maxKeyLength = dbb->getMaxIndexKeyLength(); try { - - const USHORT keyType = fuzzy ? - INTL_KEY_PARTIAL : - ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT); - // Special case single segment indices if (idx->idx_count == 1) @@ -1516,7 +1535,7 @@ idx_e BTR_make_key(thread_db* tdbb, const ValueExprNode* const* exprs, const index_desc* idx, temporary_key* key, - bool fuzzy) + USHORT keyType) { /************************************** * @@ -1545,13 +1564,11 @@ idx_e BTR_make_key(thread_db* tdbb, key->key_flags = 0; key->key_nulls = 0; + const bool fuzzy = (keyType == INTL_KEY_PARTIAL || keyType == INTL_KEY_MULTI_STARTING); const bool descending = (idx->idx_flags & idx_descending); const index_desc::idx_repeat* tail = idx->idx_rpt; - const USHORT keyType = fuzzy ? - INTL_KEY_PARTIAL : ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT); - const USHORT maxKeyLength = dbb->getMaxIndexKeyLength(); // If the index is a single segment index, don't sweat the compound stuff @@ -1567,7 +1584,10 @@ idx_e BTR_make_key(thread_db* tdbb, compress(tdbb, desc, key, tail->idx_itype, isNull, descending, keyType); if (fuzzy && (key->key_flags & key_empty)) + { key->key_length = 0; + key->key_next.reset(); + } } else { @@ -1604,37 +1624,64 @@ idx_e BTR_make_key(thread_db* tdbb, prior_length = (p - key->key_data); - const UCHAR* q = temp.key_data; - for (USHORT l = temp.key_length; l; --l, --stuff_count) - { - if (stuff_count == 0) - { - *p++ = idx->idx_count - n; - stuff_count = STUFF_COUNT; + fb_assert(n == count - 1 || !temp.key_next); - if (p - key->key_data >= maxKeyLength) + SSHORT save_stuff_count = stuff_count; + temporary_key* current_key = key; + temporary_key* temp_ptr = &temp; + + do + { + const UCHAR* q = temp_ptr->key_data; + + for (USHORT l = temp_ptr->key_length; l; --l, --stuff_count) + { + if (stuff_count == 0) + { + *p++ = idx->idx_count - n; + stuff_count = STUFF_COUNT; + + if (p - current_key->key_data >= maxKeyLength) + return idx_e_keytoobig; + } + + *p++ = *q++; + + if (p - current_key->key_data >= maxKeyLength) return idx_e_keytoobig; } - *p++ = *q++; + // AB: Fix bug SF #1242982 + // Equality search on first segment (integer) in compound indexes resulted + // in more scans on specific values (2^n, f.e. 131072) than needed. + if (!fuzzy && count != idx->idx_count && n == count - 1) + { + for (; stuff_count; --stuff_count) + { + *p++ = 0; - if (p - key->key_data >= maxKeyLength) - return idx_e_keytoobig; - } - } + if (p - current_key->key_data >= maxKeyLength) + return idx_e_keytoobig; + } + } - // AB: Fix bug SF #1242982 - // Equality search on first segment (integer) in compound indexes resulted - // in more scans on specific values (2^n, f.e. 131072) than needed. - if (!fuzzy && (n != idx->idx_count)) - { - for (; stuff_count; --stuff_count) - { - *p++ = 0; + current_key->key_length = p - current_key->key_data; - if (p - key->key_data >= maxKeyLength) - return idx_e_keytoobig; - } + if ((temp_ptr = temp_ptr->key_next.get())) + { + temporary_key* next_key = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key(); + next_key->key_length = 0; + next_key->key_flags = key->key_flags; + next_key->key_nulls = key->key_nulls; + memcpy(next_key->key_data, key->key_data, prior_length); + + current_key->key_next = next_key; + current_key = next_key; + p = current_key->key_data + prior_length; + + stuff_count = save_stuff_count; + } + } while (temp_ptr); } // dimitr: If the search is fuzzy and the last segment is empty, @@ -1642,8 +1689,6 @@ idx_e BTR_make_key(thread_db* tdbb, // the rule that every string starts with an empty string. if (fuzzy && (temp.key_flags & key_empty)) key->key_length = prior_length; - else - key->key_length = (p - key->key_data); if (is_key_empty) { @@ -1704,7 +1749,7 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke // If the index is a single segment index, don't sweat the compound stuff if ((idx->idx_count == 1) || (idx->idx_flags & idx_expressn)) { - compress(tdbb, &null_desc, key, tail->idx_itype, true, descending, false); + compress(tdbb, &null_desc, key, tail->idx_itype, true, descending, INTL_KEY_SORT); } else { @@ -1718,7 +1763,7 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke for (; stuff_count; --stuff_count) *p++ = 0; - compress(tdbb, &null_desc, &temp, tail->idx_itype, true, descending, false); + compress(tdbb, &null_desc, &temp, tail->idx_itype, true, descending, INTL_KEY_SORT); const UCHAR* q = temp.key_data; for (USHORT l = temp.key_length; l; --l, --stuff_count) @@ -2422,16 +2467,24 @@ static void compress(thread_db* tdbb, * Compress a data value into an index key. * **************************************/ - union { - INT64_KEY temp_int64_key; - double temp_double; - ULONG temp_ulong; - SLONG temp_slong; - SINT64 temp_sint64; - UCHAR temp_char[sizeof(INT64_KEY)]; - } temp; - bool temp_is_negative = false; - bool int64_key_op = false; + if (isNull) + { + const UCHAR pad = 0; + key->key_flags &= ~key_empty; + // AB: NULL should be threated as lowest value possible. + // Therefore don't complement pad when we have an ascending index. + if (descending) + { + // DESC NULLs are stored as 1 byte + key->key_data[0] = pad; + key->key_length = 1; + } + else + key->key_length = 0; // ASC NULLs are stored with no data + + fb_assert(!key->key_next); + return; + } // For descending index and new index structure we insert 0xFE at the beginning. // This is only done for values which begin with 0xFE (254) or 0xFF (255) and @@ -2442,96 +2495,151 @@ static void compress(thread_db* tdbb, const UCHAR desc_end_value_check = 0x00; // ~0xFF; const Database* dbb = tdbb->getDatabase(); - + bool first_key = true; + VaryStr buffer; + size_t multiKeyLength; + UCHAR* ptr; UCHAR* p = key->key_data; - if (isNull) - { - const UCHAR pad = 0; - key->key_flags &= ~key_empty; - // AB: NULL should be threated as lowest value possible. - // Therefore don't complement pad when we have an ascending index. - if (descending) - { - // DESC NULLs are stored as 1 byte - *p++ = pad; - key->key_length = (p - key->key_data); - } - else - key->key_length = 0; // ASC NULLs are stored with no data - - return; - } - if (itype == idx_string || itype == idx_byte_array || itype == idx_metadata || itype == idx_decimal || itype >= idx_first_intl_string) { - VaryStr buffer; - const UCHAR pad = (itype == idx_string) ? ' ' : 0; - UCHAR* ptr; + temporary_key* root_key = key; + bool has_next; - size_t length; - - if (itype == idx_decimal) + do { - Decimal128 dec = MOV_get_dec128(tdbb, desc); - length = dec.makeIndexKey(&buffer); - ptr = reinterpret_cast(buffer.vary_string); - } - else if (itype >= idx_first_intl_string || itype == idx_metadata) - { - DSC to; + size_t length; - // convert to an international byte array - to.dsc_dtype = dtype_text; - to.dsc_flags = 0; - to.dsc_sub_type = 0; - to.dsc_scale = 0; - to.dsc_ttype() = ttype_sort_key; - to.dsc_length = MIN(MAX_KEY, sizeof(buffer)); - ptr = to.dsc_address = reinterpret_cast(buffer.vary_string); - length = INTL_string_to_key(tdbb, itype, desc, &to, key_type); - } - else - length = MOV_get_string(tdbb, desc, &ptr, &buffer, MAX_KEY); + has_next = false; - if (length) - { - // clear key_empty flag, because length is >= 1 - key->key_flags &= ~key_empty; - - if (length > sizeof(key->key_data)) - length = sizeof(key->key_data); - - if (descending && ((*ptr == desc_end_value_prefix) || (*ptr == desc_end_value_check))) + if (first_key) { - *p++ = desc_end_value_prefix; - if ((length + 1) > sizeof(key->key_data)) - length = sizeof(key->key_data) - 1; + first_key = false; + + if (itype == idx_decimal) + { + Decimal128 dec = MOV_get_dec128(tdbb, desc); + length = dec.makeIndexKey(&buffer); + ptr = reinterpret_cast(buffer.vary_string); + } + else if (itype >= idx_first_intl_string || itype == idx_metadata) + { + DSC to; + + // convert to an international byte array + to.dsc_dtype = dtype_text; + to.dsc_flags = 0; + to.dsc_sub_type = 0; + to.dsc_scale = 0; + to.dsc_ttype() = ttype_sort_key; + to.dsc_length = MIN(MAX_COLUMN_SIZE, MAX_KEY * 4); + ptr = to.dsc_address = reinterpret_cast(buffer.vary_string); + multiKeyLength = length = INTL_string_to_key(tdbb, itype, desc, &to, key_type); + } + else + length = MOV_get_string(tdbb, desc, &ptr, &buffer, MAX_KEY); } - memcpy(p, ptr, length); - p += length; - } - else - { - // Leave key_empty flag, because the string is an empty string - if (descending && ((pad == desc_end_value_prefix) || (pad == desc_end_value_check))) - *p++ = desc_end_value_prefix; + if (key_type == INTL_KEY_MULTI_STARTING && multiKeyLength != 0) + { + fb_assert(ptr < (UCHAR*) buffer.vary_string + multiKeyLength); - *p++ = pad; - } + length = ptr[0] + ptr[1] * 256; + ptr += 2; - while (p > key->key_data) - { - if (*--p != pad) - break; - } + has_next = ptr + length < (UCHAR*) buffer.vary_string + multiKeyLength; + + if (descending) + { + if (has_next) + { + temporary_key* new_key = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key(); + new_key->key_length = 0; + new_key->key_flags = 0; + new_key->key_nulls = 0; + new_key->key_next = key == root_key ? NULL : key; + + key = new_key; + } + else if (key != root_key) + { + root_key->key_next = key; + key = root_key; + } + + p = key->key_data; + } + } + + const UCHAR pad = (itype == idx_string) ? ' ' : 0; + + if (length) + { + // clear key_empty flag, because length is >= 1 + key->key_flags &= ~key_empty; + + if (length > sizeof(key->key_data)) + length = sizeof(key->key_data); + + if (descending && ((*ptr == desc_end_value_prefix) || (*ptr == desc_end_value_check))) + { + *p++ = desc_end_value_prefix; + if ((length + 1) > sizeof(key->key_data)) + length = sizeof(key->key_data) - 1; + } + + memcpy(p, ptr, length); + p += length; + } + else + { + // Leave key_empty flag, because the string is an empty string + if (descending && ((pad == desc_end_value_prefix) || (pad == desc_end_value_check))) + *p++ = desc_end_value_prefix; + + *p++ = pad; + } + + while (p > key->key_data) + { + if (*--p != pad) + break; + } + + key->key_length = p + 1 - key->key_data; + + if (has_next && !descending) + { + temporary_key* new_key = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key(); + new_key->key_length = 0; + new_key->key_flags = 0; + new_key->key_nulls = 0; + key->key_next = new_key; + + key = new_key; + p = key->key_data; + } + + ptr += length; + } while (has_next); - key->key_length = p + 1 - key->key_data; return; } + p = key->key_data; + + union { + INT64_KEY temp_int64_key; + double temp_double; + ULONG temp_ulong; + SLONG temp_slong; + SINT64 temp_sint64; + UCHAR temp_char[sizeof(INT64_KEY)]; + } temp; + bool temp_is_negative = false; + bool int64_key_op = false; + // The index is numeric. // For idx_numeric... // Convert the value to a double precision number, diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 36cb89ab15..33b29c4270 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -150,6 +150,7 @@ struct temporary_key UCHAR key_flags; USHORT key_nulls; // bitmap of encountered null segments, // USHORT is enough to store MAX_INDEX_SEGMENTS bits + Firebird::AutoPtr key_next; // next key (INTL_KEY_MULTI_STARTING) }; @@ -223,6 +224,7 @@ const int irb_ignore_null_value_key = 8; // if lower bound is specified and upp const int irb_descending = 16; // Base index uses descending order const int irb_exclude_lower = 32; // exclude lower bound keys while scanning index const int irb_exclude_upper = 64; // exclude upper bound keys while scanning index +const int irb_multi_starting = 128; // Use INTL_KEY_MULTI_STARTING typedef Firebird::HalfStaticArray SelectivityList; diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index 83bbcde035..e33482ba34 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -38,15 +38,15 @@ DSC* BTR_eval_expression(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*, bool&) void BTR_evaluate(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::RecordBitmap**, Jrd::RecordBitmap*); UCHAR* BTR_find_leaf(Ods::btree_page*, Jrd::temporary_key*, UCHAR*, USHORT*, bool, bool); Ods::btree_page* BTR_find_page(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::win*, Jrd::index_desc*, - Jrd::temporary_key*, Jrd::temporary_key*); + Jrd::temporary_key*, Jrd::temporary_key*, bool = true); void BTR_insert(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); Jrd::idx_e BTR_key(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::Record*, Jrd::index_desc*, Jrd::temporary_key*, - const bool, USHORT = 0); + const USHORT, USHORT = 0); USHORT BTR_key_length(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); Ods::btree_page* BTR_left_handoff(Jrd::thread_db*, Jrd::win*, Ods::btree_page*, SSHORT); bool BTR_lookup(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::index_desc*, Jrd::RelationPages*); Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const Jrd::index_desc*, - Jrd::temporary_key*, bool); + Jrd::temporary_key*, USHORT); void BTR_make_null_key(Jrd::thread_db*, const Jrd::index_desc*, Jrd::temporary_key*); bool BTR_next_index(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*); void BTR_remove(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 8cdfe811a3..e066c48a47 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -405,7 +405,8 @@ void IDX_create_index(thread_db* tdbb, { Record* record = stack.pop(); - result = BTR_key(tdbb, relation, record, idx, &key, false); + result = BTR_key(tdbb, relation, record, idx, &key, + ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); if (result == idx_e_ok) { @@ -748,7 +749,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec1 = stack1.object(); - idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, false); + idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + if (result != idx_e_ok) { if (result == idx_e_conversion) @@ -765,7 +768,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec2 = stack2.object(); - result = BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, false); + result = BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + if (result != idx_e_ok) { if (result == idx_e_conversion) @@ -788,7 +793,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec3 = stack3.object(); - result = BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, false); + result = BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + if (result != idx_e_ok) { if (result == idx_e_conversion) @@ -860,14 +867,16 @@ void IDX_modify(thread_db* tdbb, idx_e error_code; if ((error_code = BTR_key(tdbb, new_rpb->rpb_relation, - new_rpb->rpb_record, &idx, &key1, false))) + new_rpb->rpb_record, &idx, &key1, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, new_rpb->rpb_record); } if ((error_code = BTR_key(tdbb, org_rpb->rpb_relation, - org_rpb->rpb_record, &idx, &key2, false))) + org_rpb->rpb_record, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, org_rpb->rpb_record); @@ -934,14 +943,16 @@ void IDX_modify_check_constraints(thread_db* tdbb, idx_e error_code; if ((error_code = BTR_key(tdbb, new_rpb->rpb_relation, - new_rpb->rpb_record, &idx, &key1, false))) + new_rpb->rpb_record, &idx, &key1, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, new_rpb->rpb_record); } if ((error_code = BTR_key(tdbb, org_rpb->rpb_relation, - org_rpb->rpb_record, &idx, &key2, false))) + org_rpb->rpb_record, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, org_rpb->rpb_record); @@ -1080,7 +1091,8 @@ void IDX_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) IndexErrorContext context(rpb->rpb_relation, &idx); idx_e error_code; - if ( (error_code = BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, false)) ) + if ((error_code = BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, rpb->rpb_record); @@ -1429,7 +1441,12 @@ static idx_e check_partner_index(thread_db* tdbb, // tmpIndex.idx_flags |= idx_unique; tmpIndex.idx_flags = (tmpIndex.idx_flags & ~idx_unique) | (partner_idx.idx_flags & idx_unique); temporary_key key; - result = BTR_key(tdbb, relation, record, &tmpIndex, &key, starting, segment); + + const USHORT keyType = starting ? + INTL_KEY_PARTIAL : + (tmpIndex.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT; + + result = BTR_key(tdbb, relation, record, &tmpIndex, &key, keyType, segment); CCH_RELEASE(tdbb, &window); // now check for current duplicates diff --git a/src/jrd/intl.cpp b/src/jrd/intl.cpp index 530531dbdd..d6cdd9aafa 100644 --- a/src/jrd/intl.cpp +++ b/src/jrd/intl.cpp @@ -1272,6 +1272,7 @@ USHORT INTL_string_to_key(thread_db* tdbb, case ttype_binary: case ttype_ascii: case ttype_none: + fb_assert(key_type != INTL_KEY_MULTI_STARTING); while (len-- && destLen-- > 0) *dest++ = *src++; // strip off ending pad characters @@ -1286,6 +1287,7 @@ USHORT INTL_string_to_key(thread_db* tdbb, break; default: TextType* obj = INTL_texttype_lookup(tdbb, ttype); + fb_assert(key_type != INTL_KEY_MULTI_STARTING || (obj->getFlags() & TEXTTYPE_MULTI_STARTING_KEY)); outlen = obj->string_to_key(len, src, pByte->dsc_length, dest, key_type); break; } diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index 61855e52b0..a0bb67e14b 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -495,7 +495,8 @@ struct IndexScratch unsigned lowerCount = 0; unsigned upperCount = 0; unsigned nonFullMatchedSegments = 0; - bool fuzzy = false; // Need to use INTL_KEY_PARTIAL in btr lookups + bool usePartialKey = false; // Use INTL_KEY_PARTIAL + bool useMultiStartingKeys = false; // Use INTL_KEY_MULTI_STARTING Firebird::ObjectsArray segments; }; diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 5db89d3c79..213ba19dbc 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -160,7 +160,8 @@ IndexScratch::IndexScratch(MemoryPool& p, const IndexScratch& other) lowerCount(other.lowerCount), upperCount(other.upperCount), nonFullMatchedSegments(other.nonFullMatchedSegments), - fuzzy(other.fuzzy), + usePartialKey(other.usePartialKey), + useMultiStartingKeys(other.useMultiStartingKeys), segments(p, other.segments) {} @@ -738,7 +739,8 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions, scratch.lowerCount = 0; scratch.upperCount = 0; scratch.nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; - scratch.fuzzy = false; + scratch.usePartialKey = false; + scratch.useMultiStartingKeys = false; if (scratch.candidate) { @@ -755,26 +757,34 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions, if (segment.scope == scope) scratch.scopeCandidate = true; - if (segment.scanType != segmentScanMissing && !(idx->idx_flags & idx_unique)) + const USHORT iType = idx->idx_rpt[j].idx_itype; + + if (iType >= idx_first_intl_string) { - const auto iType = idx->idx_rpt[j].idx_itype; + TextType* textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(iType)); - if (iType >= idx_first_intl_string) + if (segment.scanType != segmentScanMissing && !(idx->idx_flags & idx_unique)) { - TextType* textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(iType)); - if (textType->getFlags() & TEXTTYPE_SEPARATE_UNIQUE) { // ASF: Order is more precise than equivalence class. // We can't use the next segments, and we'll need to use // INTL_KEY_PARTIAL to construct the last segment's key. - scratch.fuzzy = true; + scratch.usePartialKey = true; } } + + if (segment.scanType == segmentScanStarting) + { + if (textType->getFlags() & TEXTTYPE_MULTI_STARTING_KEY) + scratch.useMultiStartingKeys = true; // use INTL_KEY_MULTI_STARTING + + scratch.usePartialKey = true; + } } // Check if this is the last usable segment - if (!scratch.fuzzy && + if (!scratch.usePartialKey && (segment.scanType == segmentScanEqual || segment.scanType == segmentScanEquivalent || segment.scanType == segmentScanMissing)) @@ -1032,9 +1042,15 @@ InversionNode* Retrieval::makeIndexScanNode(IndexScratch* indexScratch) const retrieval->irb_generic |= irb_exclude_upper; } - if (indexScratch->fuzzy) + if (indexScratch->usePartialKey) retrieval->irb_generic |= irb_starting; // Flag the need to use INTL_KEY_PARTIAL in btr. + if (indexScratch->useMultiStartingKeys) + { + // Flag the need to use INTL_KEY_MULTI_STARTING in btr. + retrieval->irb_generic |= irb_multi_starting | irb_starting; + } + // This index is never used for IS NULL, thus we can ignore NULLs // already at index scan. But this rule doesn't apply to nod_equiv // which requires NULLs to be found in the index. diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index a976ab3b8a..7fe9d5b60a 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -70,6 +70,12 @@ void IndexTableScan::open(thread_db* tdbb) const RLCK_reserve_relation(tdbb, request->req_transaction, m_relation, false); rpb->rpb_number.setValue(BOF_NUMBER); + + fb_assert(!impure->irsb_nav_lower); + impure->irsb_nav_current_lower = impure->irsb_nav_lower = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key; + + fb_assert(!impure->irsb_nav_upper); + impure->irsb_nav_current_upper = impure->irsb_nav_upper = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key; } void IndexTableScan::close(thread_db* tdbb) const @@ -107,6 +113,18 @@ void IndexTableScan::close(thread_db* tdbb) const impure->irsb_nav_btr_gc_lock = NULL; } impure->irsb_nav_page = 0; + + if (impure->irsb_nav_lower) + { + delete impure->irsb_nav_lower; + impure->irsb_nav_current_lower = impure->irsb_nav_lower = NULL; + } + + if (impure->irsb_nav_upper) + { + delete impure->irsb_nav_upper; + impure->irsb_nav_current_upper = impure->irsb_nav_upper = NULL; + } } #ifdef DEBUG_LCK_LIST // paranoid check @@ -142,120 +160,137 @@ bool IndexTableScan::getRecord(thread_db* tdbb) const setPage(tdbb, impure, NULL); } - index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); - - // find the last fetched position from the index - const USHORT pageSpaceID = m_relation->getPages(tdbb)->rel_pg_space_id; - win window(pageSpaceID, impure->irsb_nav_page); - - UCHAR* nextPointer = getPosition(tdbb, impure, &window); - if (!nextPointer) + // If this is the first time, start at the beginning + if (!impure->irsb_nav_page) { - rpb->rpb_number.setValid(false); - return false; - } - - temporary_key key; - memcpy(key.key_data, impure->irsb_nav_data, impure->irsb_nav_length); - - const IndexRetrieval* const retrieval = m_index->retrieval; - const USHORT flags = retrieval->irb_generic & (irb_descending | irb_partial | irb_starting); - - // set the upper (or lower) limit for navigational retrieval - temporary_key upper; - if (retrieval->irb_upper_count) - { - upper.key_length = impure->irsb_nav_upper_length; - memcpy(upper.key_data, impure->irsb_nav_data + m_length, upper.key_length); - } - - // Find the next interesting node. If necessary, skip to the next page. - RecordNumber number; - IndexNode node; - while (true) - { - Ods::btree_page* page = (Ods::btree_page*) window.win_buffer; - - UCHAR* pointer = nextPointer; - if (pointer) - { - node.readNode(pointer, true); - number = node.recordNumber; - } - - if (node.isEndLevel) - break; - - if (node.isEndBucket) - { - page = (Ods::btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); - nextPointer = page->btr_nodes + page->btr_jump_size; - continue; - } - - // Build the current key value from the prefix and current node data. - memcpy(key.key_data + node.prefix, node.data, node.length); - key.key_length = node.length + node.prefix; - - // Make sure we haven't hit the upper (or lower) limit. - if (retrieval->irb_upper_count && - compareKeys(idx, key.key_data, key.key_length, &upper, flags) > 0) - { - break; - } - - // skip this record if: - // 1) there is an inversion tree for this index and this record - // is not in the bitmap for the inversion, or - // 2) the record has already been visited - - if ((!(impure->irsb_flags & irsb_mustread) && - (!impure->irsb_nav_bitmap || - !RecordBitmap::test(*impure->irsb_nav_bitmap, number.getValue()))) || - RecordBitmap::test(impure->irsb_nav_records_visited, number.getValue())) - { - nextPointer = node.readNode(pointer, true); - continue; - } - - // reset the current navigational position in the index - rpb->rpb_number = number; - setPosition(tdbb, impure, rpb, &window, pointer, key); - - CCH_RELEASE(tdbb, &window); - - if (VIO_get(tdbb, rpb, request->req_transaction, request->req_pool)) - { - temporary_key value; - - const idx_e result = BTR_key(tdbb, m_relation, rpb->rpb_record, idx, &value, false); - - if (result != idx_e_ok) - { - IndexErrorContext context(m_relation, idx); - context.raise(tdbb, result, rpb->rpb_record); - } - - if (!compareKeys(idx, key.key_data, key.key_length, &value, 0)) - { - // mark in the navigational bitmap that we have visited this record - RBM_SET(tdbb->getDefaultPool(), &impure->irsb_nav_records_visited, - rpb->rpb_number.getValue()); - - rpb->rpb_number.setValid(true); - return true; - } - } - - nextPointer = getPosition(tdbb, impure, &window); - if (!nextPointer) + // initialize for a retrieval + if (!setupBitmaps(tdbb, impure)) { rpb->rpb_number.setValid(false); return false; } } - CCH_RELEASE(tdbb, &window); + index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); + + // find the last fetched position from the index + const USHORT pageSpaceID = m_relation->getPages(tdbb)->rel_pg_space_id; + win window(pageSpaceID, impure->irsb_nav_page); + + const IndexRetrieval* const retrieval = m_index->retrieval; + const USHORT flags = retrieval->irb_generic & (irb_descending | irb_partial | irb_starting); + + do + { + UCHAR* nextPointer = getPosition(tdbb, impure, &window); + if (!nextPointer) + { + rpb->rpb_number.setValid(false); + return false; + } + + temporary_key key; + memcpy(key.key_data, impure->irsb_nav_data, impure->irsb_nav_length); + + // set the upper (or lower) limit for navigational retrieval + temporary_key upper; + if (retrieval->irb_upper_count) + { + upper.key_length = impure->irsb_nav_upper_length; + memcpy(upper.key_data, impure->irsb_nav_data + m_length, upper.key_length); + } + + // Find the next interesting node. If necessary, skip to the next page. + RecordNumber number; + IndexNode node; + while (true) + { + Ods::btree_page* page = (Ods::btree_page*) window.win_buffer; + + UCHAR* pointer = nextPointer; + if (pointer) + { + node.readNode(pointer, true); + number = node.recordNumber; + } + + if (node.isEndLevel) + break; + + if (node.isEndBucket) + { + page = (Ods::btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + nextPointer = page->btr_nodes + page->btr_jump_size; + continue; + } + + // Build the current key value from the prefix and current node data. + memcpy(key.key_data + node.prefix, node.data, node.length); + key.key_length = node.length + node.prefix; + + // Make sure we haven't hit the upper (or lower) limit. + if (retrieval->irb_upper_count && + compareKeys(idx, key.key_data, key.key_length, &upper, flags) > 0) + { + break; + } + + // skip this record if: + // 1) there is an inversion tree for this index and this record + // is not in the bitmap for the inversion, or + // 2) the record has already been visited + + if ((!(impure->irsb_flags & irsb_mustread) && + (!impure->irsb_nav_bitmap || + !RecordBitmap::test(*impure->irsb_nav_bitmap, number.getValue()))) || + RecordBitmap::test(impure->irsb_nav_records_visited, number.getValue())) + { + nextPointer = node.readNode(pointer, true); + continue; + } + + // reset the current navigational position in the index + rpb->rpb_number = number; + setPosition(tdbb, impure, rpb, &window, pointer, key); + + CCH_RELEASE(tdbb, &window); + + if (VIO_get(tdbb, rpb, request->req_transaction, request->req_pool)) + { + temporary_key value; + + const idx_e result = BTR_key(tdbb, m_relation, rpb->rpb_record, idx, &value, + ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + + if (result != idx_e_ok) + { + IndexErrorContext context(m_relation, idx); + context.raise(tdbb, result, rpb->rpb_record); + } + + if (!compareKeys(idx, key.key_data, key.key_length, &value, 0)) + { + // mark in the navigational bitmap that we have visited this record + RBM_SET(tdbb->getDefaultPool(), &impure->irsb_nav_records_visited, + rpb->rpb_number.getValue()); + + rpb->rpb_number.setValid(true); + return true; + } + } + + nextPointer = getPosition(tdbb, impure, &window); + if (!nextPointer) + { + rpb->rpb_number.setValid(false); + return false; + } + } + + CCH_RELEASE(tdbb, &window); + + advanceStream(tdbb, impure, &window); + } while (true); // bof or eof must have been set at this point rpb->rpb_number.setValid(false); @@ -460,14 +495,34 @@ bool IndexTableScan::findSavedNode(thread_db* tdbb, Impure* impure, win* window, } } +void IndexTableScan::advanceStream(thread_db* tdbb, Impure* impure, win* window) const +{ + impure->irsb_nav_current_lower = impure->irsb_nav_current_lower->key_next.get(); + impure->irsb_nav_current_upper = impure->irsb_nav_current_upper->key_next.get(); + setPage(tdbb, impure, NULL); + window->win_page = 0; +} + UCHAR* IndexTableScan::getPosition(thread_db* tdbb, Impure* impure, win* window) const { - // If this is the first time, start at the beginning - if (!window->win_page.getPageNum()) + while (impure->irsb_nav_current_lower) { - return openStream(tdbb, impure, window); + UCHAR* position = getStreamPosition(tdbb, impure, window); + + if (position) + return position; + + advanceStream(tdbb, impure, window); } + return NULL; +} + +UCHAR* IndexTableScan::getStreamPosition(thread_db* tdbb, Impure* impure, win* window) const +{ + if (!window->win_page.getPageNum()) + return openStream(tdbb, impure, window); + // Re-fetch page and get incarnation counter Ods::btree_page* page = (Ods::btree_page*) CCH_FETCH(tdbb, window, LCK_read, pag_index); @@ -504,9 +559,9 @@ UCHAR* IndexTableScan::getPosition(thread_db* tdbb, Impure* impure, win* window) UCHAR* IndexTableScan::openStream(thread_db* tdbb, Impure* impure, win* window) const { - // initialize for a retrieval - if (!setupBitmaps(tdbb, impure)) - return NULL; + temporary_key* lower = impure->irsb_nav_current_lower; + temporary_key* upper = impure->irsb_nav_current_upper; + const bool firstKeys = lower == impure->irsb_nav_lower; setPage(tdbb, impure, NULL); impure->irsb_nav_length = 0; @@ -514,8 +569,7 @@ UCHAR* IndexTableScan::openStream(thread_db* tdbb, Impure* impure, win* window) // Find the starting leaf page const IndexRetrieval* const retrieval = m_index->retrieval; index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); - temporary_key lower, upper; - Ods::btree_page* page = BTR_find_page(tdbb, retrieval, window, idx, &lower, &upper); + Ods::btree_page* page = BTR_find_page(tdbb, retrieval, window, idx, lower, upper, firstKeys); setPage(tdbb, impure, window); // find the upper limit for the search @@ -525,12 +579,12 @@ UCHAR* IndexTableScan::openStream(thread_db* tdbb, Impure* impure, win* window) // If upper key length is greater than declared key length, we need // one "excess" byte for correct comparison. Without it there could // be false equality hits. - impure->irsb_nav_upper_length = MIN(m_length + 1, upper.key_length); - memcpy(impure->irsb_nav_data + m_length, upper.key_data, impure->irsb_nav_upper_length); + impure->irsb_nav_upper_length = MIN(m_length + 1, upper->key_length); + memcpy(impure->irsb_nav_data + m_length, upper->key_data, impure->irsb_nav_upper_length); } if (retrieval->irb_lower_count) - limit_ptr = &lower; + limit_ptr = lower; // If there is a starting descriptor, search down index to starting position. // This may involve sibling buckets if splits are in progress. If there diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index ac29e89a96..4b6f9c1600 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -205,6 +205,10 @@ namespace Jrd RecordBitmap** irsb_nav_bitmap; // bitmap for inversion tree RecordBitmap* irsb_nav_records_visited; // bitmap of records already retrieved BtrPageGCLock* irsb_nav_btr_gc_lock; // lock to prevent removal of currently walked index page + temporary_key* irsb_nav_lower; // lower (possible multiple) key + temporary_key* irsb_nav_upper; // upper (possible multiple) key + temporary_key* irsb_nav_current_lower; // current lower key + temporary_key* irsb_nav_current_upper; // current upper key USHORT irsb_nav_offset; // page offset of current index node USHORT irsb_nav_upper_length; // length of upper key value USHORT irsb_nav_length; // length of expanded key @@ -234,7 +238,9 @@ namespace Jrd private: int compareKeys(const index_desc*, const UCHAR*, USHORT, const temporary_key*, USHORT) const; bool findSavedNode(thread_db* tdbb, Impure* impure, win* window, UCHAR**) const; + void advanceStream(thread_db* tdbb, Impure* impure, win* window) const; UCHAR* getPosition(thread_db* tdbb, Impure* impure, win* window) const; + UCHAR* getStreamPosition(thread_db* tdbb, Impure* impure, win* window) const; UCHAR* openStream(thread_db* tdbb, Impure* impure, win* window) const; void setPage(thread_db* tdbb, Impure* impure, win* window) const; void setPosition(thread_db* tdbb, Impure* impure, record_param*, From 71200308bbd74d38bf90043cce769a9f5eb73e6b Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 17 Mar 2022 16:11:33 -0300 Subject: [PATCH 116/187] Misc. --- src/common/unicode_util.cpp | 26 ++++++++++++-------------- src/jrd/optimizer/Retrieval.cpp | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 417ac92d96..6d3a69252b 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -1620,7 +1620,7 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( USet* contractions = icu->usetOpen(1, 0); // status not verified here. - icu->ucolGetContractionsAndExpansions(partialCollator, contractions, NULL, false, &status); + icu->ucolGetContractionsAndExpansions(partialCollator, contractions, nullptr, false, &status); int contractionsCount = icu->usetGetItemCount(contractions); @@ -1643,7 +1643,7 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( for (int prefixLen = 1; prefixLen < len; ++prefixLen) { const Array str(reinterpret_cast(strChars), prefixLen); - SortKeyArray* keySet = obj->contractionsPrefix.get(str); + auto keySet = obj->contractionsPrefix.get(str); if (!keySet) { @@ -1667,7 +1667,7 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( for (bool found = accessor.getFirst(); found; found = accessor.getNext()) { - SortKeyArray& keySet = accessor.current()->second; + auto& keySet = accessor.current()->second; if (keySet.getCount() <= 1) continue; @@ -1682,8 +1682,8 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( continue; } - SortKeyArray::iterator firstKeyIt = keySet.begin(); - SortKeyArray::iterator lastKeyIt = --keySet.end(); + auto firstKeyIt = keySet.begin(); + auto lastKeyIt = --keySet.end(); const UCHAR* firstKeyDataIt = firstKeyIt->begin(); const UCHAR* lastKeyDataIt = lastKeyIt->begin(); @@ -1706,7 +1706,7 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( } else { - SortKeyArray::iterator secondKeyIt = ++keySet.begin(); + auto secondKeyIt = ++keySet.begin(); const UCHAR* secondKeyDataIt = secondKeyIt->begin(); const UCHAR* secondKeyDataEnd = secondKeyIt->end(); @@ -1751,8 +1751,8 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( keySet.clear(); - for (ObjectsArray >::iterator ckIt = commonKeys.begin(); ckIt != commonKeys.end(); ++ckIt) - keySet.add(*ckIt); + for (auto ck : commonKeys) + keySet.add(ck); } } @@ -1863,7 +1863,7 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src { for (int i = MIN(maxContractionsPrefixLength, srcLenLong); i > 0; --i) { - SortKeyArray* keys = contractionsPrefix.get(Array(src + srcLenLong - i, i)); + auto keys = contractionsPrefix.get(Array(src + srcLenLong - i, i)); if (keys) { @@ -1887,11 +1887,9 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src else prefixLen = 0; - for (SortKeyArray::const_iterator keyIt = keys->begin(); - keyIt != keys->end(); - ++keyIt) + for (const auto& keyIt : *keys) { - const ULONG keyLen = prefixLen + keyIt->getCount(); + const ULONG keyLen = prefixLen + keyIt.getCount(); if (keyLen > dstLen - 2 || keyLen > MAX_USHORT) return INTL_BAD_KEY_LENGTH; @@ -1902,7 +1900,7 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src if (dst != dstStart) memcpy(dst + 2, dstStart + 2, prefixLen); - memcpy(dst + 2 + prefixLen, keyIt->begin(), keyIt->getCount()); + memcpy(dst + 2 + prefixLen, keyIt.begin(), keyIt.getCount()); dst += 2 + keyLen; dstLen -= 2 + keyLen; } diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 213ba19dbc..24475d19ea 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -761,7 +761,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions, if (iType >= idx_first_intl_string) { - TextType* textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(iType)); + auto textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(iType)); if (segment.scanType != segmentScanMissing && !(idx->idx_flags & idx_unique)) { From bc26fb72feee1843ccd40c1132f3bf560c550877 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 18 Mar 2022 00:11:20 +0000 Subject: [PATCH 117/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 842b0eed15..1b4b301a2f 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:426 + FORMAL BUILD NUMBER:429 */ -#define PRODUCT_VER_STRING "5.0.0.426" -#define FILE_VER_STRING "WI-T5.0.0.426" -#define LICENSE_VER_STRING "WI-T5.0.0.426" -#define FILE_VER_NUMBER 5, 0, 0, 426 +#define PRODUCT_VER_STRING "5.0.0.429" +#define FILE_VER_STRING "WI-T5.0.0.429" +#define LICENSE_VER_STRING "WI-T5.0.0.429" +#define FILE_VER_NUMBER 5, 0, 0, 429 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "426" +#define FB_BUILD_NO "429" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 2153695e2f..4f29f75438 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=426 +BuildNum=429 NowAt=`pwd` cd `dirname $0` From 4bf1b808656c7130ad2f30530c72e7903f25fe99 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 20:44:33 -0300 Subject: [PATCH 118/187] Add stl-compatible allocator classes: PoolAllocator and std::allocator_traits specialization. --- src/common/classes/alloc.h | 131 +++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h index cf52648855..470ea439b9 100644 --- a/src/common/classes/alloc.h +++ b/src/common/classes/alloc.h @@ -57,6 +57,7 @@ #endif #include +#include #ifdef DEBUG_GDS_ALLOC #define FB_NEW new(*getDefaultMemoryPool(), __FILE__, __LINE__) @@ -456,7 +457,137 @@ namespace Firebird typedef AutoPtr AutoMemoryPool; + template + class PoolAllocator + { + template friend class PoolAllocator; + + public: + using value_type = T; + using size_type = size_t; + using pointer = T*; + using const_pointer = const T*; + using difference_type = std::ptrdiff_t; + using is_always_equal = std::true_type; + + template + struct rebind + { + typedef PoolAllocator other; + }; + + public: + PoolAllocator(MemoryPool& aPool) noexcept + : pool(aPool) + {} + + PoolAllocator(const PoolAllocator& o) noexcept + : pool(o.pool) + {} + + template + PoolAllocator(const PoolAllocator& o) noexcept + : pool(o.pool) + {} + + ~PoolAllocator() noexcept + {} + + public: + constexpr pointer allocate(size_type n, const void* hint = nullptr) + { + return static_cast(pool.allocate(n * sizeof(T) ALLOC_ARGS)); + } + + constexpr void deallocate(pointer p, size_type n) + { + pool.deallocate(p); + } + + constexpr size_type max_size() const noexcept + { + return size_t(-1) / sizeof(T); + } + + template + constexpr void construct(U* ptr, Args&&... args) + { + new ((void*) ptr) U(pool, std::forward(args)...); + } + + template + constexpr void destroy(U* ptr) + { + ptr->~U(); + } + + constexpr bool operator==(const PoolAllocator& o) const noexcept + { + return &pool == &o.pool; + } + + constexpr bool operator!=(const PoolAllocator& o) const noexcept + { + return &pool != &o.pool; + } + + private: + MemoryPool& pool; + }; } // namespace Firebird +template +struct std::allocator_traits> +{ + using Alloc = Firebird::PoolAllocator; + + using allocator_type = Alloc; + using value_type = typename Alloc::value_type; + using pointer = typename Alloc::pointer; + using const_pointer = typename Alloc::const_pointer; + using void_pointer = void*; + using const_void_pointer = const void*; + using size_type = typename Alloc::size_type; + using difference_type = typename Alloc::difference_type; + using reference = value_type&; + using const_reference = const value_type&; + + using is_always_equal = typename Alloc::is_always_equal; + + template + using rebind_alloc = typename Alloc::template rebind::other; + + template + using rebind_traits = allocator_traits>; + + static constexpr pointer allocate(Alloc& alloc, size_type size) + { + return alloc.allocate(size); + } + + static constexpr void deallocate(Alloc& alloc, pointer ptr, size_type size) + { + alloc.deallocate(ptr, size); + } + + template + static constexpr void construct(Alloc& alloc, T* ptr, Args&&... args) + { + alloc.construct(ptr, std::forward(args)...); + } + + template + static constexpr void destroy(Alloc& alloc, T* ptr) + { + alloc.destroy(ptr); + } + + static constexpr size_type max_size(const Alloc& alloc) noexcept + { + return alloc.max_size(); + } +}; + + #endif // CLASSES_ALLOC_H From f4bc127704082ba013559e050474940f175781bc Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 20:44:33 -0300 Subject: [PATCH 119/187] Add class DoublyLinkedList. It internally uses std::list with PoolAllocator. --- builds/win32/msvc15/common.vcxproj | 1 + builds/win32/msvc15/common.vcxproj.filters | 3 + src/common/classes/DoublyLinkedList.h | 149 +++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 src/common/classes/DoublyLinkedList.h diff --git a/builds/win32/msvc15/common.vcxproj b/builds/win32/msvc15/common.vcxproj index 90a062e04d..86baeb4be8 100644 --- a/builds/win32/msvc15/common.vcxproj +++ b/builds/win32/msvc15/common.vcxproj @@ -124,6 +124,7 @@ + diff --git a/builds/win32/msvc15/common.vcxproj.filters b/builds/win32/msvc15/common.vcxproj.filters index 8f41a256cf..700b1d4543 100644 --- a/builds/win32/msvc15/common.vcxproj.filters +++ b/builds/win32/msvc15/common.vcxproj.filters @@ -392,6 +392,9 @@ headers + + headers + headers diff --git a/src/common/classes/DoublyLinkedList.h b/src/common/classes/DoublyLinkedList.h new file mode 100644 index 0000000000..18bd8897c7 --- /dev/null +++ b/src/common/classes/DoublyLinkedList.h @@ -0,0 +1,149 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2022 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef CLASSES_DOUBLY_LINKED_LIST_H +#define CLASSES_DOUBLY_LINKED_LIST_H + +#include "../common/classes/alloc.h" +#include +#include + + +namespace Firebird +{ + +template +class DoublyLinkedList +{ +private: + using StdList = std::list>; + +public: + using Iterator = typename StdList::iterator; + using ConstIterator = typename StdList::const_iterator; + +public: + explicit DoublyLinkedList(MemoryPool& p) + : stdList(p) + { + } + +public: + constexpr T& front() noexcept + { + return stdList.front(); + } + + constexpr const T& front() const noexcept + { + return stdList.front(); + } + + constexpr T& back() noexcept + { + return stdList.back(); + } + + constexpr const T& back() const noexcept + { + return stdList.back(); + } + + constexpr Iterator begin() noexcept + { + return stdList.begin(); + } + + constexpr ConstIterator begin() const noexcept + { + return stdList.begin(); + } + + constexpr ConstIterator cbegin() const noexcept + { + return stdList.cbegin(); + } + + constexpr Iterator end() noexcept + { + return stdList.end(); + } + + constexpr ConstIterator end() const noexcept + { + return stdList.end(); + } + + constexpr ConstIterator cend() const noexcept + { + return stdList.cend(); + } + + constexpr bool isEmpty() const noexcept + { + return stdList.empty(); + } + + constexpr void clear() noexcept + { + stdList.clear(); + } + + constexpr void erase(Iterator pos) + { + stdList.erase(pos); + } + + constexpr void erase(ConstIterator pos) + { + stdList.erase(pos); + } + + constexpr void pushBack(const T& value) + { + stdList.push_back(value); + } + + constexpr void pushBack(T&& value) + { + stdList.push_back(std::move(value)); + } + + constexpr void splice(ConstIterator pos, DoublyLinkedList& other, ConstIterator it) + { + fb_assert(stdList.get_allocator() == other.stdList.get_allocator()); + stdList.splice(pos, other.stdList, it); + } + + constexpr void splice(ConstIterator pos, DoublyLinkedList&& other, ConstIterator it) + { + fb_assert(stdList.get_allocator() == other.stdList.get_allocator()); + stdList.splice(pos, std::move(other.stdList), it); + } + +private: + StdList stdList; +}; + +} // namespace Firebird + +#endif // CLASSES_DOUBLY_LINKED_LIST_H From c677ac8b5b605c415862b80a5e34d7851acf0a6d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 20:44:33 -0300 Subject: [PATCH 120/187] Added copy constructor to SortedObjectsArray. --- src/common/classes/objects_array.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/classes/objects_array.h b/src/common/classes/objects_array.h index d946b87976..ed9f27c050 100644 --- a/src/common/classes/objects_array.h +++ b/src/common/classes/objects_array.h @@ -483,6 +483,13 @@ namespace Firebird ObjectCmp> >() { } + explicit SortedObjectsArray(MemoryPool& p, const SortedObjectsArray& o) : + ObjectsArray >(p, o) + { + } + bool find(const ObjectKey& item, size_type& pos) const { const ObjectKey* const pItem = &item; From 0ecf1d71d59c90b53c5ebb0d82424e89a00ab78f Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 20:44:33 -0300 Subject: [PATCH 121/187] Misc. --- src/common/config/config_file.h | 2 +- src/jrd/flu.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/config/config_file.h b/src/common/config/config_file.h index 4699ff7a5a..51b3836a59 100644 --- a/src/common/config/config_file.h +++ b/src/common/config/config_file.h @@ -81,7 +81,7 @@ public: sub(par.sub), line(par.line) { } Parameter() - : AutoStorage(), name(getPool()), value(getPool()), sub(0), line(0) + : AutoStorage(), name(getPool()), value(getPool()), line(0) { } SINT64 asInteger() const; diff --git a/src/jrd/flu.h b/src/jrd/flu.h index d9a8bf7c26..45880a24b1 100644 --- a/src/jrd/flu.h +++ b/src/jrd/flu.h @@ -90,10 +90,10 @@ namespace Jrd public: typedef Firebird::Array LoadedModules; - Module() : interMod(0) { } + Module() + { } explicit Module(MemoryPool&) - : interMod(NULL) { } Module(MemoryPool&, const Module& m) From 4c9fffd9b931bc30209b1dc505ec4af5c197aead Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 4 Mar 2022 20:44:33 -0300 Subject: [PATCH 122/187] Compiled statement cache. --- builds/install/misc/firebird.conf | 13 + builds/win32/msvc15/engine.vcxproj | 2 + builds/win32/msvc15/engine.vcxproj.filters | 6 + src/common/classes/alloc.cpp | 10 + src/common/classes/alloc.h | 2 + src/common/config/config.cpp | 2 + src/common/config/config.h | 6 +- src/dsql/DsqlRequests.cpp | 3 + src/dsql/DsqlRequests.h | 10 - src/dsql/DsqlStatementCache.cpp | 285 +++++++++++++++++++++ src/dsql/DsqlStatementCache.h | 137 ++++++++++ src/dsql/DsqlStatements.cpp | 22 +- src/dsql/DsqlStatements.h | 30 ++- src/dsql/dsql.cpp | 39 ++- src/dsql/dsql.h | 22 +- src/jrd/Attachment.cpp | 6 +- src/jrd/Monitoring.cpp | 6 + src/jrd/Statement.cpp | 126 +++++---- src/jrd/Statement.h | 7 +- src/jrd/constants.h | 3 +- src/jrd/jrd.cpp | 19 +- src/jrd/lck.cpp | 1 + src/jrd/lck.h | 3 +- src/jrd/relations.h | 1 + src/jrd/req.h | 15 +- src/jrd/scl.h | 7 + src/jrd/trace/TraceObjects.cpp | 2 +- 27 files changed, 670 insertions(+), 115 deletions(-) create mode 100644 src/dsql/DsqlStatementCache.cpp create mode 100644 src/dsql/DsqlStatementCache.h diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index 54711f45cf..c35907338d 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -1019,6 +1019,19 @@ #GCPolicy = combined +# ---------------------------- +# Maximum statement cache size +# +# The maximum amount of RAM used to cache unused DSQL compiled statements. +# If set to 0 (zero), statement cache is disabled. +# +# Per-database configurable. +# +# Type: integer +# +#MaxStatementCacheSize = 2M + + # ---------------------------- # Security database # diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj index 94e4e4222b..4bc3e762f6 100644 --- a/builds/win32/msvc15/engine.vcxproj +++ b/builds/win32/msvc15/engine.vcxproj @@ -42,6 +42,7 @@ + @@ -190,6 +191,7 @@ + diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index 3356f663a6..80fe264b9f 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -144,6 +144,9 @@ DSQL + + DSQL + DSQL @@ -560,6 +563,9 @@ Header files + + Header files + Header files diff --git a/src/common/classes/alloc.cpp b/src/common/classes/alloc.cpp index 86c5a64ec9..efe4b49b2a 100644 --- a/src/common/classes/alloc.cpp +++ b/src/common/classes/alloc.cpp @@ -1850,6 +1850,11 @@ public: // Create memory pool instance static MemPool* createPool(MemPool* parent, MemoryStats& stats); + MemoryStats& getStatsGroup() noexcept + { + return *stats; + } + // Set statistics group for pool. Usage counters will be decremented from // previously set group and added to new void setStatsGroup(MemoryStats& stats) noexcept; @@ -2262,6 +2267,11 @@ void MemPool::setStatsGroup(MemoryStats& newStats) noexcept stats->increment_usage(sav_used_memory); } +MemoryStats& MemoryPool::getStatsGroup() noexcept +{ + return pool->getStatsGroup(); +} + void MemoryPool::setStatsGroup(MemoryStats& newStats) noexcept { pool->setStatsGroup(newStats); diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h index 470ea439b9..d133837206 100644 --- a/src/common/classes/alloc.h +++ b/src/common/classes/alloc.h @@ -213,6 +213,8 @@ public: // Get context pool for current thread of execution static MemoryPool* getContextPool(); + MemoryStats& getStatsGroup() noexcept; + // Set statistics group for pool. Usage counters will be decremented from // previously set group and added to new void setStatsGroup(MemoryStats& stats) noexcept; diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 6e17cf9b50..1e43ea4769 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -410,6 +410,8 @@ void Config::checkValues() checkIntForHiBound(KEY_TIP_CACHE_BLOCK_SIZE, MAX_ULONG, true); checkIntForLoBound(KEY_INLINE_SORT_THRESHOLD, 0, true); + + checkIntForLoBound(KEY_MAX_STATEMENT_CACHE_SIZE, 0, true); } diff --git a/src/common/config/config.h b/src/common/config/config.h index c5b86233d1..a7b1febc4c 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -188,6 +188,7 @@ enum ConfigKey KEY_USE_FILESYSTEM_CACHE, KEY_INLINE_SORT_THRESHOLD, KEY_TEMP_PAGESPACE_DIR, + KEY_MAX_STATEMENT_CACHE_SIZE, MAX_CONFIG_KEY // keep it last }; @@ -302,7 +303,8 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_STRING, "DataTypeCompatibility", false, nullptr}, {TYPE_BOOLEAN, "UseFileSystemCache", false, true}, {TYPE_INTEGER, "InlineSortThreshold", false, 1000}, // bytes - {TYPE_STRING, "TempTableDirectory", false, ""} + {TYPE_STRING, "TempTableDirectory", false, ""}, + {TYPE_INTEGER, "MaxStatementCacheSize", false, 2 * 1048576} // bytes }; @@ -624,6 +626,8 @@ public: CONFIG_GET_PER_DB_KEY(ULONG, getInlineSortThreshold, KEY_INLINE_SORT_THRESHOLD, getInt); CONFIG_GET_PER_DB_STR(getTempPageSpaceDirectory, KEY_TEMP_PAGESPACE_DIR); + + CONFIG_GET_PER_DB_INT(getMaxStatementCacheSize, KEY_MAX_STATEMENT_CACHE_SIZE); }; // Implementation of interface to access master configuration file diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp index c5b34e8503..2bdb14f154 100644 --- a/src/dsql/DsqlRequests.cpp +++ b/src/dsql/DsqlRequests.cpp @@ -23,6 +23,7 @@ #include "../dsql/DsqlRequests.h" #include "../dsql/dsql.h" #include "../dsql/DsqlBatch.h" +#include "../dsql/DsqlStatementCache.h" #include "../dsql/Nodes.h" #include "../jrd/Statement.h" #include "../jrd/req.h" @@ -1035,6 +1036,8 @@ void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, { AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); + req_dbb->dbb_attachment->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); + node->executeDdl(tdbb, internalScratch, req_transaction); const bool isInternalRequest = diff --git a/src/dsql/DsqlRequests.h b/src/dsql/DsqlRequests.h index 451936a900..860f46433a 100644 --- a/src/dsql/DsqlRequests.h +++ b/src/dsql/DsqlRequests.h @@ -75,11 +75,6 @@ public: return nullptr; } - virtual bool isDml() const - { - return false; - } - virtual DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, Firebird::IMessageMetadata* outMeta, ULONG flags) @@ -165,11 +160,6 @@ public: return request; } - bool isDml() const override - { - return true; - } - DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, Firebird::IMessageMetadata* outMeta, ULONG flags) override; diff --git a/src/dsql/DsqlStatementCache.cpp b/src/dsql/DsqlStatementCache.cpp new file mode 100644 index 0000000000..ecec0e2c7a --- /dev/null +++ b/src/dsql/DsqlStatementCache.cpp @@ -0,0 +1,285 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2022 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../dsql/DsqlStatementCache.h" +#include "../dsql/DsqlStatements.h" +#include "../jrd/Attachment.h" +#include "../jrd/Statement.h" +#include "../jrd/lck.h" +#include "../jrd/lck_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +// Class DsqlStatementCache + +DsqlStatementCache::DsqlStatementCache(MemoryPool& o, Attachment* attachment) + : PermanentStorage(o), + map(o), + activeStatementList(o), + inactiveStatementList(o) +{ + const auto dbb = attachment->att_database; + maxCacheSize = dbb->dbb_config->getMaxStatementCacheSize(); +} + +DsqlStatementCache::~DsqlStatementCache() +{ + purge(JRD_get_thread_data()); +} + +int DsqlStatementCache::blockingAst(void* astObject) +{ +#ifdef DSQL_STATEMENT_CACHE_DEBUG + printf("DsqlStatementCache::blockingAst()\n"); +#endif + + const auto self = static_cast(astObject); + + try + { + const auto dbb = self->lock->lck_dbb; + AsyncContextHolder tdbb(dbb, FB_FUNCTION, self->lock); + + self->purge(tdbb); + } + catch (const Exception&) + {} // no-op + + return 0; +} + +RefPtr DsqlStatementCache::getStatement(thread_db* tdbb, const string& text, USHORT clientDialect, + bool isInternalRequest) +{ + RefStrPtr key; + buildStatementKey(tdbb, key, text, clientDialect, isInternalRequest); + + if (const auto entryPtr = map.get(key)) + { + const auto entry = *entryPtr; + auto dsqlStatement(entry->dsqlStatement); + + string verifyKey; + buildVerifyKey(tdbb, verifyKey, isInternalRequest); + + FB_SIZE_T verifyPos; + if (!entry->verifyCache.find(verifyKey, verifyPos)) + { + dsqlStatement->getStatement()->verifyAccess(tdbb); + entry->verifyCache.insert(verifyPos, verifyKey); + } + + if (!entry->active) + { + entry->dsqlStatement->setCacheKey(key); + // Active statement has cacheKey and will tell us when it's going to be released. + entry->dsqlStatement->release(); + + entry->active = true; + + cacheSize -= entry->size; + + activeStatementList.splice(activeStatementList.end(), inactiveStatementList, entry); + } + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + dump(); +#endif + + return dsqlStatement; + } + + return {}; +} + +void DsqlStatementCache::putStatement(thread_db* tdbb, const string& text, USHORT clientDialect, + bool isInternalRequest, RefPtr dsqlStatement) +{ + fb_assert(dsqlStatement->isDml()); + + const unsigned statementSize = dsqlStatement->getSize(); + + RefStrPtr key; + buildStatementKey(tdbb, key, text, clientDialect, isInternalRequest); + + StatementEntry newStatement(getPool()); + newStatement.key = key; + newStatement.size = statementSize; + newStatement.dsqlStatement = std::move(dsqlStatement); + newStatement.active = true; + + string verifyKey; + buildVerifyKey(tdbb, verifyKey, isInternalRequest); + newStatement.verifyCache.add(verifyKey); + + newStatement.dsqlStatement->setCacheKey(key); + // Active statement has cacheKey and will tell us when it's going to be released. + newStatement.dsqlStatement->release(); + + activeStatementList.pushBack(std::move(newStatement)); + map.put(key, --activeStatementList.end()); + + if (!lock) + { + lock = FB_NEW_RPT(getPool(), 0) Lock(tdbb, 0, LCK_dsql_statement_cache, this, blockingAst); + LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT); + } + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + dump(); +#endif +} + +void DsqlStatementCache::statementGoingInactive(Firebird::RefStrPtr& key) +{ + const auto entryPtr = map.get(key); + + if (!entryPtr) + { + fb_assert(false); + return; + } + + const auto entry = *entryPtr; + + fb_assert(entry->active); + entry->active = false; + entry->size = entry->dsqlStatement->getSize(); // update size + + inactiveStatementList.splice(inactiveStatementList.end(), activeStatementList, entry); + + cacheSize += entry->size; + + if (cacheSize > maxCacheSize) + shrink(); +} + +void DsqlStatementCache::purge(thread_db* tdbb) +{ + for (auto& entry : activeStatementList) + { + entry.dsqlStatement->addRef(); + entry.dsqlStatement->resetCacheKey(); + } + + map.clear(); + activeStatementList.clear(); + inactiveStatementList.clear(); + + cacheSize = 0; + + if (lock) + { + LCK_release(tdbb, lock); + lock.reset(); + } +} + +void DsqlStatementCache::purgeAllAttachments(thread_db* tdbb) +{ + if (lock) + LCK_convert(tdbb, lock, LCK_EX, LCK_WAIT); + else + { + lock = FB_NEW_RPT(getPool(), 0) Lock(tdbb, 0, LCK_dsql_statement_cache, this, blockingAst); + LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT); + } + + purge(tdbb); +} + +void DsqlStatementCache::buildStatementKey(thread_db* tdbb, RefStrPtr& key, const string& text, USHORT clientDialect, + bool isInternalRequest) +{ + const auto attachment = tdbb->getAttachment(); + + const SSHORT charSetId = isInternalRequest ? CS_METADATA : attachment->att_charset; + + key = FB_NEW_POOL(getPool()) RefString(getPool()); + + key->resize(1 + sizeof(charSetId) + text.length()); + char* p = key->begin(); + *p = (clientDialect << 1) | int(isInternalRequest); + memcpy(p + 1, &charSetId, sizeof(charSetId)); + memcpy(p + 1 + sizeof(charSetId), text.c_str(), text.length()); +} + +void DsqlStatementCache::buildVerifyKey(thread_db* tdbb, string& key, bool isInternalRequest) +{ + key.clear(); + + const auto attachment = tdbb->getAttachment(); + + if (isInternalRequest || !attachment->att_user) + return; + + const auto& roles = attachment->att_user->getGrantedRoles(tdbb); + + string roleStr; + + for (const auto& role : roles) + { + roleStr.printf("%d,%s,", int(role.length()), role.c_str()); + key += roleStr; + } +} + +void DsqlStatementCache::shrink() +{ +#ifdef DSQL_STATEMENT_CACHE_DEBUG + printf("DsqlStatementCache::shrink() - cacheSize: %u, maxCacheSize: %u\n\n", cacheSize, maxCacheSize); +#endif + + while (cacheSize > maxCacheSize && !inactiveStatementList.isEmpty()) + { + const auto& front = inactiveStatementList.front(); + map.remove(front.key); + cacheSize -= front.size; + inactiveStatementList.erase(inactiveStatementList.begin()); + } + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + dump(); +#endif +} + +#ifdef DSQL_STATEMENT_CACHE_DEBUG +void DsqlStatementCache::dump() +{ + printf("DsqlStatementCache::dump() - cacheSize: %u, maxCacheSize: %u\n\n", cacheSize, maxCacheSize); + + printf("\tactive:\n"); + + for (auto& entry : activeStatementList) + printf("\t\tsize: %u; text: %s\n", entry.size, entry.dsqlStatement->getSqlText()->c_str()); + + printf("\n\tinactive:\n"); + + for (auto& entry : inactiveStatementList) + printf("\t\tsize: %u; text: %s\n", entry.size, entry.dsqlStatement->getSqlText()->c_str()); + + printf("\n"); +} +#endif diff --git a/src/dsql/DsqlStatementCache.h b/src/dsql/DsqlStatementCache.h new file mode 100644 index 0000000000..da6e3f793c --- /dev/null +++ b/src/dsql/DsqlStatementCache.h @@ -0,0 +1,137 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2022 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef DSQL_STATEMENT_CACHE_H +#define DSQL_STATEMENT_CACHE_H + +///#define DSQL_STATEMENT_CACHE_DEBUG 1 + +#include "../common/classes/alloc.h" +#include "../common/classes/DoublyLinkedList.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" +#include "../common/classes/objects_array.h" +#include "../common/classes/RefCounted.h" + +namespace Jrd { + + +class Attachment; +class DsqlStatement; +class Lock; +class thread_db; + + +class DsqlStatementCache final : public Firebird::PermanentStorage +{ +private: + struct StatementEntry + { + explicit StatementEntry(MemoryPool& p) + : verifyCache(p) + { + } + + StatementEntry(MemoryPool& p, StatementEntry&& o) + : key(std::move(o.key)), + dsqlStatement(std::move(o.dsqlStatement)), + verifyCache(p, std::move(o.verifyCache)), + size(o.size), + active(o.active) + { + } + + StatementEntry(const StatementEntry&) = delete; + StatementEntry& operator=(const StatementEntry&) = delete; + + Firebird::RefStrPtr key; + Firebird::RefPtr dsqlStatement; + Firebird::SortedObjectsArray verifyCache; + unsigned size = 0; + bool active = true; + }; + + class RefStrPtrComparator + { + public: + static bool greaterThan(const Firebird::RefStrPtr& i1, const Firebird::RefStrPtr& i2) + { + return *i1 > *i2; + } + }; + +public: + explicit DsqlStatementCache(MemoryPool& o, Attachment* attachment); + ~DsqlStatementCache(); + + DsqlStatementCache(const DsqlStatementCache&) = delete; + DsqlStatementCache& operator=(const DsqlStatementCache&) = delete; + +private: + static int blockingAst(void* astObject); + +public: + bool isActive() const + { + return maxCacheSize > 0; + } + + Firebird::RefPtr getStatement(thread_db* tdbb, const Firebird::string& text, + USHORT clientDialect, bool isInternalRequest); + + void putStatement(thread_db* tdbb, const Firebird::string& text, USHORT clientDialect, bool isInternalRequest, + Firebird::RefPtr dsqlStatement); + + void statementGoingInactive(Firebird::RefStrPtr& key); + + void purge(thread_db* tdbb); + void purgeAllAttachments(thread_db* tdbb); + +private: + void buildStatementKey(thread_db* tdbb, Firebird::RefStrPtr& key, const Firebird::string& text, + USHORT clientDialect, bool isInternalRequest); + + void buildVerifyKey(thread_db* tdbb, Firebird::string& key, bool isInternalRequest); + + void shrink(); + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + void dump(); +#endif + +private: + Firebird::NonPooledMap< + Firebird::RefStrPtr, + Firebird::DoublyLinkedList::Iterator, + RefStrPtrComparator + > map; + Firebird::DoublyLinkedList activeStatementList; + Firebird::DoublyLinkedList inactiveStatementList; + Firebird::AutoPtr lock; + unsigned maxCacheSize = 0; + unsigned cacheSize = 0; +}; + + +} // namespace Jrd + +#endif // DSQL_STATEMENT_CACHE_H diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp index 9f51ce4153..9d18ac74b7 100644 --- a/src/dsql/DsqlStatements.cpp +++ b/src/dsql/DsqlStatements.cpp @@ -24,6 +24,7 @@ #include "../dsql/dsql.h" #include "../dsql/Nodes.h" #include "../dsql/DsqlCompilerScratch.h" +#include "../dsql/DsqlStatementCache.h" #include "../jrd/Statement.h" #include "../dsql/errd_proto.h" #include "../dsql/gen_proto.h" @@ -58,12 +59,22 @@ void DsqlStatement::rethrowDdlException(status_exception& ex, bool metadataUpdat int DsqlStatement::release() { fb_assert(refCounter.value() > 0); - const int refCnt = --refCounter; + int refCnt = --refCounter; if (!refCnt) { - doRelease(); - dsqlAttachment->deletePool(&getPool()); + if (cacheKey) + { + refCnt = ++refCounter; + auto key = cacheKey; + cacheKey = nullptr; + dsqlAttachment->dbb_statement_cache->statementGoingInactive(key); + } + else + { + doRelease(); + dsqlAttachment->deletePool(&getPool()); + } } return refCnt; @@ -119,6 +130,11 @@ void DsqlDmlStatement::doRelease() DsqlStatement::doRelease(); } +unsigned DsqlDmlStatement::getSize() const +{ + return DsqlStatement::getSize() + statement->getSize(); +} + void DsqlDmlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) { { // scope diff --git a/src/dsql/DsqlStatements.h b/src/dsql/DsqlStatements.h index fc88b53662..42d18d01a1 100644 --- a/src/dsql/DsqlStatements.h +++ b/src/dsql/DsqlStatements.h @@ -26,6 +26,7 @@ #include "../common/classes/array.h" #include "../common/classes/fb_string.h" #include "../common/classes/NestConst.h" +#include "../common/classes/RefCounted.h" #include "../jrd/jrd.h" #include "../jrd/ntrace.h" #include "../dsql/DsqlRequests.h" @@ -67,14 +68,15 @@ public: static void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate, DdlNode* node); public: - DsqlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment) - : PermanentStorage(p), + DsqlStatement(MemoryPool& pool, dsql_dbb* aDsqlAttachment) + : PermanentStorage(pool), dsqlAttachment(aDsqlAttachment), type(TYPE_SELECT), flags(0), blrVersion(5), - ports(p) + ports(pool) { + pool.setStatsGroup(memoryStats); } protected: @@ -133,7 +135,15 @@ public: const dsql_par* getEof() const { return eof; } void setEof(dsql_par* value) { eof = value; } + void setCacheKey(Firebird::RefStrPtr& value) { cacheKey = value; } + void resetCacheKey() { cacheKey = nullptr; } + public: + virtual bool isDml() const + { + return false; + } + virtual Statement* getStatement() const { return nullptr; @@ -149,6 +159,11 @@ public: return true; } + virtual unsigned getSize() const + { + return (unsigned) memoryStats.getCurrentUsage(); + } + virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) = 0; virtual DsqlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) = 0; @@ -157,11 +172,13 @@ protected: protected: dsql_dbb* dsqlAttachment; + Firebird::MemoryStats memoryStats; Type type; // Type of statement ULONG flags; // generic flag unsigned blrVersion; Firebird::RefStrPtr sqlText; Firebird::RefStrPtr orgText; + Firebird::RefStrPtr cacheKey; Firebird::Array ports; // Port messages dsql_msg* sendMsg = nullptr; // Message to be sent to start request dsql_msg* receiveMsg = nullptr; // Per record message to be received @@ -182,11 +199,18 @@ public: } public: + bool isDml() const override + { + return true; + } + Statement* getStatement() const override { return statement; } + unsigned getSize() const override; + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; DsqlDmlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 1f77edb1b8..0d60b0aca8 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -72,6 +72,7 @@ #include "../common/utils_proto.h" #include "../common/StatusArg.h" #include "../dsql/DsqlBatch.h" +#include "../dsql/DsqlStatementCache.h" #ifdef HAVE_CTYPE_H #include @@ -109,6 +110,21 @@ namespace IMPLEMENT_TRACE_ROUTINE(dsql_trace, "DSQL") #endif +dsql_dbb::dsql_dbb(MemoryPool& p, Attachment* attachment) + : dbb_relations(p), + dbb_procedures(p), + dbb_functions(p), + dbb_charsets(p), + dbb_collations(p), + dbb_charsets_by_id(p), + dbb_cursors(p), + dbb_pool(p), + dbb_dfl_charset(p) +{ + dbb_attachment = attachment; + dbb_statement_cache = FB_NEW_POOL(p) DsqlStatementCache(p, dbb_attachment); +} + dsql_dbb::~dsql_dbb() { } @@ -411,8 +427,7 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment) return attachment->att_dsql_instance; MemoryPool& pool = *attachment->createPool(); - dsql_dbb* const database = FB_NEW_POOL(pool) dsql_dbb(pool); - database->dbb_attachment = attachment; + dsql_dbb* const database = FB_NEW_POOL(pool) dsql_dbb(pool, attachment); attachment->att_dsql_instance = database; INI_init_dsql(tdbb, database); @@ -497,12 +512,24 @@ static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* databas Arg::Gds(isc_sql_too_long) << Arg::Num(MAX_SQL_LENGTH)); } + string textStr(text, textLength); + const bool isStatementCacheActive = database->dbb_statement_cache->isActive(); + + RefPtr dsqlStatement; + + if (isStatementCacheActive) + { + dsqlStatement = database->dbb_statement_cache->getStatement(tdbb, textStr, clientDialect, isInternalRequest); + + if (dsqlStatement) + return dsqlStatement; + } + // allocate the statement block, then prepare the statement MemoryPool* scratchPool = nullptr; DsqlCompilerScratch* scratch = nullptr; MemoryPool* statementPool = database->createPool(); - RefPtr dsqlStatement; Jrd::ContextPoolHolder statementContext(tdbb, statementPool); try @@ -593,6 +620,12 @@ static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* databas if (!isInternalRequest && dsqlStatement->mustBeReplicated()) dsqlStatement->setOrgText(text, textLength); + if (isStatementCacheActive && dsqlStatement->isDml()) + { + database->dbb_statement_cache->putStatement(tdbb, + textStr, clientDialect, isInternalRequest, dsqlStatement); + } + return dsqlStatement; } catch (const Exception&) diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 7ca31a3bc1..067ae93c61 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -77,12 +77,9 @@ namespace Jrd class Attachment; class Database; class DsqlCompilerScratch; - class DsqlDmlStatement; - class DdlNode; + class DsqlStatement; + class DsqlStatementCache; class RseNode; - class StmtNode; - class TransactionNode; - class SessionManagementNode; class ValueExprNode; class ValueListNode; class WindowClause; @@ -92,7 +89,6 @@ namespace Jrd struct bid; class dsql_ctx; - class dsql_msg; class dsql_par; class dsql_map; class dsql_intlsym; @@ -130,24 +126,14 @@ public: Firebird::LeftPooledMap dbb_collations; // known collations in database Firebird::NonPooledMap dbb_charsets_by_id; // charsets sorted by charset_id Firebird::LeftPooledMap dbb_cursors; // known cursors in database + Firebird::AutoPtr dbb_statement_cache; MemoryPool& dbb_pool; // The current pool for the dbb Attachment* dbb_attachment; MetaName dbb_dfl_charset; bool dbb_no_charset; - explicit dsql_dbb(MemoryPool& p) - : dbb_relations(p), - dbb_procedures(p), - dbb_functions(p), - dbb_charsets(p), - dbb_collations(p), - dbb_charsets_by_id(p), - dbb_cursors(p), - dbb_pool(p), - dbb_dfl_charset(p) - {} - + dsql_dbb(MemoryPool& p, Attachment* attachment); ~dsql_dbb(); MemoryPool* createPool() diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index 9d3cbd5915..fcb946a9b6 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -145,6 +145,8 @@ void Jrd::Attachment::destroy(Attachment* const attachment) MemoryPool* Jrd::Attachment::createPool() { MemoryPool* const pool = MemoryPool::createPool(att_pool, att_memory_stats); + auto stats = FB_NEW_POOL(*pool) MemoryStats(&att_memory_stats); + pool->setStatsGroup(*stats); att_pools.add(pool); return pool; } @@ -154,9 +156,7 @@ void Jrd::Attachment::deletePool(MemoryPool* pool) { if (pool) { - FB_SIZE_T pos; - if (att_pools.find(pool, pos)) - att_pools.remove(pos); + att_pools.findAndRemove(pool); #ifdef DEBUG_LCK_LIST // hvlad: this could be slow, use only when absolutely necessary diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index 17aa3438ec..ece65b9d30 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -1172,7 +1172,13 @@ void Monitoring::putStatement(SnapshotData::DumpRecord& record, const Statement* record.storeInteger(f_mon_cmp_stmt_type, obj_trigger); } + // statistics + const int stat_id = fb_utils::genUniqueId(); + record.storeGlobalId(f_mon_cmp_stmt_stat_id, getGlobalId(stat_id)); + record.write(); + + putMemoryUsage(record, statement->pool->getStatsGroup(), stat_id, stat_cmp_statement); } diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 0ac1683f8d..b876daced9 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -74,8 +74,7 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) localTables(*p), invariants(*p), blr(*p), - mapFieldInfo(*p), - mapItemInfo(*p) + mapFieldInfo(*p) { try { @@ -86,9 +85,16 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) static_cast(csb->csb_node) : NULL; accessList = csb->csb_access; + csb->csb_access.clear(); + externalList = csb->csb_external; + csb->csb_external.clear(); + mapFieldInfo.takeOwnership(csb->csb_map_field_info); + resources = csb->csb_resources; // Assign array contents + csb->csb_resources.clear(); + impureSize = csb->csb_impure; //if (csb->csb_g_flags & csb_blr_version4) @@ -155,19 +161,22 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) // make a vector of all used RSEs fors = csb->csb_fors; + csb->csb_fors.clear(); localTables = csb->csb_localTables; + csb->csb_localTables.clear(); // make a vector of all invariant-type nodes, so that we will // be able to easily reinitialize them when we restart the request invariants.join(csb->csb_invariants); + csb->csb_invariants.clear(); rpbsSetup.grow(csb->csb_n_stream); - CompilerScratch::csb_repeat* tail = csb->csb_rpt.begin(); - const CompilerScratch::csb_repeat* const streams_end = tail + csb->csb_n_stream; + auto tail = csb->csb_rpt.begin(); + const auto* const streams_end = tail + csb->csb_n_stream; - for (record_param* rpb = rpbsSetup.begin(); tail < streams_end; ++rpb, ++tail) + for (auto rpb = rpbsSetup.begin(); tail < streams_end; ++rpb, ++tail) { // fetch input stream for update if all booleans matched against indices if ((tail->csb_flags & csb_update) && !(tail->csb_flags & csb_unmatched)) @@ -186,6 +195,22 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) delete tail->csb_fields; tail->csb_fields = NULL; } + + if (csb->csb_variables) + csb->csb_variables->clear(); + + csb->csb_current_nodes.free(); + csb->csb_current_for_nodes.free(); + csb->csb_computing_fields.free(); + csb->csb_variables_used_in_subroutines.free(); + csb->csb_dbg_info.reset(); + csb->csb_map_item_info.clear(); + csb->csb_message_pad.clear(); + csb->subFunctions.clear(); + csb->subProcedures.clear(); + csb->outerMessagesMap.clear(); + csb->outerVarsMap.clear(); + csb->csb_rpt.free(); } catch (Exception&) { @@ -206,13 +231,15 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool DEV_BLKCHK(csb, type_csb); SET_TDBB(tdbb); - Database* const dbb = tdbb->getDatabase(); + const auto dbb = tdbb->getDatabase(); fb_assert(dbb); - Request* const old_request = tdbb->getRequest(); - tdbb->setRequest(NULL); + const auto attachment = tdbb->getAttachment(); - Statement* statement = NULL; + const auto old_request = tdbb->getRequest(); + tdbb->setRequest(nullptr); + + Statement* statement = nullptr; try { @@ -277,7 +304,7 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool // Build the statement and the final request block. - MemoryPool* const pool = tdbb->getDefaultPool(); + const auto pool = tdbb->getDefaultPool(); statement = FB_NEW_POOL(*pool) Statement(tdbb, pool, csb); @@ -288,12 +315,8 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool if (statement) { // Release sub statements. - for (Statement** subStatement = statement->subStatements.begin(); - subStatement != statement->subStatements.end(); - ++subStatement) - { - (*subStatement)->release(tdbb); - } + for (auto subStatement : statement->subStatements) + subStatement->release(tdbb); } ex.stuffException(tdbb->tdbb_status_vector); @@ -302,9 +325,14 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool } if (internalFlag) + { statement->flags |= FLAG_INTERNAL; + statement->charSetId = CS_METADATA; + } + else + statement->charSetId = attachment->att_charset; - tdbb->getAttachment()->att_statements.add(statement); + attachment->att_statements.add(statement); return statement; } @@ -402,17 +430,11 @@ Request* Statement::getRequest(thread_db* tdbb, USHORT level) if (level < requests.getCount() && requests[level]) return requests[level]; - requests.grow(level + 1); - - MemoryStats* const parentStats = (flags & FLAG_INTERNAL) ? - &dbb->dbb_memory_stats : &attachment->att_memory_stats; - // Create the request. - Request* const request = FB_NEW_POOL(*pool) Request(attachment, this, parentStats); - - if (level == 0) - pool->setStatsGroup(request->req_memory_stats); + AutoMemoryPool reqPool(MemoryPool::createPool(pool)); + const auto request = FB_NEW_POOL(*reqPool) Request(reqPool, attachment, this); + requests.grow(level + 1); requests[level] = request; return request; @@ -500,15 +522,13 @@ void Statement::verifyAccess(thread_db* tdbb) if (!routine->getStatement()) continue; - for (const AccessItem* access = routine->getStatement()->accessList.begin(); - access != routine->getStatement()->accessList.end(); - ++access) + for (const auto& access : routine->getStatement()->accessList) { MetaName userName = item->user; - if (access->acc_ss_rel_id) + if (access.acc_ss_rel_id) { - const jrd_rel* view = MET_lookup_relation_id(tdbb, access->acc_ss_rel_id, false); + const jrd_rel* view = MET_lookup_relation_id(tdbb, access.acc_ss_rel_id, false); if (view && (view->rel_flags & REL_sql_relation)) userName = view->rel_owner_name; } @@ -517,17 +537,17 @@ void Statement::verifyAccess(thread_db* tdbb) UserId* effectiveUser = userName.hasData() ? attachment->getUserId(userName) : attachment->att_ss_user; AutoSetRestore userIdHolder(&attachment->att_ss_user, effectiveUser); - const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); + const SecurityClass* sec_class = SCL_get_class(tdbb, access.acc_security_name.c_str()); if (routine->getName().package.isEmpty()) { SCL_check_access(tdbb, sec_class, aclType, routine->getName().identifier, - access->acc_mask, access->acc_type, true, access->acc_name, access->acc_r_name); + access.acc_mask, access.acc_type, true, access.acc_name, access.acc_r_name); } else { SCL_check_access(tdbb, sec_class, id_package, routine->getName().package, - access->acc_mask, access->acc_type, true, access->acc_name, access->acc_r_name); + access.acc_mask, access.acc_type, true, access.acc_name, access.acc_r_name); } } } @@ -650,7 +670,11 @@ void Statement::release(thread_db* tdbb) } for (Request** instance = requests.begin(); instance != requests.end(); ++instance) + { EXE_release(tdbb, *instance); + MemoryPool::deletePool((*instance)->req_pool); + *instance = nullptr; + } const auto attachment = tdbb->getAttachment(); @@ -842,41 +866,33 @@ template static void makeSubRoutines(thread_db* tdbb, Statement* st { typename T::Accessor subAccessor(&subs); - for (bool found = subAccessor.getFirst(); found; found = subAccessor.getNext()) + for (auto& sub : subs) { - typename T::ValueType subNode = subAccessor.current()->second; - Routine* subRoutine = subNode->routine; - CompilerScratch*& subCsb = subNode->subCsb; + auto subNode = sub.second; + auto subRoutine = subNode->routine; + auto& subCsb = subNode->subCsb; - Statement* subStatement = Statement::makeStatement(tdbb, subCsb, false); + auto subStatement = Statement::makeStatement(tdbb, subCsb, false); subStatement->parentStatement = statement; subRoutine->setStatement(subStatement); // Move dependencies and permissions from the sub routine to the parent. - for (CompilerScratch::Dependency* dependency = subCsb->csb_dependencies.begin(); - dependency != subCsb->csb_dependencies.end(); - ++dependency) - { - csb->csb_dependencies.push(*dependency); - } + for (auto& dependency : subCsb->csb_dependencies) + csb->csb_dependencies.push(dependency); - for (ExternalAccess* access = subCsb->csb_external.begin(); - access != subCsb->csb_external.end(); - ++access) + for (auto& access : subStatement->externalList) { FB_SIZE_T i; - if (!csb->csb_external.find(*access, i)) - csb->csb_external.insert(i, *access); + if (!csb->csb_external.find(access, i)) + csb->csb_external.insert(i, access); } - for (AccessItem* access = subCsb->csb_access.begin(); - access != subCsb->csb_access.end(); - ++access) + for (auto& access : subStatement->accessList) { FB_SIZE_T i; - if (!csb->csb_access.find(*access, i)) - csb->csb_access.insert(i, *access); + if (!csb->csb_access.find(access, i)) + csb->csb_access.insert(i, access); } delete subCsb; diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index cdf98ff9ad..6f68a360e1 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -55,6 +55,11 @@ public: return id; } + unsigned getSize() const + { + return (unsigned) pool->getStatsGroup().getCurrentUsage(); + } + const Routine* getRoutine() const; bool isActive() const; @@ -78,6 +83,7 @@ public: unsigned blrVersion; ULONG impureSize; // Size of impure area mutable StmtNumber id; // statement identifier + USHORT charSetId; // client character set (CS_METADATA for internal statements) Firebird::Array rpbsSetup; Firebird::Array requests; // vector of requests ExternalAccessList externalList; // Access to procedures/triggers to be checked @@ -96,7 +102,6 @@ public: Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset) Firebird::Array blr; // BLR for non-SQL query MapFieldInfo mapFieldInfo; // Map field name to field info - MapItemInfo mapItemInfo; // Map item to item info }; diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 508a4bc33d..da2aa2f439 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -284,7 +284,8 @@ enum stat_group_t { stat_attachment = 1, stat_transaction = 2, stat_statement = 3, - stat_call = 4 + stat_call = 4, + stat_cmp_statement = 5 }; enum InfoType diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index af4b5a2302..1a92558460 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -134,6 +134,7 @@ #include "../dsql/dsql.h" #include "../dsql/dsql_proto.h" #include "../dsql/DsqlBatch.h" +#include "../dsql/DsqlStatementCache.h" #ifdef WIN_NT #include @@ -4714,13 +4715,9 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* CompilerScratch* csb = PAR_parse(tdbb, reinterpret_cast(blr), blr_length, false); - request = Statement::makeRequest(tdbb, csb, false); - request->getStatement()->verifyAccess(tdbb); - for (FB_SIZE_T i = 0; i < csb->csb_rpt.getCount(); i++) { - const MessageNode* node = csb->csb_rpt[i].csb_message; - if (node) + if (const auto node = csb->csb_rpt[i].csb_message) { if (node->messageNumber == 0) inMessage = node; @@ -4728,6 +4725,9 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* outMessage = node; } } + + request = Statement::makeRequest(tdbb, csb, false); + request->getStatement()->verifyAccess(tdbb); } catch (const Exception&) { @@ -7551,6 +7551,9 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment) attachment->att_replicator = nullptr; + if (attachment->att_dsql_instance) + attachment->att_dsql_instance->dbb_statement_cache->purge(tdbb); + while (attachment->att_repl_appliers.hasData()) { AutoPtr cleanupApplier(attachment->att_repl_appliers.pop()); @@ -8906,8 +8909,10 @@ void thread_db::setRequest(Request* val) SSHORT thread_db::getCharSet() const { - if (request && request->charSetId != CS_dynamic) - return request->charSetId; + USHORT charSetId; + + if (request && (charSetId = request->getStatement()->charSetId) != CS_dynamic) + return charSetId; return attachment->att_charset; } diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index 62863b5e72..af2b76ab11 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -584,6 +584,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_record_gc: case LCK_alter_database: case LCK_repl_tables: + case LCK_dsql_statement_cache: owner_type = LCK_OWNER_attachment; break; diff --git a/src/jrd/lck.h b/src/jrd/lck.h index 892aa1fe21..1efa69cf26 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -74,7 +74,8 @@ enum lck_t { LCK_record_gc, // Record-level GC lock LCK_alter_database, // ALTER DATABASE lock LCK_repl_state, // Replication state lock - LCK_repl_tables // Replication set lock + LCK_repl_tables, // Replication set lock + LCK_dsql_statement_cache // DSQL statement cache lock }; // Lock owner types diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 2fbfe56596..1016ad7c8e 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -752,4 +752,5 @@ RELATION(nam_mon_compiled_statements, rel_mon_compiled_statements, ODS_13_1, rel FIELD(f_mon_cmp_stmt_name, nam_mon_obj_name, fld_gnr_name, 0, ODS_13_1) FIELD(f_mon_cmp_stmt_type, nam_mon_obj_type, fld_obj_type, 0, ODS_13_1) FIELD(f_mon_cmp_stmt_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_stat_id, nam_mon_stat_id, fld_stat_id, 0, ODS_13_1) END_RELATION diff --git a/src/jrd/req.h b/src/jrd/req.h index be5a82a55c..c6c266e562 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -266,11 +266,10 @@ private: }; public: - Request(Attachment* attachment, /*const*/ Statement* aStatement, - Firebird::MemoryStats* parent_stats) + Request(Firebird::AutoMemoryPool& pool, Attachment* attachment, /*const*/ Statement* aStatement) : statement(aStatement), - req_pool(statement->pool), - req_memory_stats(parent_stats), + req_pool(pool), + req_memory_stats(&aStatement->pool->getStatsGroup()), req_blobs(req_pool), req_stats(*req_pool), req_base_stats(*req_pool), @@ -288,6 +287,9 @@ public: setAttachment(attachment); req_rpb = statement->rpbsSetup; impureArea.grow(statement->impureSize); + + pool->setStatsGroup(req_memory_stats); + pool.release(); } Statement* getStatement() @@ -313,8 +315,6 @@ public: void setAttachment(Attachment* newAttachment) { req_attachment = newAttachment; - charSetId = statement->flags & Statement::FLAG_INTERNAL ? - CS_METADATA : req_attachment->att_charset; } bool isRoot() const @@ -351,9 +351,9 @@ private: public: MemoryPool* req_pool; + Firebird::MemoryStats req_memory_stats; Attachment* req_attachment; // database attachment USHORT req_incarnation; // incarnation number - Firebird::MemoryStats req_memory_stats; // Transaction pointer and doubly linked list pointers for requests in this // transaction. Maintained by TRA_attach_request/TRA_detach_request. @@ -399,7 +399,6 @@ public: SortOwner req_sorts; Firebird::Array req_rpb; // record parameter blocks Firebird::Array impureArea; // impure area - USHORT charSetId; // "client" character set of the request TriggerAction req_trigger_action; // action that caused trigger to fire // Fields to support read consistency in READ COMMITTED transactions diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 14fc38eefb..caaef2f4eb 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -366,6 +366,13 @@ public: return usr_granted_roles.exist(role); } + const auto& getGrantedRoles(thread_db* tdbb) const + { + if (testFlag(USR_newrole)) + findGrantedRoles(tdbb); + return usr_granted_roles; + } + void makeRoleName(const int dialect) { makeRoleName(usr_sql_role_name, dialect); diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 741882585a..93863a9acb 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -234,7 +234,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() if (m_descs.getCount() || !m_params || m_params->getCount() == 0) return; - if (!m_stmt->isDml()) + if (!m_stmt->getDsqlStatement()->isDml()) { fb_assert(false); return; From 652ebe49ffb4dff41e926230bed454519405703a Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sat, 5 Mar 2022 22:37:42 -0300 Subject: [PATCH 123/187] PoolAllocator: fix VS 2017 build. --- src/common/classes/alloc.h | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h index d133837206..bc25095170 100644 --- a/src/common/classes/alloc.h +++ b/src/common/classes/alloc.h @@ -469,6 +469,10 @@ namespace Firebird using size_type = size_t; using pointer = T*; using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using void_pointer = void* ; + using const_void_pointer = const void*; using difference_type = std::ptrdiff_t; using is_always_equal = std::true_type; @@ -511,12 +515,37 @@ namespace Firebird return size_t(-1) / sizeof(T); } + /* C++17 template constexpr void construct(U* ptr, Args&&... args) + { + if constexpr (std::is_constructible::value) + new ((void*) ptr) U(pool, std::forward(args)...); + else + new ((void*) ptr) U(std::forward(args)...); + } + */ + + template < + typename U, + typename... Args, + std::enable_if_t::value, bool> = true + > + constexpr void construct(U* ptr, Args&&... args) { new ((void*) ptr) U(pool, std::forward(args)...); } + template < + typename U, + typename... Args, + std::enable_if_t::value, bool> = true + > + constexpr void construct(U* ptr, Args&&... args) + { + new ((void*) ptr) U(std::forward(args)...); + } + template constexpr void destroy(U* ptr) { @@ -548,8 +577,8 @@ struct std::allocator_traits> using value_type = typename Alloc::value_type; using pointer = typename Alloc::pointer; using const_pointer = typename Alloc::const_pointer; - using void_pointer = void*; - using const_void_pointer = const void*; + using void_pointer = typename Alloc::void_pointer; + using const_void_pointer = typename Alloc::const_void_pointer; using size_type = typename Alloc::size_type; using difference_type = typename Alloc::difference_type; using reference = value_type&; From 463605e0938a0e5f5990e7a3df7546806630eb6a Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 19 Mar 2022 00:06:41 +0000 Subject: [PATCH 124/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 1b4b301a2f..5a05c5279f 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:429 + FORMAL BUILD NUMBER:435 */ -#define PRODUCT_VER_STRING "5.0.0.429" -#define FILE_VER_STRING "WI-T5.0.0.429" -#define LICENSE_VER_STRING "WI-T5.0.0.429" -#define FILE_VER_NUMBER 5, 0, 0, 429 +#define PRODUCT_VER_STRING "5.0.0.435" +#define FILE_VER_STRING "WI-T5.0.0.435" +#define LICENSE_VER_STRING "WI-T5.0.0.435" +#define FILE_VER_NUMBER 5, 0, 0, 435 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "429" +#define FB_BUILD_NO "435" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 4f29f75438..2d78521f31 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=429 +BuildNum=435 NowAt=`pwd` cd `dirname $0` From 78ef42d38ae6b84779a1b4d9f9cca9cbeb143de0 Mon Sep 17 00:00:00 2001 From: asfernandes Date: Sat, 19 Mar 2022 11:12:53 +0000 Subject: [PATCH 125/187] Update tzdata to version 2022a. --- extern/icu/tzdata/be.zip | Bin 97986 -> 97392 bytes extern/icu/tzdata/le.zip | Bin 97317 -> 96752 bytes extern/icu/tzdata/version.txt | 2 +- src/common/TimeZones.h | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/icu/tzdata/be.zip b/extern/icu/tzdata/be.zip index 6632977cac296467f5d2a5c81a050925e35612e6..0044e7b97ad883c6ce7b633ee80f1606d0e11239 100644 GIT binary patch delta 63165 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 POkXf1LW?K(U!(s4#1L?X delta 63721 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

bsU9sc}}|GG{7YrM}%I8L!my2V!(oQ|CS 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

aCk>L(o~DVWP%5Y_j{}G9X&@y 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 O`G6C{@OprM-T8ksmiE&C delta 63676 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 diff --git a/extern/icu/tzdata/version.txt b/extern/icu/tzdata/version.txt index dac017a1e4..ca002de23b 100644 --- a/extern/icu/tzdata/version.txt +++ b/extern/icu/tzdata/version.txt @@ -1 +1 @@ -2021a4 +2022a diff --git a/src/common/TimeZones.h b/src/common/TimeZones.h index e7d4353b83..3754b8d4d6 100644 --- a/src/common/TimeZones.h +++ b/src/common/TimeZones.h @@ -1,6 +1,6 @@ // The content of this file is generated with help of update-ids utility Do not edit. -static const char* BUILTIN_TIME_ZONE_VERSION = "2021a4"; +static const char* BUILTIN_TIME_ZONE_VERSION = "2022a"; // Do not change order of items in this array! The index corresponds to a TimeZone ID, which must be fixed! static const char* BUILTIN_TIME_ZONE_LIST[] = { From 35da8f4553c50d6306f43c6b824d018f05a1e7f1 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sat, 19 Mar 2022 15:04:02 -0300 Subject: [PATCH 126/187] Remove deprecated GitHub Actions Windows 2016 environment. --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2235bc29d0..051219b643 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,13 +7,12 @@ jobs: runs-on: ${{ matrix.os }} container: ${{ matrix.container }} env: - VS_VERSION: ${{ matrix.os == 'windows-2016' && '2017' || (matrix.os == 'windows-2019' && '2019' || (matrix.os == 'windows-2022' && '2022' || '')) }} + VS_VERSION: ${{ (matrix.os == 'windows-2019' && '2019' || (matrix.os == 'windows-2022' && '2022' || '')) }} strategy: fail-fast: false matrix: os: - - windows-2016 - windows-2019 platform: [x64, x86] include: From 5251444ef3b1e3ccfac55afd13aafc34e607be64 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sun, 20 Mar 2022 00:05:57 +0000 Subject: [PATCH 127/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 5a05c5279f..492688f13b 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:435 + FORMAL BUILD NUMBER:437 */ -#define PRODUCT_VER_STRING "5.0.0.435" -#define FILE_VER_STRING "WI-T5.0.0.435" -#define LICENSE_VER_STRING "WI-T5.0.0.435" -#define FILE_VER_NUMBER 5, 0, 0, 435 +#define PRODUCT_VER_STRING "5.0.0.437" +#define FILE_VER_STRING "WI-T5.0.0.437" +#define LICENSE_VER_STRING "WI-T5.0.0.437" +#define FILE_VER_NUMBER 5, 0, 0, 437 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "435" +#define FB_BUILD_NO "437" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 2d78521f31..9a4cb1b361 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=435 +BuildNum=437 NowAt=`pwd` cd `dirname $0` From 6214b12028501cdc2875ad7d0deb62c4822060b8 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 24 Mar 2022 08:55:07 -0300 Subject: [PATCH 128/187] Fix issue reported by Pavel Zotov: recreate table test (id int primary key)! commit! execute block as begin execute statement 'drop table test'; in autonomous transaction do execute statement ( 'insert into test values ( ? ) ') (1); end! commit! Output on 5.0.0.426 (built: 18.03.2022): ======== ======== (no errors, expected) Output on 5.0.0.435 (built: 19.03.2022): ======== Statement failed, SQLSTATE = 42000 unsuccessful metadata update -object TABLE "TEST" is in use ======== --- src/dsql/DsqlRequests.cpp | 5 +++-- src/jrd/dfw.epp | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp index 2bdb14f154..baeea0a8df 100644 --- a/src/dsql/DsqlRequests.cpp +++ b/src/dsql/DsqlRequests.cpp @@ -23,7 +23,7 @@ #include "../dsql/DsqlRequests.h" #include "../dsql/dsql.h" #include "../dsql/DsqlBatch.h" -#include "../dsql/DsqlStatementCache.h" +///#include "../dsql/DsqlStatementCache.h" #include "../dsql/Nodes.h" #include "../jrd/Statement.h" #include "../jrd/req.h" @@ -1036,7 +1036,8 @@ void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, { AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); - req_dbb->dbb_attachment->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); + //// Doing it in DFW_perform_work to avoid problems with DDL+DML in the same transaction. + ///req_dbb->dbb_attachment->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); node->executeDdl(tdbb, internalScratch, req_transaction); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 3d6d9a5ee3..a3a956462d 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -85,6 +85,7 @@ #include "../jrd/intl.h" #include "../intl/charsets.h" #include "../jrd/align.h" +#include "../dsql/DsqlStatementCache.h" #include "../common/gdsassert.h" #include "../jrd/blb_proto.h" #include "../jrd/btr_proto.h" @@ -1477,6 +1478,9 @@ void DFW_perform_work(thread_db* tdbb, jrd_tra* transaction) } SET_TDBB(tdbb); + + tdbb->getAttachment()->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); + Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool); /* Loop for as long as any of the deferred work routines says that it has From dd17c5585c50ee548c9ed91300019c42a85bf071 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 24 Mar 2022 20:36:16 +0300 Subject: [PATCH 129/187] Use correct way to count updated records in management plugin --- .../manage/SrpManagement.cpp | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index 341c1f2f66..db795d0312 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -532,12 +532,9 @@ public: assignField(active, user->active()); setField(login, user->userName()); - int count = 0; - checkCount(status, stmt, &count, isc_info_req_update_count); stmt->execute(status, mainTra, up.getMetadata(), up.getBuffer(), NULL, NULL); check(status); - - if (!checkCount(status, stmt, &count, isc_info_req_update_count)) + if (recordsCount(status, stmt, isc_info_req_update_count) != 1) { stmt->release(); return GsecMsg22; @@ -573,12 +570,9 @@ public: Varfield login(dl); setField(login, user->userName()); - int count = 0; - checkCount(status, stmt, &count, isc_info_req_delete_count); stmt->execute(status, mainTra, dl.getMetadata(), dl.getBuffer(), NULL, NULL); check(status); - - if (!checkCount(status, stmt, &count, isc_info_req_delete_count)) + if (recordsCount(status, stmt, isc_info_req_delete_count) != 1) { stmt->release(); return GsecMsg22; @@ -731,7 +725,7 @@ private: RemotePasswordImpl server; - bool checkCount(Firebird::CheckStatusWrapper* status, Firebird::IStatement* stmt, int* count, UCHAR item) + int recordsCount(Firebird::CheckStatusWrapper* status, Firebird::IStatement* stmt, UCHAR item) { UCHAR buffer[33]; const UCHAR count_info[] = { isc_info_sql_records }; @@ -747,17 +741,12 @@ private: const SSHORT len = gds__vax_integer(p, 2); p += 2; if (count_is == item) - { - int newCount = gds__vax_integer(p, len); - int oldCount = *count; - *count = newCount; - return newCount == oldCount + 1; - } + return gds__vax_integer(p, len); p += len; } } - return false; + return 0; } static void check(Firebird::CheckStatusWrapper* status) From 348bb77fc0777c8cc0b9a5676ad008bd534f5c5e Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 25 Mar 2022 00:07:26 +0000 Subject: [PATCH 130/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 492688f13b..c23907e37d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:437 + FORMAL BUILD NUMBER:439 */ -#define PRODUCT_VER_STRING "5.0.0.437" -#define FILE_VER_STRING "WI-T5.0.0.437" -#define LICENSE_VER_STRING "WI-T5.0.0.437" -#define FILE_VER_NUMBER 5, 0, 0, 437 +#define PRODUCT_VER_STRING "5.0.0.439" +#define FILE_VER_STRING "WI-T5.0.0.439" +#define LICENSE_VER_STRING "WI-T5.0.0.439" +#define FILE_VER_NUMBER 5, 0, 0, 439 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "437" +#define FB_BUILD_NO "439" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 9a4cb1b361..ff0636cab7 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=437 +BuildNum=439 NowAt=`pwd` cd `dirname $0` From 59403d7fef34577354dfcb0a66988d5ec3308bcd Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 25 Mar 2022 19:58:28 +0200 Subject: [PATCH 131/187] Adjust debug messages to avoid false failures of some fbt tests. --- src/jrd/btr.cpp | 4 +++- src/jrd/btr.h | 6 ++++++ src/jrd/recsrc/IndexTableScan.cpp | 12 ++++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 2908828d71..92965d5a72 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -252,7 +252,9 @@ void BtrPageGCLock::disablePageGC(thread_db* tdbb, const PageNumber& page) void BtrPageGCLock::enablePageGC(thread_db* tdbb) { - LCK_release(tdbb, this); + fb_assert(lck_id); + if (lck_id) + LCK_release(tdbb, this); } bool BtrPageGCLock::isPageGCAllowed(thread_db* tdbb, const PageNumber& page) diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 33b29c4270..b3f630a247 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -240,6 +240,12 @@ public: void disablePageGC(thread_db* tdbb, const PageNumber &page); void enablePageGC(thread_db* tdbb); + // return true if lock is active + bool isActive() const + { + return lck_id != 0; + } + static bool isPageGCAllowed(thread_db* tdbb, const PageNumber& page); #ifdef DEBUG_LCK_LIST diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index 7fe9d5b60a..94858836b9 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -105,10 +105,12 @@ void IndexTableScan::close(thread_db* tdbb) const if (impure->irsb_nav_btr_gc_lock) { #ifdef DEBUG_LCK_LIST - if (!impure->irsb_nav_page) - gds__log("DEBUG_LCK_LIST: irsb_nav_btr_gc_lock && !irsb_nav_page"); + if (!impure->irsb_nav_page && impure->irsb_nav_btr_gc_lock->isActive()) + gds__log("DEBUG_LCK_LIST: irsb_nav_btr_gc_lock->isActive() && !irsb_nav_page"); #endif - impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + if (impure->irsb_nav_btr_gc_lock->isActive()) + impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + delete impure->irsb_nav_btr_gc_lock; impure->irsb_nav_btr_gc_lock = NULL; } @@ -132,7 +134,9 @@ void IndexTableScan::close(thread_db* tdbb) const { gds__log("DEBUG_LCK_LIST: irsb_nav_btr_gc_lock && !(irsb_flags & irsb_open)"); - impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + if (impure->irsb_nav_btr_gc_lock->isActive()) + impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + delete impure->irsb_nav_btr_gc_lock; impure->irsb_nav_btr_gc_lock = NULL; impure->irsb_nav_page = 0; From 96abd02998128c593756b844958ba2757919e8f2 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Mon, 28 Mar 2022 00:06:27 +0000 Subject: [PATCH 132/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index c23907e37d..ceccc88d37 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:439 + FORMAL BUILD NUMBER:440 */ -#define PRODUCT_VER_STRING "5.0.0.439" -#define FILE_VER_STRING "WI-T5.0.0.439" -#define LICENSE_VER_STRING "WI-T5.0.0.439" -#define FILE_VER_NUMBER 5, 0, 0, 439 +#define PRODUCT_VER_STRING "5.0.0.440" +#define FILE_VER_STRING "WI-T5.0.0.440" +#define LICENSE_VER_STRING "WI-T5.0.0.440" +#define FILE_VER_NUMBER 5, 0, 0, 440 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "439" +#define FB_BUILD_NO "440" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index ff0636cab7..082ec58357 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=439 +BuildNum=440 NowAt=`pwd` cd `dirname $0` From 902bbaec8d8989949c535045de33fb9d524393a9 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 29 Mar 2022 15:18:49 +0300 Subject: [PATCH 133/187] Attempt to fix #7160: Missing checkout in trace manager when performing user mapping may cause server hang --- src/jrd/trace/TraceManager.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 58afdfe193..57a714affc 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -283,6 +283,32 @@ void TraceManager::update_session(const TraceSession& session) mapping.setSecurityDbAlias(dbb->dbb_config->getSecurityDatabase(), dbb->dbb_filename.c_str()); mapping.setDb(attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), attachment->getInterface()); + + class AttachmentUnlock + { + public: + AttachmentUnlock(Attachment* att, const char* from) + : m_from(from) + { + if (att && att->att_use_count) + m_ref = att->getStable(); + + if (m_ref) + m_ref->getSync()->leave(); + } + + ~AttachmentUnlock() + { + if (m_ref) + m_ref->getSync()->enter(m_from); + } + + private: + Firebird::RefPtr m_ref; + const char* m_from; + }; + + AttachmentUnlock guard(attachment, FB_FUNCTION); mapResult = mapping.mapUser(s_user, t_role); } } From 620941399209b41a97d82a42f843a898035d587a Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 29 Mar 2022 16:50:50 +0300 Subject: [PATCH 134/187] Reworked fix for #7160: Missing checkout in trace manager when performing user mapping may cause server hang --- src/jrd/jrd.h | 12 ++++++++++++ src/jrd/trace/TraceManager.cpp | 28 ++-------------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index d42c37f9f6..5818ce3efb 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -1092,6 +1092,18 @@ namespace Jrd { m_ref->getSync()->leave(); } + EngineCheckout(Attachment* att, const char* from) + : m_tdbb(nullptr), m_from(from) + { + fb_assert(att); + + if (att && att->att_use_count) + { + m_ref = att->getStable(); + m_ref->getSync()->leave(); + } + } + ~EngineCheckout() { if (m_ref.hasData()) diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 57a714affc..e25be60b04 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -271,7 +271,7 @@ void TraceManager::update_session(const TraceSession& session) curr_user = attachment->getUserName().c_str(); if (session.ses_auth.hasData()) - { // scope + { AutoSetRestoreFlag autoRestore(&attachment->att_flags, ATT_mapping, true); Database* dbb = attachment->att_database; @@ -284,31 +284,7 @@ void TraceManager::update_session(const TraceSession& session) mapping.setDb(attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), attachment->getInterface()); - class AttachmentUnlock - { - public: - AttachmentUnlock(Attachment* att, const char* from) - : m_from(from) - { - if (att && att->att_use_count) - m_ref = att->getStable(); - - if (m_ref) - m_ref->getSync()->leave(); - } - - ~AttachmentUnlock() - { - if (m_ref) - m_ref->getSync()->enter(m_from); - } - - private: - Firebird::RefPtr m_ref; - const char* m_from; - }; - - AttachmentUnlock guard(attachment, FB_FUNCTION); + EngineCheckout guard(attachment, FB_FUNCTION); mapResult = mapping.mapUser(s_user, t_role); } } From 6708318ba19f908b54de7c670280fc3a6f8ff9b9 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 30 Mar 2022 00:06:45 +0000 Subject: [PATCH 135/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index ceccc88d37..03386f5368 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:440 + FORMAL BUILD NUMBER:442 */ -#define PRODUCT_VER_STRING "5.0.0.440" -#define FILE_VER_STRING "WI-T5.0.0.440" -#define LICENSE_VER_STRING "WI-T5.0.0.440" -#define FILE_VER_NUMBER 5, 0, 0, 440 +#define PRODUCT_VER_STRING "5.0.0.442" +#define FILE_VER_STRING "WI-T5.0.0.442" +#define LICENSE_VER_STRING "WI-T5.0.0.442" +#define FILE_VER_NUMBER 5, 0, 0, 442 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "440" +#define FB_BUILD_NO "442" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 082ec58357..fc0b029e82 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=440 +BuildNum=442 NowAt=`pwd` cd `dirname $0` From c234434894dd7b337289914eb7917742170cfca5 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 11 Mar 2022 09:26:25 +0300 Subject: [PATCH 136/187] Initial estimation of rivers cardinality. Adjustments will follow. --- src/jrd/optimizer/Optimizer.cpp | 55 ++++++++++++++++++++-------- src/jrd/optimizer/Optimizer.h | 23 ++++++++++++ src/jrd/optimizer/Retrieval.cpp | 50 +++++++++++-------------- src/jrd/recsrc/AggregatedStream.cpp | 6 +++ src/jrd/recsrc/BitmapTableScan.cpp | 4 +- src/jrd/recsrc/BufferedStream.cpp | 2 + src/jrd/recsrc/ConditionalStream.cpp | 3 ++ src/jrd/recsrc/ExternalTableScan.cpp | 2 + src/jrd/recsrc/FilteredStream.cpp | 11 +++++- src/jrd/recsrc/FirstRowsStream.cpp | 9 +++++ src/jrd/recsrc/FullOuterJoin.cpp | 1 + src/jrd/recsrc/FullTableScan.cpp | 2 + src/jrd/recsrc/HashJoin.cpp | 5 +++ src/jrd/recsrc/IndexTableScan.cpp | 5 ++- src/jrd/recsrc/LocalTableStream.cpp | 5 +++ src/jrd/recsrc/LockedStream.cpp | 4 ++ src/jrd/recsrc/MergeJoin.cpp | 5 +++ src/jrd/recsrc/NestedLoopJoin.cpp | 7 ++++ src/jrd/recsrc/ProcedureScan.cpp | 3 ++ src/jrd/recsrc/RecordSource.cpp | 14 +++++++ src/jrd/recsrc/RecordSource.h | 18 +++++++-- src/jrd/recsrc/RecursiveStream.cpp | 3 ++ src/jrd/recsrc/SingularStream.cpp | 5 +++ src/jrd/recsrc/SkipRowsStream.cpp | 4 ++ src/jrd/recsrc/SortedStream.cpp | 6 +++ src/jrd/recsrc/Union.cpp | 4 ++ src/jrd/recsrc/VirtualTableScan.cpp | 2 + src/jrd/recsrc/WindowedStream.cpp | 4 ++ 28 files changed, 211 insertions(+), 51 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 0dda7f9993..5a75a6b2e1 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -375,17 +375,18 @@ namespace { // Return the estimated cardinality for the given relation - if (relation->isVirtual()) - return 100.0; // Just a dumb estimation + double cardinality = DEFAULT_CARDINALITY; if (relation->rel_file) - return EXT_cardinality(tdbb, relation); + cardinality = EXT_cardinality(tdbb, relation); + else if (!relation->isVirtual()) + { + MET_post_existence(tdbb, relation); + cardinality = DPM_cardinality(tdbb, relation, format); + MET_release_existence(tdbb, relation); + } - MET_post_existence(tdbb, relation); - const double cardinality = DPM_cardinality(tdbb, relation, format); - MET_release_existence(tdbb, relation); - - return cardinality; + return MAX(cardinality, MINIMUM_CARDINALITY); } void markIndices(CompilerScratch::csb_repeat* tail, USHORT relationId) @@ -1462,6 +1463,7 @@ RecordSource* Optimizer::applyLocalBoolean(const River* river) river->activate(csb); BoolExprNode* boolean = nullptr; + double selectivity = MAXIMUM_SELECTIVITY; for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) { @@ -1471,11 +1473,14 @@ RecordSource* Optimizer::applyLocalBoolean(const River* river) { compose(getPool(), &boolean, iter); iter |= CONJUNCT_USED; + + if (!(iter & CONJUNCT_MATCHED)) + selectivity *= getSelectivity(*iter); } } const auto rsb = river->getRecordSource(); - return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean) : rsb; + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity) : rsb; } @@ -2503,6 +2508,7 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) // Pick up any boolean that may apply BoolExprNode* boolean = nullptr; + double selectivity = MAXIMUM_SELECTIVITY; for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) { @@ -2512,11 +2518,14 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) { compose(getPool(), &boolean, iter); iter |= CONJUNCT_USED; + + if (!(iter & CONJUNCT_MATCHED)) + selectivity *= getSelectivity(*iter); } } if (boolean) - rsb = FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean); + rsb = FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity); const auto merged_river = FB_NEW_POOL(getPool()) River(csb, rsb, rivers_to_merge); @@ -2706,6 +2715,7 @@ RecordSource* Optimizer::generateOuterJoin(RiverList& rivers, RecordSource* Optimizer::generateResidualBoolean(RecordSource* rsb) { BoolExprNode* boolean = nullptr; + double selectivity = MAXIMUM_SELECTIVITY; for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) { @@ -2713,10 +2723,13 @@ RecordSource* Optimizer::generateResidualBoolean(RecordSource* rsb) { compose(getPool(), &boolean, iter); iter |= CONJUNCT_USED; + + if (!(iter & CONJUNCT_MATCHED)) + selectivity *= getSelectivity(*iter); } } - return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean) : rsb; + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity) : rsb; } @@ -2748,6 +2761,7 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, InversionNode* inversion = nullptr; BoolExprNode* condition = nullptr; Array dbkeyRanges; + double scanSelectivity = MAXIMUM_SELECTIVITY; if (relation->rel_file) { @@ -2801,6 +2815,7 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, inversion = candidate->inversion; condition = candidate->condition; dbkeyRanges.assign(candidate->dbkeyRanges); + scanSelectivity = candidate->selectivity; // Just for safety sake, this condition must be already checked // inside OptimizerRetrieval::matchOnIndexes() @@ -2853,6 +2868,7 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, // it used. If a computable boolean didn't match against an index then // mark the stream to denote unmatched booleans. BoolExprNode* boolean = nullptr; + double filterSelectivity = MAXIMUM_SELECTIVITY; for (auto iter = getConjuncts(innerFlag, outerFlag); iter.hasData(); ++iter) { @@ -2871,8 +2887,13 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, compose(getPool(), &boolean, iter); iter |= CONJUNCT_USED; - if (!outerFlag && !(iter & CONJUNCT_MATCHED)) - tail->csb_flags |= csb_unmatched; + if (!(iter & CONJUNCT_MATCHED)) + { + if (!outerFlag) + tail->csb_flags |= csb_unmatched; + + filterSelectivity *= getSelectivity(*iter); + } } } } @@ -2884,13 +2905,15 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, RecordSource* const rsb1 = FB_NEW_POOL(getPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); RecordSource* const rsb2 = - FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, inversion); + FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, + inversion, scanSelectivity); rsb = FB_NEW_POOL(getPool()) ConditionalStream(csb, rsb1, rsb2, condition); } else if (inversion) { - rsb = FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, inversion); + rsb = FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, + inversion, scanSelectivity); } else { @@ -2901,7 +2924,7 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, } } - return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean) : rsb; + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, filterSelectivity) : rsb; } diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index a0bb67e14b..24025abee7 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -35,6 +35,8 @@ #include "../common/classes/alloc.h" #include "../common/classes/array.h" #include "../common/classes/fb_string.h" +#include "../dsql/BoolNodes.h" +#include "../dsql/ExprNodes.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/exe.h" @@ -55,6 +57,7 @@ const double DEFAULT_SELECTIVITY = 0.1; const double MINIMUM_CARDINALITY = 1.0; const double THRESHOLD_CARDINALITY = 5.0; +const double DEFAULT_CARDINALITY = 1000.0; // Default depth of an index tree (including one leaf page), // also representing the minimal cost of the index scan. @@ -335,6 +338,26 @@ public: return statement ? statement->getPlan(tdbb, detailed) : ""; } + static double getSelectivity(const BoolExprNode* node) + { + const auto cmpNode = nodeAs(node); + + return (cmpNode && cmpNode->blrOp == blr_eql) ? + REDUCE_SELECTIVITY_FACTOR_EQUALITY : + REDUCE_SELECTIVITY_FACTOR_INEQUALITY; + } + + static void adjustSelectivity(double& selectivity, double factor, double cardinality) + { + if (cardinality) + { + const auto minSelectivity = 1 / cardinality; + const auto diffSelectivity = selectivity > minSelectivity ? + selectivity - minSelectivity : 0; + selectivity = minSelectivity + diffSelectivity * factor; + } + } + static RecordSource* compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 24475d19ea..4e52a09a80 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -323,6 +323,8 @@ InversionCandidate* Retrieval::getInversion() } } + const auto cardinality = csb->csb_rpt[stream].csb_cardinality; + if (!invCandidate) { // No index will be used, thus create a dummy inversion candidate @@ -340,30 +342,31 @@ InversionCandidate* Retrieval::getInversion() else { // Add the records retrieval cost to the priorly calculated index scan cost - invCandidate->cost += csb->csb_rpt[stream].csb_cardinality * invCandidate->selectivity; + invCandidate->cost += cardinality * invCandidate->selectivity; } + // Adjust the effective selectivity by treating computable but unmatched conjunctions + // as filters. But consider only those local to our stream. + // While being here, also mark matched conjuncts, if requested. + double selectivity = MAXIMUM_SELECTIVITY; for (iter.rewind(); iter.hasData(); ++iter) { if (!(iter & Optimizer::CONJUNCT_USED)) { const auto matched = invCandidate->matches.exist(iter); - // Adjust the effective selectivity by treating computable conjunctions as filters - if (iter->computable(csb, stream, true) && !matched) - { - const auto cmpNode = nodeAs(*iter); - - const double factor = (cmpNode && cmpNode->blrOp == blr_eql) ? - REDUCE_SELECTIVITY_FACTOR_EQUALITY : REDUCE_SELECTIVITY_FACTOR_INEQUALITY; - invCandidate->selectivity *= factor; - } - if (setConjunctionsMatched && matched) iter |= Optimizer::CONJUNCT_MATCHED; + else if (!setConjunctionsMatched && !matched && + iter->computable(csb, stream, true)) + { + selectivity *= Optimizer::getSelectivity(*iter); + } } } + Optimizer::adjustSelectivity(invCandidate->selectivity, selectivity, cardinality); + // Add the streams where this stream is depending on for (auto match : invCandidate->matches) { @@ -399,7 +402,8 @@ IndexTableScan* Retrieval::getNavigation() InversionNode* const index_node = makeIndexScanNode(scratch); return FB_NEW_POOL(getPool()) - IndexTableScan(csb, getAlias(), stream, relation, index_node, key_length); + IndexTableScan(csb, getAlias(), stream, relation, index_node, key_length, + navigationCandidate->selectivity); } void Retrieval::analyzeNavigation(const InversionCandidateList& inversions) @@ -670,13 +674,10 @@ bool Retrieval::betterInversion(const InversionCandidate* inv1, if (inv1->dependencies == inv2->dependencies) { - const double cardinality = - MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); + const double cardinality = csb->csb_rpt[stream].csb_cardinality; - const double cost1 = - inv1->cost + (inv1->selectivity * cardinality); - const double cost2 = - inv2->cost + (inv2->selectivity * cardinality); + const double cost1 = inv1->cost + (inv1->selectivity * cardinality); + const double cost2 = inv2->cost + (inv2->selectivity * cardinality); // Do we have very similar costs? double diffCost = 0; @@ -894,12 +895,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions, double selectivity = scratch.selectivity; if (selectivity <= 0) - { - if (unique && cardinality) - selectivity = 1 / cardinality; - else - selectivity = DEFAULT_SELECTIVITY; - } + selectivity = unique ? 1 / cardinality : DEFAULT_SELECTIVITY; const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate->unique = unique; @@ -1101,8 +1097,7 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) if (inversions.isEmpty() && !navigationCandidate) return nullptr; - const double streamCardinality = - MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); + const double streamCardinality = csb->csb_rpt[stream].csb_cardinality; // Prepared statements could be optimized against an almost empty table // and then cached (such as in the restore process), thus causing slowdown @@ -1815,8 +1810,7 @@ InversionCandidate* Retrieval::matchDbKey(BoolExprNode* boolean) const // If this is a dbkey for the appropriate stream, it's invertable - const double cardinality = - MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); + const double cardinality = csb->csb_rpt[stream].csb_cardinality; bool unique = false; double selectivity = 0; diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index 049a8eb576..ee63ee8f3c 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -27,6 +27,7 @@ #include "../jrd/mov_proto.h" #include "../jrd/vio_proto.h" #include "../jrd/Attachment.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -48,7 +49,9 @@ BaseAggWinStream::BaseAggWinStream(thread_db* tdbb, Compiler m_oneRowWhenEmpty(oneRowWhenEmpty) { fb_assert(m_next); + m_impure = csb->allocImpure(); + m_cardinality = group ? next->getCardinality() * DEFAULT_SELECTIVITY : MINIMUM_CARDINALITY; } template @@ -368,7 +371,10 @@ AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, Stream void AggregatedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Aggregate"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } diff --git a/src/jrd/recsrc/BitmapTableScan.cpp b/src/jrd/recsrc/BitmapTableScan.cpp index 1c7cc3ec03..2542910cd6 100644 --- a/src/jrd/recsrc/BitmapTableScan.cpp +++ b/src/jrd/recsrc/BitmapTableScan.cpp @@ -37,13 +37,14 @@ using namespace Jrd; BitmapTableScan::BitmapTableScan(CompilerScratch* csb, const string& alias, StreamType stream, jrd_rel* relation, - InversionNode* inversion) + InversionNode* inversion, double selectivity) : RecordStream(csb, stream), m_alias(csb->csb_pool, alias), m_relation(relation), m_inversion(inversion) { fb_assert(m_inversion); m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality * selectivity; } void BitmapTableScan::open(thread_db* tdbb) const @@ -129,6 +130,7 @@ void BitmapTableScan::print(thread_db* tdbb, string& plan, plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; + printOptInfo(plan); printInversion(tdbb, m_inversion, plan, true, level); } else diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 24302b6dda..8f8e5355f5 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -45,6 +45,7 @@ BufferedStream::BufferedStream(CompilerScratch* csb, RecordSource* next) fb_assert(m_next); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); StreamList streams; m_next->findUsedStreams(streams); @@ -319,6 +320,7 @@ void BufferedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigne extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length); plan += printIndent(++level) + "Record Buffer" + extras; + printOptInfo(plan); } m_next->print(tdbb, plan, detailed, level); diff --git a/src/jrd/recsrc/ConditionalStream.cpp b/src/jrd/recsrc/ConditionalStream.cpp index 3175cb1a0e..d0c5f98b41 100644 --- a/src/jrd/recsrc/ConditionalStream.cpp +++ b/src/jrd/recsrc/ConditionalStream.cpp @@ -46,6 +46,7 @@ ConditionalStream::ConditionalStream(CompilerScratch* csb, fb_assert(m_first && m_second && m_boolean); m_impure = csb->allocImpure(); + m_cardinality = (first->getCardinality() + second->getCardinality()) / 2; } void ConditionalStream::open(thread_db* tdbb) const @@ -115,6 +116,8 @@ void ConditionalStream::print(thread_db* tdbb, string& plan, bool detailed, unsi if (detailed) { plan += printIndent(++level) + "Condition"; + printOptInfo(plan); + m_first->print(tdbb, plan, true, level); m_second->print(tdbb, plan, true, level); } diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 9567d9d914..be63ba8401 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -42,6 +42,7 @@ ExternalTableScan::ExternalTableScan(CompilerScratch* csb, const string& alias, : RecordStream(csb, stream), m_relation(relation), m_alias(csb->csb_pool, alias) { m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality; } void ExternalTableScan::open(thread_db* tdbb) const @@ -122,6 +123,7 @@ void ExternalTableScan::print(thread_db* tdbb, string& plan, { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/FilteredStream.cpp b/src/jrd/recsrc/FilteredStream.cpp index 8fd1eeaf96..a5a4ed25cf 100644 --- a/src/jrd/recsrc/FilteredStream.cpp +++ b/src/jrd/recsrc/FilteredStream.cpp @@ -25,6 +25,7 @@ #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/evl_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -35,13 +36,18 @@ using namespace Jrd; // Data access: predicate driven filter // ------------------------------------ -FilteredStream::FilteredStream(CompilerScratch* csb, RecordSource* next, BoolExprNode* boolean) +FilteredStream::FilteredStream(CompilerScratch* csb, RecordSource* next, + BoolExprNode* boolean, double selectivity) : m_next(next), m_boolean(boolean), m_anyBoolean(NULL), m_ansiAny(false), m_ansiAll(false), m_ansiNot(false) { fb_assert(m_next && m_boolean); m_impure = csb->allocImpure(); + + const auto cardinality = next->getCardinality(); + Optimizer::adjustSelectivity(selectivity, MAXIMUM_SELECTIVITY, cardinality); + m_cardinality = cardinality * selectivity; } void FilteredStream::open(thread_db* tdbb) const @@ -105,7 +111,10 @@ bool FilteredStream::lockRecord(thread_db* tdbb) const void FilteredStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Filter"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } diff --git a/src/jrd/recsrc/FirstRowsStream.cpp b/src/jrd/recsrc/FirstRowsStream.cpp index e0cdb5d596..8fa3fecaf7 100644 --- a/src/jrd/recsrc/FirstRowsStream.cpp +++ b/src/jrd/recsrc/FirstRowsStream.cpp @@ -26,6 +26,7 @@ #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -42,6 +43,11 @@ FirstRowsStream::FirstRowsStream(CompilerScratch* csb, RecordSource* next, Value fb_assert(m_next && m_value); m_impure = csb->allocImpure(); + + const auto valueConst = nodeAs(value); + const auto valueDesc = valueConst ? &valueConst->litDesc : nullptr; + m_cardinality = (valueDesc && valueDesc->dsc_dtype == dtype_long) ? + valueConst->getSlong() : DEFAULT_CARDINALITY; } void FirstRowsStream::open(thread_db* tdbb) const @@ -115,7 +121,10 @@ bool FirstRowsStream::lockRecord(thread_db* tdbb) const void FirstRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "First N Records"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index 62c62b7ab7..3725560906 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -43,6 +43,7 @@ FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSou fb_assert(m_arg1 && m_arg2); m_impure = csb->allocImpure(); + m_cardinality = arg1->getCardinality() * arg2->getCardinality(); } void FullOuterJoin::open(thread_db* tdbb) const diff --git a/src/jrd/recsrc/FullTableScan.cpp b/src/jrd/recsrc/FullTableScan.cpp index c1494abb81..563fc07d54 100644 --- a/src/jrd/recsrc/FullTableScan.cpp +++ b/src/jrd/recsrc/FullTableScan.cpp @@ -45,6 +45,7 @@ FullTableScan::FullTableScan(CompilerScratch* csb, const string& alias, m_dbkeyRanges(csb->csb_pool, dbkeyRanges) { m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality; } void FullTableScan::open(thread_db* tdbb) const @@ -185,6 +186,7 @@ void FullTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index a32339da68..118d369c43 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -30,6 +30,7 @@ #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/intl_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -214,6 +215,7 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, fb_assert(count >= 2); m_impure = csb->allocImpure(); + m_cardinality = args[0]->getCardinality(); m_leader.source = args[0]; m_leader.keys = keys[0]; @@ -248,6 +250,8 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, RecordSource* const sub_rsb = args[i]; fb_assert(sub_rsb); + m_cardinality *= sub_rsb->getCardinality() * DEFAULT_SELECTIVITY; + SubStream sub; sub.buffer = FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, sub_rsb); sub.keys = keys[i]; @@ -431,6 +435,7 @@ void HashJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned leve if (detailed) { plan += printIndent(++level) + "Hash Join (inner)"; + printOptInfo(plan); m_leader.source->print(tdbb, plan, true, level); diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index 94858836b9..0befcca78d 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -41,7 +41,8 @@ using namespace Jrd; IndexTableScan::IndexTableScan(CompilerScratch* csb, const string& alias, StreamType stream, jrd_rel* relation, - InversionNode* index, USHORT length) + InversionNode* index, USHORT length, + double selectivity) : RecordStream(csb, stream), m_alias(csb->csb_pool, alias), m_relation(relation), m_index(index), m_inversion(NULL), m_condition(NULL), m_length(length), m_offset(0) @@ -57,6 +58,7 @@ IndexTableScan::IndexTableScan(CompilerScratch* csb, const string& alias, size += sizeof(index_desc); m_impure = csb->allocImpure(FB_ALIGNMENT, static_cast(size)); + m_cardinality = csb->csb_rpt[stream].csb_cardinality * selectivity; } void IndexTableScan::open(thread_db* tdbb) const @@ -308,6 +310,7 @@ void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigne plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; + printOptInfo(plan); printInversion(tdbb, m_index, plan, true, level, true); if (m_inversion) diff --git a/src/jrd/recsrc/LocalTableStream.cpp b/src/jrd/recsrc/LocalTableStream.cpp index 727a3934d9..e678f47a9d 100644 --- a/src/jrd/recsrc/LocalTableStream.cpp +++ b/src/jrd/recsrc/LocalTableStream.cpp @@ -25,6 +25,7 @@ #include "../jrd/jrd.h" #include "../jrd/req.h" #include "../dsql/StmtNodes.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -42,6 +43,7 @@ LocalTableStream::LocalTableStream(CompilerScratch* csb, StreamType stream, cons fb_assert(m_table); m_impure = csb->allocImpure(); + m_cardinality = DEFAULT_CARDINALITY; } void LocalTableStream::open(thread_db* tdbb) const @@ -113,7 +115,10 @@ void LocalTableStream::print(thread_db* tdbb, string& plan, bool detailed, unsig //// TODO: Use Local Table name/alias. if (detailed) + { plan += printIndent(++level) + "Local Table Full Scan"; + printOptInfo(plan); + } else { if (!level) diff --git a/src/jrd/recsrc/LockedStream.cpp b/src/jrd/recsrc/LockedStream.cpp index 7cebce895c..f25a577e30 100644 --- a/src/jrd/recsrc/LockedStream.cpp +++ b/src/jrd/recsrc/LockedStream.cpp @@ -40,6 +40,7 @@ LockedStream::LockedStream(CompilerScratch* csb, RecordSource* next) fb_assert(m_next); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); } void LockedStream::open(thread_db* tdbb) const @@ -105,7 +106,10 @@ bool LockedStream::lockRecord(thread_db* tdbb) const void LockedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Write Lock"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index c8e79a456e..fbcc8a07ed 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -23,6 +23,7 @@ #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -41,6 +42,7 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, { const size_t size = sizeof(struct Impure) + count * sizeof(Impure::irsb_mrg_repeat); m_impure = csb->allocImpure(FB_ALIGNMENT, static_cast(size)); + m_cardinality = MINIMUM_CARDINALITY; m_args.resize(count); m_keys.resize(count); @@ -50,6 +52,8 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, fb_assert(args[i]); m_args[i] = args[i]; + m_cardinality *= args[i]->getCardinality() * DEFAULT_SELECTIVITY; + fb_assert(keys[i]); m_keys[i] = keys[i]; } @@ -340,6 +344,7 @@ void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned lev if (detailed) { plan += printIndent(++level) + "Merge Join (inner)"; + printOptInfo(plan); for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->print(tdbb, plan, true, level); diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index 9545619c8b..9209549b6e 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -24,6 +24,7 @@ #include "../jrd/evl_proto.h" #include "../jrd/met_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -38,11 +39,15 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSour : m_joinType(INNER_JOIN), m_args(csb->csb_pool), m_boolean(NULL) { m_impure = csb->allocImpure(); + m_cardinality = MINIMUM_CARDINALITY; m_args.resize(count); for (FB_SIZE_T i = 0; i < count; i++) + { m_args[i] = args[i]; + m_cardinality *= args[i]->getCardinality(); + } } NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner, @@ -226,6 +231,8 @@ void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigne fb_assert(false); } + printOptInfo(plan); + for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->print(tdbb, plan, true, level); } diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index 9eb7d95229..34655a7a00 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -29,6 +29,7 @@ #include "../jrd/vio_proto.h" #include "../jrd/trace/TraceManager.h" #include "../jrd/trace/TraceJrdHelpers.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -46,6 +47,7 @@ ProcedureScan::ProcedureScan(CompilerScratch* csb, const string& alias, StreamTy m_procedure(procedure), m_sourceList(sourceList), m_targetList(targetList), m_message(message) { m_impure = csb->allocImpure(); + m_cardinality = DEFAULT_CARDINALITY; fb_assert(!sourceList == !targetList); @@ -247,6 +249,7 @@ void ProcedureScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned { plan += printIndent(++level) + "Procedure " + printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan"; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index be59ce762c..eec46ca288 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -39,6 +39,10 @@ using namespace Firebird; using namespace Jrd; +#ifdef DEV_BUILD +#define PRINT_OPT_INFO // print optimizer info (cardinality, cost) in plans +#endif + // Record source class // ------------------- @@ -172,6 +176,16 @@ void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversio } } +void RecordSource::printOptInfo(string& plan) const +{ +#ifdef PRINT_OPT_INFO + string info; + // Add 0.5 to convert double->int truncation into rounding + info.printf(" [rows: %" UQUADFORMAT "]", (FB_UINT64) (m_cardinality + 0.5)); + plan += info; +#endif +} + RecordSource::~RecordSource() { } diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 4b6f9c1600..fc577c3ef0 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -85,6 +85,11 @@ namespace Jrd return true; } + double getCardinality() const + { + return m_cardinality; + } + protected: // Generic impure block struct Impure @@ -99,7 +104,7 @@ namespace Jrd static const ULONG irsb_singular_processed = 16; RecordSource() - : m_impure(0), m_recursive(false) + : m_impure(0), m_recursive(false), m_cardinality(0.0) {} static Firebird::string printName(thread_db* tdbb, const Firebird::string& name, bool quote = true); @@ -110,12 +115,14 @@ namespace Jrd static void printInversion(thread_db* tdbb, const InversionNode* inversion, Firebird::string& plan, bool detailed, unsigned level, bool navigation = false); + void printOptInfo(Firebird::string& plan) const; static void saveRecord(thread_db* tdbb, record_param* rpb); static void restoreRecord(thread_db* tdbb, record_param* rpb); ULONG m_impure; bool m_recursive; + double m_cardinality; }; @@ -179,7 +186,8 @@ namespace Jrd public: BitmapTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation, InversionNode* inversion); + StreamType stream, jrd_rel* relation, + InversionNode* inversion, double selectivity); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -218,7 +226,8 @@ namespace Jrd public: IndexTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, jrd_rel* relation, - InversionNode* index, USHORT keyLength); + InversionNode* index, USHORT keyLength, + double selectivity); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -473,7 +482,8 @@ namespace Jrd class FilteredStream : public RecordSource { public: - FilteredStream(CompilerScratch* csb, RecordSource* next, BoolExprNode* boolean); + FilteredStream(CompilerScratch* csb, RecordSource* next, + BoolExprNode* boolean, double selectivity); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; diff --git a/src/jrd/recsrc/RecursiveStream.cpp b/src/jrd/recsrc/RecursiveStream.cpp index 3083f39d70..76979db678 100644 --- a/src/jrd/recsrc/RecursiveStream.cpp +++ b/src/jrd/recsrc/RecursiveStream.cpp @@ -51,6 +51,7 @@ RecursiveStream::RecursiveStream(CompilerScratch* csb, StreamType stream, Stream fb_assert(m_root && m_inner && m_rootMap && m_innerMap); m_impure = csb->allocImpure(); + m_cardinality = root->getCardinality() * inner->getCardinality(); m_saveSize = csb->csb_impure - saveOffset; m_innerStreams.resize(streamCount); @@ -248,6 +249,8 @@ void RecursiveStream::print(thread_db* tdbb, string& plan, bool detailed, unsign if (detailed) { plan += printIndent(++level) + "Recursion"; + printOptInfo(plan); + m_root->print(tdbb, plan, true, level); m_inner->print(tdbb, plan, true, level); } diff --git a/src/jrd/recsrc/SingularStream.cpp b/src/jrd/recsrc/SingularStream.cpp index 5e66c943b7..25cce31f91 100644 --- a/src/jrd/recsrc/SingularStream.cpp +++ b/src/jrd/recsrc/SingularStream.cpp @@ -22,6 +22,7 @@ #include "../jrd/req.h" #include "../jrd/cmp_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -40,6 +41,7 @@ SingularStream::SingularStream(CompilerScratch* csb, RecordSource* next) m_next->findUsedStreams(m_streams); m_impure = csb->allocImpure(); + m_cardinality = MINIMUM_CARDINALITY; } void SingularStream::open(thread_db* tdbb) const @@ -145,7 +147,10 @@ bool SingularStream::lockRecord(thread_db* tdbb) const void SingularStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Singularity Check"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } diff --git a/src/jrd/recsrc/SkipRowsStream.cpp b/src/jrd/recsrc/SkipRowsStream.cpp index 3ee32787f3..c2625d2221 100644 --- a/src/jrd/recsrc/SkipRowsStream.cpp +++ b/src/jrd/recsrc/SkipRowsStream.cpp @@ -42,6 +42,7 @@ SkipRowsStream::SkipRowsStream(CompilerScratch* csb, RecordSource* next, ValueEx fb_assert(m_next && m_value); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); } void SkipRowsStream::open(thread_db* tdbb) const @@ -116,7 +117,10 @@ bool SkipRowsStream::lockRecord(thread_db* tdbb) const void SkipRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Skip N Records"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index b74761251e..3c57bfcebf 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -32,6 +32,7 @@ #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -48,6 +49,10 @@ SortedStream::SortedStream(CompilerScratch* csb, RecordSource* next, SortMap* ma fb_assert(m_next && m_map); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); + + if (m_map->flags & FLAG_PROJECT) + m_cardinality *= DEFAULT_SELECTIVITY; } void SortedStream::open(thread_db* tdbb) const @@ -128,6 +133,7 @@ void SortedStream::print(thread_db* tdbb, string& plan, plan += printIndent(++level) + ((m_map->flags & FLAG_PROJECT) ? "Unique Sort" : "Sort") + extras; + printOptInfo(plan); m_next->print(tdbb, plan, true, level); } diff --git a/src/jrd/recsrc/Union.cpp b/src/jrd/recsrc/Union.cpp index 95c9179bbe..94f9beb544 100644 --- a/src/jrd/recsrc/Union.cpp +++ b/src/jrd/recsrc/Union.cpp @@ -46,7 +46,10 @@ Union::Union(CompilerScratch* csb, StreamType stream, m_args.resize(argCount); for (FB_SIZE_T i = 0; i < argCount; i++) + { m_args[i] = args[i]; + m_cardinality += args[i]->getCardinality(); + } m_maps.resize(argCount); @@ -169,6 +172,7 @@ void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) if (detailed) { plan += printIndent(++level) + (m_args.getCount() == 1 ? "Materialize" : "Union"); + printOptInfo(plan); for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->print(tdbb, plan, true, level); diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 2b7d88c7df..21cfac5ac5 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -41,6 +41,7 @@ VirtualTableScan::VirtualTableScan(CompilerScratch* csb, const string& alias, : RecordStream(csb, stream), m_relation(relation), m_alias(csb->csb_pool, alias) { m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality; } void VirtualTableScan::open(thread_db* tdbb) const @@ -115,6 +116,7 @@ void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsig { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index 67449b60e1..f261a7b02c 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -191,6 +191,7 @@ WindowedStream::WindowedStream(thread_db* tdbb, Optimizer* opt, m_next = FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, next); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); // Process the unpartioned and unordered map, if existent. @@ -874,7 +875,10 @@ void WindowedStream::WindowStream::print(thread_db* tdbb, string& plan, bool det unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Window"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } From 343c1f97df13bb99a0552ac3f82992b3ef28d5f3 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 29 Mar 2022 20:04:41 +0300 Subject: [PATCH 137/187] Adjustments for cardinality estimations. Minor optimizer refactoring. --- src/jrd/optimizer/InnerJoin.cpp | 104 ++++++++++++++++++---------- src/jrd/optimizer/Optimizer.cpp | 86 ++++++----------------- src/jrd/optimizer/Optimizer.h | 25 +++---- src/jrd/recsrc/AggregatedStream.cpp | 10 ++- src/jrd/recsrc/HashJoin.cpp | 4 +- src/jrd/recsrc/MergeJoin.cpp | 4 +- src/jrd/recsrc/NestedLoopJoin.cpp | 2 + 7 files changed, 117 insertions(+), 118 deletions(-) diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp index 76435cf65e..3e235a4ef3 100644 --- a/src/jrd/optimizer/InnerJoin.cpp +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -59,15 +59,16 @@ using namespace Jrd; InnerJoin::InnerJoin(thread_db* aTdbb, Optimizer* opt, const StreamList& streams, - SortNode* sort_clause, bool hasPlan) + SortNode** sortClause, bool hasPlan) : PermanentStorage(*aTdbb->getDefaultPool()), tdbb(aTdbb), optimizer(opt), csb(opt->getCompilerScratch()), - sort(sort_clause), + sortPtr(sortClause), plan(hasPlan), innerStreams(getPool(), streams.getCount()), - joinedStreams(getPool()) + joinedStreams(getPool()), + bestStreams(getPool()) { joinedStreams.grow(streams.getCount()); @@ -92,6 +93,8 @@ void InnerJoin::calculateStreamInfo() optimizer->printf("Base stream info:\n"); #endif + const auto sort = sortPtr ? *sortPtr : nullptr; + for (auto innerStream : innerStreams) { streams.add(innerStream->stream); @@ -167,23 +170,25 @@ void InnerJoin::calculateStreamInfo() // Estimate the cost for the stream // -void InnerJoin::estimateCost(StreamType stream, +void InnerJoin::estimateCost(unsigned position, + const StreamInfo* stream, double* cost, - double* resulting_cardinality, - bool start) const + double* resultingCardinality) const { + const auto sort = (position == 0 && sortPtr) ? *sortPtr : nullptr; + // Create the optimizer retrieval generation class and calculate // which indexes will be used and the total estimated selectivity will be returned - Retrieval retrieval(tdbb, optimizer, stream, false, false, (start ? sort : nullptr), true); + Retrieval retrieval(tdbb, optimizer, stream->stream, false, false, sort, true); const auto candidate = retrieval.getInversion(); *cost = candidate->cost; // Calculate cardinality - const auto tail = &csb->csb_rpt[stream]; + const auto tail = &csb->csb_rpt[stream->stream]; const double cardinality = tail->csb_cardinality * candidate->selectivity; - *resulting_cardinality = MAX(cardinality, MINIMUM_CARDINALITY); + *resultingCardinality = MAX(cardinality, MINIMUM_CARDINALITY); } @@ -193,7 +198,7 @@ void InnerJoin::estimateCost(StreamType stream, // Next loop through the remaining streams and find the best order. // -bool InnerJoin::findJoinOrder(StreamList& bestStreams) +bool InnerJoin::findJoinOrder() { bestStreams.clear(); bestCount = 0; @@ -298,7 +303,6 @@ void InnerJoin::findBestOrder(unsigned position, double cost, double cardinality) { - const bool start = (position == 0); const auto tail = &csb->csb_rpt[stream->stream]; // Do some initializations @@ -313,21 +317,21 @@ void InnerJoin::findBestOrder(unsigned position, streamFlags.add(innerStream->used); // Compute delta and total estimate cost to fetch this stream - double position_cost = 0, position_cardinality = 0, new_cost = 0, new_cardinality = 0; + double positionCost = 0, positionCardinality = 0, newCost = 0, newCardinality = 0; if (!plan) { - estimateCost(stream->stream, &position_cost, &position_cardinality, start); - new_cost = cost + cardinality * position_cost; - new_cardinality = position_cardinality * cardinality; + estimateCost(position, stream, &positionCost, &positionCardinality); + newCost = cost + cardinality * positionCost; + newCardinality = positionCardinality * cardinality; } // If the partial order is either longer than any previous partial order, // or the same length and cheap, save order as "best" - if (position > bestCount || (position == bestCount && new_cost < bestCost)) + if (position > bestCount || (position == bestCount && newCost < bestCost)) { bestCount = position; - bestCost = new_cost; + bestCost = newCost; const auto end = joinedStreams.begin() + position; for (auto iter = joinedStreams.begin(); iter != end; ++iter) @@ -353,10 +357,24 @@ void InnerJoin::findBestOrder(unsigned position, // If we know a combination with all streams used and the // current cost is higher as the one from the best we're done - if (bestCount == remainingStreams && bestCost < new_cost) + if (bestCount == remainingStreams && bestCost < newCost) done = true; - if (!done && !plan) + if (plan) + { + // If a explicit PLAN was specific pick the next relation. + // The order in innerStreams is expected to be exactly the order as + // specified in the explicit PLAN. + for (auto nextStream : innerStreams) + { + if (!nextStream->used) + { + findBestOrder(position, nextStream, processList, newCost, newCardinality); + break; + } + } + } + else if (!done) { // Add these relations to the processing list for (auto& relationship : stream->indexedRelationships) @@ -395,22 +413,7 @@ void InnerJoin::findBestOrder(unsigned position, auto relationStreamInfo = getStreamInfo(nextRelationship.stream); if (!relationStreamInfo->used) { - findBestOrder(position, relationStreamInfo, processList, new_cost, new_cardinality); - break; - } - } - } - - if (plan) - { - // If a explicit PLAN was specific pick the next relation. - // The order in innerStreams is expected to be exactly the order as - // specified in the explicit PLAN. - for (auto nextStream : innerStreams) - { - if (!nextStream->used) - { - findBestOrder(position, nextStream, processList, new_cost, new_cardinality); + findBestOrder(position, relationStreamInfo, processList, newCost, newCardinality); break; } } @@ -423,6 +426,37 @@ void InnerJoin::findBestOrder(unsigned position, } +// +// Form streams into rivers (combinations of streams) +// + +River* InnerJoin::formRiver() +{ + fb_assert(bestCount); + fb_assert(bestStreams.hasData()); + + if (bestStreams.getCount() != innerStreams.getCount()) + sortPtr = nullptr; + + HalfStaticArray rsbs; + + for (const auto stream : bestStreams) + { + const auto rsb = optimizer->generateRetrieval(stream, sortPtr, false, false); + rsbs.add(rsb); + sortPtr = nullptr; + } + + const auto rsb = (rsbs.getCount() == 1) ? rsbs[0] : + FB_NEW_POOL(getPool()) NestedLoopJoin(csb, rsbs.getCount(), rsbs.begin()); + + // Allocate a river block and move the best order into it + const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, nullptr, bestStreams); + river->deactivate(csb); + return river; +} + + // // Check if the testStream can use a index when the baseStream is active. If so // then we create a indexRelationship and fill it with the needed information. diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 5a75a6b2e1..c42d1e1daf 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -2137,49 +2137,6 @@ void Optimizer::findDependentStreams(const StreamList& streams, } -// -// Form streams into rivers (combinations of streams) -// - -bool Optimizer::formRiver(unsigned streamCount, - StreamList& streams, - const StreamList& joinedStreams, - RiverList& rivers, - SortNode** sortClause) -{ - const auto count = joinedStreams.getCount(); - fb_assert(count); - - if (count != streamCount) - sortClause = nullptr; - - HalfStaticArray rsbs(count); - - for (const auto stream : joinedStreams) - { - rsbs.add(generateRetrieval(stream, sortClause, false, false, nullptr)); - sortClause = nullptr; - - // Remove already consumed streams from remainingStreams - FB_SIZE_T pos; - if (streams.find(stream, pos)) - streams.remove(pos); - else - fb_assert(false); - } - - const auto rsb = (count == 1) ? rsbs[0] : - FB_NEW_POOL(getPool()) NestedLoopJoin(csb, count, rsbs.begin()); - - // Allocate a river block and move the best order into it - const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, nullptr, joinedStreams); - river->deactivate(csb); - rivers.push(river); - - return streams.hasData(); -} - - // // Form streams into rivers according to the user-specified plan // @@ -2225,23 +2182,14 @@ void Optimizer::formRivers(const StreamList& streams, // AB: Only form rivers when any retrieval node is seen, for // example a MERGE on two JOINs will come with no retrievals // at this point. - // CVC: Notice "plan_node" is pointing to the last element in the loop above. - // If the loop didn't execute, we had garbage in "planNode". if (tempStreams.hasData()) { - const auto count = tempStreams.getCount(); - InnerJoin innerJoin(tdbb, this, tempStreams, - (sortClause ? *sortClause : nullptr), - (planClause != nullptr)); + sortClause, (planClause != nullptr)); - StreamList joinedStreams; - while (innerJoin.findJoinOrder(joinedStreams)) - { - if (!formRiver(count, tempStreams, joinedStreams, rivers, sortClause)) - break; - } + while (innerJoin.findJoinOrder()) + rivers.add(innerJoin.formRiver()); } } @@ -2555,17 +2503,23 @@ void Optimizer::generateInnerJoin(StreamList& streams, return; } - const auto count = streams.getCount(); - InnerJoin innerJoin(tdbb, this, streams, - (sortClause ? *sortClause : nullptr), - (planClause != nullptr)); + sortClause, (planClause != nullptr)); - StreamList joinedStreams; - while (innerJoin.findJoinOrder(joinedStreams)) + while (innerJoin.findJoinOrder()) { - if (!formRiver(count, streams, joinedStreams, rivers, sortClause)) - break; + const auto river = innerJoin.formRiver(); + rivers.add(river); + + // Remove already consumed streams from the source stream list + for (const auto stream : river->getStreams()) + { + FB_SIZE_T pos; + if (streams.find(stream, pos)) + streams.remove(pos); + else + fb_assert(false); + } } } @@ -2637,7 +2591,7 @@ RecordSource* Optimizer::generateOuterJoin(RiverList& rivers, // AB: the sort clause for the inner stream of an OUTER JOIN // should never be used for the index retrieval stream_i.stream_rsb = - generateRetrieval(stream_i.stream_num, nullptr, false, true, nullptr); + generateRetrieval(stream_i.stream_num, nullptr, false, true); } // generate a parent boolean rsb for any remaining booleans that @@ -2664,7 +2618,7 @@ RecordSource* Optimizer::generateOuterJoin(RiverList& rivers, { hasInnerRsb = false; stream_i.stream_rsb = - generateRetrieval(stream_i.stream_num, nullptr, false, true, nullptr); + generateRetrieval(stream_i.stream_num, nullptr, false, true); } const auto innerRsb = generateResidualBoolean(stream_i.stream_rsb); @@ -2695,7 +2649,7 @@ RecordSource* Optimizer::generateOuterJoin(RiverList& rivers, if (!hasOuterRsb) { stream_o.stream_rsb = - generateRetrieval(stream_o.stream_num, nullptr, false, false, nullptr); + generateRetrieval(stream_o.stream_num, nullptr, false, false); } const auto outerRsb = generateResidualBoolean(stream_o.stream_rsb); diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index 24025abee7..364cf94e63 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -370,6 +370,11 @@ public: void compileRelation(StreamType stream); void generateAggregateDistincts(MapNode* map); + RecordSource* generateRetrieval(StreamType stream, + SortNode** sortClause, + bool outerFlag, + bool innerFlag, + BoolExprNode** returnBoolean = nullptr); SortedStream* generateSort(const StreamList& streams, const StreamList* dbkeyStreams, RecordSource* rsb, SortNode* sort, @@ -421,11 +426,6 @@ private: void findDependentStreams(const StreamList& streams, StreamList& dependent_streams, StreamList& free_streams); - bool formRiver(unsigned streamCount, - StreamList& streams, - const StreamList& joinedStreams, - RiverList& rivers, - SortNode** sortClause); void formRivers(const StreamList& streams, RiverList& rivers, SortNode** sortClause, @@ -438,11 +438,6 @@ private: RecordSource* generateOuterJoin(RiverList& rivers, SortNode** sortClause); RecordSource* generateResidualBoolean(RecordSource* rsb); - RecordSource* generateRetrieval(StreamType stream, - SortNode** sortClause, - bool outerFlag, - bool innerFlag, - BoolExprNode** returnBoolean); BoolExprNode* makeInferenceNode(BoolExprNode* boolean, ValueExprNode* arg1, ValueExprNode* arg2); @@ -740,7 +735,7 @@ class InnerJoin : private Firebird::PermanentStorage public: InnerJoin(thread_db* tdbb, Optimizer* opt, const StreamList& streams, - SortNode* sort_clause, bool hasPlan); + SortNode** sortClause, bool hasPlan); ~InnerJoin() { @@ -748,11 +743,12 @@ public: delete innerStream; } - bool findJoinOrder(StreamList& bestStreams); + bool findJoinOrder(); + River* formRiver(); protected: void calculateStreamInfo(); - void estimateCost(StreamType stream, double* cost, double* resulting_cardinality, bool start) const; + void estimateCost(unsigned position, const StreamInfo* stream, double* cost, double* resultingCardinality) const; void findBestOrder(unsigned position, StreamInfo* stream, IndexedRelationships& processList, double cost, double cardinality); void getIndexedRelationships(StreamInfo* testStream); @@ -769,7 +765,7 @@ private: thread_db* const tdbb; Optimizer* const optimizer; CompilerScratch* const csb; - SortNode* const sort; + SortNode** sortPtr; const bool plan; unsigned remainingStreams = 0; @@ -778,6 +774,7 @@ private: StreamInfoList innerStreams; JoinedStreamList joinedStreams; + StreamList bestStreams; }; } // namespace Jrd diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index ee63ee8f3c..14c94ec1ec 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -51,7 +51,15 @@ BaseAggWinStream::BaseAggWinStream(thread_db* tdbb, Compiler fb_assert(m_next); m_impure = csb->allocImpure(); - m_cardinality = group ? next->getCardinality() * DEFAULT_SELECTIVITY : MINIMUM_CARDINALITY; + + if (group) + { + m_cardinality = next->getCardinality(); + for (auto count = group->getCount(); count; count--) + m_cardinality *= REDUCE_SELECTIVITY_FACTOR_EQUALITY; + } + else + m_cardinality = MINIMUM_CARDINALITY; } template diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index 118d369c43..a960489080 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -250,7 +250,9 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, RecordSource* const sub_rsb = args[i]; fb_assert(sub_rsb); - m_cardinality *= sub_rsb->getCardinality() * DEFAULT_SELECTIVITY; + m_cardinality *= sub_rsb->getCardinality(); + for (auto keyCount = keys[i]->getCount(); keyCount; keyCount--) + m_cardinality *= REDUCE_SELECTIVITY_FACTOR_EQUALITY; SubStream sub; sub.buffer = FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, sub_rsb); diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index fbcc8a07ed..8aedd7bdb3 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -52,7 +52,9 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, fb_assert(args[i]); m_args[i] = args[i]; - m_cardinality *= args[i]->getCardinality() * DEFAULT_SELECTIVITY; + m_cardinality *= args[i]->getCardinality(); + for (auto keyCount = keys[i]->getCount(); keyCount; keyCount--) + m_cardinality *= REDUCE_SELECTIVITY_FACTOR_EQUALITY; fb_assert(keys[i]); m_keys[i] = keys[i]; diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index 9209549b6e..e7adca7aa1 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -60,6 +60,8 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, Record m_args.add(outer); m_args.add(inner); + + m_cardinality = outer->getCardinality() * inner->getCardinality(); } void NestedLoopJoin::open(thread_db* tdbb) const From ae1c85f8f8d411c7ff347028455a80c0a32413d1 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 30 Mar 2022 10:49:45 +0300 Subject: [PATCH 138/187] Minor refactoring. Reworked fix for #3218 (Optimizer fails applying stream-local predicates before merging), now it covers both cases mentioned in the ticket. --- src/jrd/optimizer/Optimizer.cpp | 244 ++++++++++++++++---------------- src/jrd/optimizer/Optimizer.h | 14 +- 2 files changed, 130 insertions(+), 128 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index c42d1e1daf..ff07c4b10f 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -778,6 +778,9 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) outerStreams.join(localStreams); } + // Apply local booleans, if any + rsb = applyLocalBoolean(rsb, localStreams); + const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, node, localStreams); river->deactivate(csb); rivers.add(river); @@ -858,24 +861,24 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) // no merge is made between a new cross river and the // currently active rivers. Where in the new cross river // a stream depends (index) on the active rivers. - StreamList dependent_streams, free_streams; - findDependentStreams(joinStreams, dependent_streams, free_streams); + StreamList dependentStreams, freeStreams; + findDependentStreams(joinStreams, dependentStreams, freeStreams); // If we have dependent and free streams then we can't rely on // the sort node to be used for index navigation - if (dependent_streams.hasData() && free_streams.hasData()) + if (dependentStreams.hasData() && freeStreams.hasData()) { sort = nullptr; sortCanBeUsed = false; } - if (dependent_streams.hasData()) + if (dependentStreams.hasData()) { // Copy free streams - joinStreams.assign(free_streams); + joinStreams.assign(freeStreams); // Make rivers from the dependent streams - generateInnerJoin(dependent_streams, rivers, &sort, rse->rse_plan); + generateInnerJoin(dependentStreams, rivers, &sort, rse->rse_plan); // Generate one river which holds a cross join rsb between // all currently available rivers @@ -885,7 +888,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) } else { - if (free_streams.hasData()) + if (freeStreams.hasData()) { // Deactivate streams from rivers on stack, because // the remaining streams don't have any indexed relationship with them @@ -958,7 +961,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) rsb = FB_NEW_POOL(getPool()) FirstRowsStream(csb, rsb, rse->rse_first); if (rse->flags & RseNode::FLAG_SINGULAR) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) SingularStream(csb, rsb); + rsb = FB_NEW_POOL(getPool()) SingularStream(csb, rsb); if (rse->flags & RseNode::FLAG_WRITELOCK) { @@ -974,11 +977,11 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) SCL_update, obj_relations, tail->csb_relation->rel_name); } - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) LockedStream(csb, rsb); + rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb); } if (rse->flags & RseNode::FLAG_SCROLLABLE) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) BufferedStream(csb, rsb); + rsb = FB_NEW_POOL(getPool()) BufferedStream(csb, rsb); return rsb; } @@ -1455,12 +1458,13 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, // Find conjuncts local to the given river and compose an appropriate filter // -RecordSource* Optimizer::applyLocalBoolean(const River* river) +RecordSource* Optimizer::applyLocalBoolean(RecordSource* rsb, const StreamList& streams) { - StreamStateHolder stateHolder(csb); - stateHolder.deactivate(); + StreamStateHolder globalHolder(csb); + globalHolder.deactivate(); - river->activate(csb); + StreamStateHolder localHolder(csb, streams); + localHolder.activate(csb); BoolExprNode* boolean = nullptr; double selectivity = MAXIMUM_SELECTIVITY; @@ -1479,7 +1483,6 @@ RecordSource* Optimizer::applyLocalBoolean(const River* river) } } - const auto rsb = river->getRecordSource(); return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity) : rsb; } @@ -2203,7 +2206,7 @@ void Optimizer::formRivers(const StreamList& streams, // If the whole things is a moby no-op, return false. // -bool Optimizer::generateEquiJoin(RiverList& org_rivers) +bool Optimizer::generateEquiJoin(RiverList& orgRivers) { ULONG selected_rivers[OPT_STREAM_BITS], selected_rivers2[OPT_STREAM_BITS]; ValueExprNode** eq_class; @@ -2212,13 +2215,13 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) // a scratch block large enough to hold values to compute equality // classes. - const unsigned cnt = (unsigned) org_rivers.getCount(); + const unsigned orgCount = (unsigned) orgRivers.getCount(); - if (cnt < 2) + if (orgCount < 2) return false; HalfStaticArray scratch; - scratch.grow(baseConjuncts * cnt); + scratch.grow(baseConjuncts * orgCount); ValueExprNode** classes = scratch.begin(); // Compute equivalence classes among streams. This involves finding groups @@ -2231,47 +2234,15 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) if (iter & CONJUNCT_USED) continue; - const auto cmpNode = nodeAs(*iter); + NestConst node1; + NestConst node2; - if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv)) + if (!getEquiJoinKeys(*iter, &node1, &node2, true)) continue; - ValueExprNode* node1 = cmpNode->arg1; - ValueExprNode* node2 = cmpNode->arg2; - - dsc result, desc1, desc2; - node1->getDesc(tdbb, csb, &desc1); - node2->getDesc(tdbb, csb, &desc2); - - // Ensure that arguments can be compared in the binary form - if (!CVT2_get_binary_comparable_desc(&result, &desc1, &desc2)) - continue; - - // Cast the arguments, if required - if (!DSC_EQUIV(&result, &desc1, true) || !DSC_EQUIV(&result, &desc2, true)) + for (unsigned i = 0; i < orgRivers.getCount(); i++) { - if (!DSC_EQUIV(&result, &desc1, true)) - { - const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); - cast->source = node1; - cast->castDesc = result; - cast->impureOffset = csb->allocImpure(); - node1 = cast; - } - - if (!DSC_EQUIV(&result, &desc2, true)) - { - const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); - cast->source = node2; - cast->castDesc = result; - cast->impureOffset = csb->allocImpure(); - node2 = cast; - } - } - - for (unsigned i = 0; i < org_rivers.getCount(); i++) - { - const auto river1 = org_rivers[i]; + const auto river1 = orgRivers[i]; if (!river1->isReferenced(node1)) { @@ -2283,13 +2254,13 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) node2 = temp; } - for (unsigned j = i + 1; j < org_rivers.getCount(); j++) + for (unsigned j = i + 1; j < orgRivers.getCount(); j++) { - const auto river2 = org_rivers[j]; + const auto river2 = orgRivers[j]; if (river2->isReferenced(node2)) { - for (eq_class = classes; eq_class < last_class; eq_class += cnt) + for (eq_class = classes; eq_class < last_class; eq_class += orgCount) { if (fieldEqual(node1, classes[i]) || fieldEqual(node2, classes[j])) @@ -2302,7 +2273,7 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) eq_class[j] = node2; if (eq_class == last_class) - last_class += cnt; + last_class += orgCount; } } } @@ -2312,23 +2283,23 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) // Obviously, if the set of classes is empty, return false // to indicate that nothing could be done. - unsigned river_cnt = 0; - HalfStaticArray selected_classes(cnt); + unsigned riverCount = 0; + HalfStaticArray selected_classes(orgCount); - for (eq_class = classes; eq_class < last_class; eq_class += cnt) + for (eq_class = classes; eq_class < last_class; eq_class += orgCount) { - unsigned i = getRiverCount(cnt, eq_class); + unsigned i = getRiverCount(orgCount, eq_class); - if (i > river_cnt) + if (i > riverCount) { - river_cnt = i; + riverCount = i; selected_classes.shrink(0); selected_classes.add(eq_class); - classMask(cnt, eq_class, selected_rivers); + classMask(orgCount, eq_class, selected_rivers); } else { - classMask(cnt, eq_class, selected_rivers2); + classMask(orgCount, eq_class, selected_rivers2); for (i = 0; i < OPT_STREAM_BITS; i++) { @@ -2341,30 +2312,21 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) } } - if (!river_cnt) + if (!riverCount) return false; - // AB: Deactivate currently all streams from every river, because we - // need to know which nodes are computable between the rivers used - // for the merge. - - StreamStateHolder stateHolder(csb); - stateHolder.deactivate(); - HalfStaticArray rsbs; HalfStaticArray keys; - // Unconditionally disable merge joins in favor of hash joins. - // This is a temporary debugging measure. - bool prefer_merge_over_hash = false; + bool useMergeJoin = false; // AB: Get the lowest river position from the rivers that are merged - RiverList rivers_to_merge; - unsigned lowest_river_position = MAX_ULONG; - unsigned number = 0; + StreamList streams; + RiverList rivers; + unsigned number = 0, lowestPosition = MAX_ULONG; - for (River** iter = org_rivers.begin(); iter < org_rivers.end(); number++) + for (River** iter = orgRivers.begin(); iter < orgRivers.end(); number++) { River* const river = *iter; @@ -2374,21 +2336,20 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) continue; } - if (number < lowest_river_position) - lowest_river_position = number; + if (number < lowestPosition) + lowestPosition = number; - rivers_to_merge.add(river); - org_rivers.remove(iter); + streams.join(river->getStreams()); + rivers.add(river); + orgRivers.remove(iter); - // Apply local river booleans, if any - - auto rsb = applyLocalBoolean(river); + auto rsb = river->getRecordSource(); // Collect RSBs and keys to join const auto key = FB_NEW_POOL(getPool()) SortNode(getPool()); - if (prefer_merge_over_hash) + if (useMergeJoin) { ValueExprNode*** selected_class; @@ -2400,9 +2361,7 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) key->expressions.add((*selected_class)[number]); } - StreamList streams; - streams.assign(river->getStreams()); - rsb = generateSort(streams, nullptr, rsb, key, favorFirstRows(), false); + rsb = generateSort(river->getStreams(), nullptr, rsb, key, favorFirstRows(), false); } else { @@ -2419,7 +2378,7 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) // For a hash join, we need to choose the smallest ones as inner sub-streams, // hence we reverse the order when storing them in the temporary arrays. - if (prefer_merge_over_hash) + if (useMergeJoin) { rsbs.add(rsb); keys.add(&key->expressions); @@ -2437,7 +2396,7 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) RecordSource* rsb = nullptr; - if (prefer_merge_over_hash) + if (useMergeJoin) { rsb = FB_NEW_POOL(getPool()) MergeJoin(csb, rsbs.getCount(), (SortedStream**) rsbs.begin(), keys.begin()); @@ -2448,36 +2407,11 @@ bool Optimizer::generateEquiJoin(RiverList& org_rivers) HashJoin(tdbb, csb, rsbs.getCount(), rsbs.begin(), keys.begin()); } - // Activate streams of all the rivers being merged - - for (const auto river : rivers_to_merge) - river->activate(csb); - // Pick up any boolean that may apply + rsb = applyLocalBoolean(rsb, streams); - BoolExprNode* boolean = nullptr; - double selectivity = MAXIMUM_SELECTIVITY; - - for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) - { - if (!(iter & CONJUNCT_USED) && - !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && - iter->computable(csb, INVALID_STREAM, false)) - { - compose(getPool(), &boolean, iter); - iter |= CONJUNCT_USED; - - if (!(iter & CONJUNCT_MATCHED)) - selectivity *= getSelectivity(*iter); - } - } - - if (boolean) - rsb = FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity); - - const auto merged_river = FB_NEW_POOL(getPool()) River(csb, rsb, rivers_to_merge); - - org_rivers.insert(lowest_river_position, merged_river); + const auto finalRiver = FB_NEW_POOL(getPool()) River(csb, rsb, rivers); + orgRivers.insert(lowestPosition, finalRiver); return true; } @@ -2882,6 +2816,68 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, } +// +// Check whether the given boolean can be involved in a equi-join relationship +// + +bool Optimizer::getEquiJoinKeys(BoolExprNode* boolean, + NestConst* node1, + NestConst* node2, + bool needCast) +{ + auto cmpNode = nodeAs(boolean); + if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv)) + return false; + + auto arg1 = cmpNode->arg1; + auto arg2 = cmpNode->arg2; + + if (!getEquiJoinKeys(arg1, arg2, needCast)) + return false; + + *node1 = arg1; + *node2 = arg2; + return true; +} + +bool Optimizer::getEquiJoinKeys(NestConst& node1, + NestConst& node2, + bool needCast) +{ + dsc result, desc1, desc2; + node1->getDesc(tdbb, csb, &desc1); + node2->getDesc(tdbb, csb, &desc2); + + // Ensure that arguments can be compared in the binary form + if (!CVT2_get_binary_comparable_desc(&result, &desc1, &desc2)) + return false; + + // Cast the arguments to the common data type, if required + if (needCast) + { + if (!DSC_EQUIV(&result, &desc1, true)) + { + const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); + cast->source = node1; + cast->castDesc = result; + cast->impureOffset = csb->allocImpure(); + node1 = cast; + } + + if (!DSC_EQUIV(&result, &desc2, true)) + { + const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); + cast->source = node2; + cast->castDesc = result; + cast->impureOffset = csb->allocImpure(); + node2 = cast; + } + } + + return true; +} + + // // Make an alias string suitable for printing as part of the plan. // For views, this means multiple aliases to distinguish the base table. diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index 364cf94e63..f05b3d2b92 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -156,12 +156,10 @@ class River { public: River(CompilerScratch* csb, RecordSource* rsb, RecordSourceNode* node, const StreamList& streams) - : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) + : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool, streams) { if (node) m_nodes.add(node); - - m_streams.assign(streams); } River(CompilerScratch* csb, RecordSource* rsb, RiverList& rivers) @@ -410,6 +408,14 @@ public: return (rse->flags & RseNode::FLAG_OPT_FIRST_ROWS) != 0; } + bool getEquiJoinKeys(BoolExprNode* boolean, + NestConst* node1, + NestConst* node2, + bool needCast = false); + bool getEquiJoinKeys(NestConst& node1, + NestConst& node2, + bool needCast = false); + Firebird::string makeAlias(StreamType stream); void printf(const char* format, ...); @@ -418,7 +424,7 @@ private: RecordSource* compile(BoolExprNodeStack* parentStack); - RecordSource* applyLocalBoolean(const River* river); + RecordSource* applyLocalBoolean(RecordSource* rsb, const StreamList& streams); void checkIndices(); void checkSorts(); unsigned decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack); From e62e4c2e0d0d1f75c30113feeb518da7705ad7cc Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 31 Mar 2022 00:06:42 +0000 Subject: [PATCH 139/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 03386f5368..a0af766590 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:442 + FORMAL BUILD NUMBER:445 */ -#define PRODUCT_VER_STRING "5.0.0.442" -#define FILE_VER_STRING "WI-T5.0.0.442" -#define LICENSE_VER_STRING "WI-T5.0.0.442" -#define FILE_VER_NUMBER 5, 0, 0, 442 +#define PRODUCT_VER_STRING "5.0.0.445" +#define FILE_VER_STRING "WI-T5.0.0.445" +#define LICENSE_VER_STRING "WI-T5.0.0.445" +#define FILE_VER_NUMBER 5, 0, 0, 445 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "442" +#define FB_BUILD_NO "445" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index fc0b029e82..6c71f27ee8 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=442 +BuildNum=445 NowAt=`pwd` cd `dirname $0` From d662da75930339504b3bc5eea0d9e19940c2d003 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 30 Mar 2022 13:38:50 +0300 Subject: [PATCH 140/187] Misc debugging improvements --- src/jrd/optimizer/InnerJoin.cpp | 76 ++++++++++++++++++--------------- src/jrd/optimizer/Optimizer.cpp | 41 +++++++++++++++++- src/jrd/optimizer/Optimizer.h | 2 + src/jrd/optimizer/Retrieval.cpp | 36 +++++++++------- 4 files changed, 103 insertions(+), 52 deletions(-) diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp index 3e235a4ef3..519188645b 100644 --- a/src/jrd/optimizer/InnerJoin.cpp +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -265,10 +265,6 @@ bool InnerJoin::findJoinOrder() break; } } -#ifdef OPT_DEBUG - // Debug - printProcessList(indexedRelationships, innerStream->stream); -#endif } } } @@ -343,7 +339,7 @@ void InnerJoin::findBestOrder(unsigned position, #ifdef OPT_DEBUG // Debug information - printFoundOrder(position, position_cost, position_cardinality, new_cost, new_cardinality); + printFoundOrder(position, positionCost, positionCardinality, newCost, newCardinality); #endif // Mark this stream as "used" in the sense that it is already included @@ -467,7 +463,9 @@ River* InnerJoin::formRiver() void InnerJoin::getIndexedRelationships(StreamInfo* testStream) { #ifdef OPT_DEBUG_RETRIEVAL - optimizer->printf("Dependencies for stream %u:\n", testStream->stream); + const auto name = optimizer->getStreamName(testStream->stream); + optimizer->printf("Dependencies for stream %u (%s):\n", + testStream->stream, name.c_str()); #endif const auto tail = &csb->csb_rpt[testStream->stream]; @@ -533,15 +531,21 @@ InnerJoin::StreamInfo* InnerJoin::getStreamInfo(StreamType stream) // Dump finally selected stream order void InnerJoin::printBestOrder() const { - optimizer->printf(" best order, streams: "); - auto iter = joinedStreams.begin(); - const auto end = iter + bestCount; - for (; iter < end; iter++) + if (bestStreams.isEmpty()) + return; + + optimizer->printf(" best order, streams:"); + + const auto end = bestStreams.end(); + for (auto iter = bestStreams.begin(); iter != end; iter++) { - optimizer->printf("%u", iter->bestStream); + const auto name = optimizer->getStreamName(*iter); + optimizer->printf(" %u (%s)", *iter, name.c_str()); + if (iter != end - 1) - optimizer->printf(", "); + optimizer->printf(","); } + optimizer->printf("\n"); } @@ -552,51 +556,53 @@ void InnerJoin::printFoundOrder(StreamType position, double cost, double cardinality) const { - optimizer->printf(" position %2.2u:", position); - optimizer->printf(" pos. cardinality (%10.2f), pos. cost (%10.2f)", positionCardinality, positionCost); - optimizer->printf(" cardinality (%10.2f), cost (%10.2f)", cardinality, cost); - optimizer->printf(", streams: "); + for (auto i = position - 1; i > 0; i--) + optimizer->printf(" "); + + optimizer->printf(" #%2.2u, streams:", position); + auto iter = joinedStreams.begin(); const auto end = iter + position; for (; iter < end; iter++) { - optimizer->printf("%u", iter->number); - if (iter != end - 1) - optimizer->printf(", "); - } - optimizer->printf("\n"); -} + const auto name = optimizer->getStreamName(iter->number); + optimizer->printf(" %u (%s)", iter->number, name.c_str()); -// Dump the processlist to a debug file -void InnerJoin::printProcessList(const IndexedRelationships& processList, - StreamType stream) const -{ - optimizer->printf(" base stream %u, relationships: stream (cost)", stream); - const auto end = processList.end(); - for (auto iter = processList.begin(); iter != end; iter++) - { - optimizer->printf("%u (%1.2f)", iter->stream, iter->cost); if (iter != end - 1) - optimizer->printf(", "); + optimizer->printf(","); } + + optimizer->printf("\n"); + + for (auto i = position - 1; i > 0; i--) + optimizer->printf(" "); + + optimizer->printf(" position cardinality (%10.2f), position cost (%10.2f),", positionCardinality, positionCost); + optimizer->printf(" cardinality (%10.2f), cost (%10.2f)", cardinality, cost); + optimizer->printf("\n"); } // Dump finally selected stream order void InnerJoin::printStartOrder() const { - optimizer->printf("Start join order, stream (baseCost): "); + optimizer->printf("Start join order, streams:"); + const auto end = innerStreams.end(); for (auto iter = innerStreams.begin(); iter != end; iter++) { const auto innerStream = *iter; if (!innerStream->used) { - optimizer->printf("%u (%1.2f)", innerStream->stream, innerStream->baseCost); + const auto name = optimizer->getStreamName(innerStream->stream); + optimizer->printf(" %u (%s) base cost (%1.2f)", + innerStream->stream, name.c_str(), innerStream->baseCost); + if (iter != end - 1) - optimizer->printf(", "); + optimizer->printf(","); } } + optimizer->printf("\n"); } #endif diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index ff07c4b10f..4cf6981e45 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -95,6 +95,10 @@ using namespace Firebird; #define OPT_DEBUG #endif +#ifdef OPT_DEBUG_SYS_REQUESTS +#define OPT_DEBUG +#endif + #ifdef OPT_DEBUG #define OPTIMIZER_DEBUG_FILE "opt_debug.out" #endif @@ -2878,6 +2882,35 @@ bool Optimizer::getEquiJoinKeys(NestConst& node1, } +// +// Compose a table name (including alias, if specified) for the given stream +// + +string Optimizer::getStreamName(StreamType stream) +{ + const auto tail = &csb->csb_rpt[stream]; + const auto relation = tail->csb_relation; + const auto procedure = tail->csb_procedure; + const auto alias = tail->csb_alias; + + string name; + + if (relation) + name = relation->rel_name.c_str(); + else if (procedure) + name = procedure->getName().toString(); + + if (alias && alias->hasData()) + { + if (name.hasData()) + name += " as "; + name += *alias; + } + + return name; +} + + // // Make an alias string suitable for printing as part of the plan. // For views, this means multiple aliases to distinguish the base table. @@ -3168,6 +3201,11 @@ ValueExprNode* Optimizer::optimizeLikeSimilar(ComparativeBoolNode* cmpNode) void Optimizer::printf(const char* format, ...) { +#ifndef OPT_DEBUG_SYS_REQUESTS + if (csb->csb_g_flags & csb_internal) + return; +#endif + #ifdef OPT_DEBUG if (!debugFile) debugFile = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); @@ -3180,6 +3218,7 @@ void Optimizer::printf(const char* format, ...) str.vprintf(format, arglist); va_end(arglist); - fprintf(debugFile, str.c_str()); + fprintf(debugFile, "%s", str.c_str()); + fflush(debugFile); #endif } diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index f05b3d2b92..21d5ff310c 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -31,6 +31,7 @@ //#define OPT_DEBUG //#define OPT_DEBUG_RETRIEVAL +//#define OPT_DEBUG_SYS_REQUESTS #include "../common/classes/alloc.h" #include "../common/classes/array.h" @@ -416,6 +417,7 @@ public: NestConst& node2, bool needCast = false); + Firebird::string getStreamName(StreamType stream); Firebird::string makeAlias(StreamType stream); void printf(const char* format, ...); diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 4e52a09a80..2df0b3e71c 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -2179,22 +2179,32 @@ bool Retrieval::validateStarts(IndexScratch* indexScratch, #ifdef OPT_DEBUG_RETRIEVAL void Retrieval::printCandidate(const InversionCandidate* candidate) const { - optimizer->printf(" cost (%1.2f), selectivity (%1.10f), indexes (%d), matched (%d, %d)", + optimizer->printf(" cost (%1.2f), selectivity (%1.10f), indexes (%d), matched (%d, %d)", candidate->cost, candidate->selectivity, candidate->indexes, candidate->matchedSegments, candidate->nonFullMatchedSegments); + if (candidate->unique) optimizer->printf(", unique"); + if (candidate->dependentFromStreams.hasData()) { - optimizer->printf(", dependent from streams: "); + optimizer->printf(", dependent from streams:"); + const auto end = candidate->dependentFromStreams.end(); for (auto iter = candidate->dependentFromStreams.begin(); iter != end; iter++) { - optimizer->printf("%u", *iter); + const auto name = optimizer->getStreamName(*iter); + + if (name.hasData()) + optimizer->printf(" %u (%s)", *iter, name.c_str()); + else + optimizer->printf(" %u", *iter); + if (iter != end - 1) - optimizer->printf(", "); + optimizer->printf(","); } } + optimizer->printf("\n"); } @@ -2203,12 +2213,9 @@ void Retrieval::printCandidates(const InversionCandidateList& inversions) const if (inversions.getCount() < 2) return; - const auto tail = &csb->csb_rpt[stream]; - - string relName(tail->csb_relation->rel_name.c_str()); - if (tail->csb_alias) - relName += " as " + *tail->csb_alias; - optimizer->printf(" retrieval candidates for stream %u (%s):\n", stream, relName.c_str()); + const auto name = optimizer->getStreamName(stream); + optimizer->printf(" retrieval candidates for stream %u (%s):\n", + stream, name.c_str()); for (const auto candidate : inversions) printCandidate(candidate); @@ -2219,12 +2226,9 @@ void Retrieval::printFinalCandidate(const InversionCandidate* candidate) const if (!candidate) return; - const auto tail = &csb->csb_rpt[stream]; - - string relName(tail->csb_relation->rel_name.c_str()); - if (tail->csb_alias) - relName += " as " + *tail->csb_alias; - optimizer->printf(" final candidate for stream %u (%s):\n", stream, relName.c_str()); + const auto name = optimizer->getStreamName(stream); + optimizer->printf(" final candidate for stream %u (%s):\n", + stream, name.c_str()); printCandidate(candidate); } From f57eb26b629088a52cb51aefb733532653e685b8 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Thu, 31 Mar 2022 18:29:36 +0300 Subject: [PATCH 141/187] This should fix regressions caused by my prior commit(s). --- src/jrd/optimizer/Optimizer.cpp | 19 ++++++++++++------- src/jrd/optimizer/Optimizer.h | 4 +++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 4cf6981e45..3a68756a74 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -780,10 +780,12 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) { subStreams.join(localStreams); outerStreams.join(localStreams); - } - // Apply local booleans, if any - rsb = applyLocalBoolean(rsb, localStreams); + // Apply local booleans, if any. Note that it's done + // only for inner joins and outer streams of left joins. + auto iter = getConjuncts(false, !isInnerJoin()); + rsb = applyLocalBoolean(rsb, localStreams, iter); + } const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, node, localStreams); river->deactivate(csb); @@ -1462,7 +1464,9 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, // Find conjuncts local to the given river and compose an appropriate filter // -RecordSource* Optimizer::applyLocalBoolean(RecordSource* rsb, const StreamList& streams) +RecordSource* Optimizer::applyLocalBoolean(RecordSource* rsb, + const StreamList& streams, + ConjunctIterator& iter) { StreamStateHolder globalHolder(csb); globalHolder.deactivate(); @@ -1473,7 +1477,7 @@ RecordSource* Optimizer::applyLocalBoolean(RecordSource* rsb, const StreamList& BoolExprNode* boolean = nullptr; double selectivity = MAXIMUM_SELECTIVITY; - for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + for (iter.rewind(); iter.hasData(); ++iter) { if (!(iter & CONJUNCT_USED) && !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && @@ -2233,7 +2237,8 @@ bool Optimizer::generateEquiJoin(RiverList& orgRivers) ValueExprNode** last_class = classes; - for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + auto iter = getBaseConjuncts(); + for (; iter.hasData(); ++iter) { if (iter & CONJUNCT_USED) continue; @@ -2412,7 +2417,7 @@ bool Optimizer::generateEquiJoin(RiverList& orgRivers) } // Pick up any boolean that may apply - rsb = applyLocalBoolean(rsb, streams); + rsb = applyLocalBoolean(rsb, streams, iter); const auto finalRiver = FB_NEW_POOL(getPool()) River(csb, rsb, rivers); orgRivers.insert(lowestPosition, finalRiver); diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index 21d5ff310c..a82fd91236 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -426,7 +426,9 @@ private: RecordSource* compile(BoolExprNodeStack* parentStack); - RecordSource* applyLocalBoolean(RecordSource* rsb, const StreamList& streams); + RecordSource* applyLocalBoolean(RecordSource* rsb, + const StreamList& streams, + ConjunctIterator& iter); void checkIndices(); void checkSorts(); unsigned decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack); From 12fc1950930d9f270a096776a12add4296801917 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Thu, 31 Mar 2022 18:34:01 +0300 Subject: [PATCH 142/187] Code simplification --- src/jrd/optimizer/Optimizer.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 3a68756a74..879c4e3960 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -541,13 +541,8 @@ namespace NestConst* to_ptr = to_clause->expressions.begin(); for (const auto to_end = to_ptr + count; to_ptr != to_end; ++to_ptr) { - const auto fromField = nodeAs(*from_ptr); - const auto toField = nodeAs(*to_ptr); - if ((map && mapEqual(*to_ptr, *from_ptr, map)) || - (!map && fromField && toField && - fromField->fieldStream == toField->fieldStream && - fromField->fieldId == toField->fieldId)) + (!map && fieldEqual(*to_ptr, *from_ptr))) { ValueExprNode* swap = *to_swap; *to_swap = *to_ptr; @@ -1646,12 +1641,10 @@ void Optimizer::checkSorts() bool equal = true; for (unsigned i = 0; i < sortCount; i++) { - const auto sortField = nodeAs(sort->expressions[i]); - const auto projectField = nodeAs(project->expressions[i]); + const auto sortNode = sort->expressions[i]; + const auto projectNode = project->expressions[i]; - if (!sortField || !projectField || - sortField->fieldStream != projectField->fieldStream || - sortField->fieldId != projectField->fieldId) + if (!fieldEqual(sortNode, projectNode)) { equal = false; break; From 8e23bb7c134509c1daf7fc2789f3f6ff34693695 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 1 Apr 2022 00:07:31 +0000 Subject: [PATCH 143/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index a0af766590..e7a55b3bd6 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:445 + FORMAL BUILD NUMBER:448 */ -#define PRODUCT_VER_STRING "5.0.0.445" -#define FILE_VER_STRING "WI-T5.0.0.445" -#define LICENSE_VER_STRING "WI-T5.0.0.445" -#define FILE_VER_NUMBER 5, 0, 0, 445 +#define PRODUCT_VER_STRING "5.0.0.448" +#define FILE_VER_STRING "WI-T5.0.0.448" +#define LICENSE_VER_STRING "WI-T5.0.0.448" +#define FILE_VER_NUMBER 5, 0, 0, 448 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "445" +#define FB_BUILD_NO "448" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 6c71f27ee8..13d3422935 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=445 +BuildNum=448 NowAt=`pwd` cd `dirname $0` From 7ee34987dc5408b0e86424a3e5e5560ce7ca25e2 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 1 Apr 2022 14:28:58 +0300 Subject: [PATCH 144/187] Next round of optimizer refactoring. Make sure booleans delivered to the inner RSEs and utilized there are not evaluated multiple times. --- src/jrd/RecordSourceNodes.cpp | 122 +++++++++++------------------ src/jrd/RecordSourceNodes.h | 10 --- src/jrd/optimizer/Optimizer.cpp | 90 +++++++++++++-------- src/jrd/optimizer/Optimizer.h | 16 ++-- src/jrd/recsrc/RecordSource.h | 4 +- src/jrd/recsrc/RecursiveStream.cpp | 9 +-- src/jrd/recsrc/Union.cpp | 9 +-- 7 files changed, 121 insertions(+), 139 deletions(-) diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 881d2a6d49..345d63f9b5 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -48,8 +48,8 @@ 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); static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Format** inputFormat); -static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverStack, MapNode* map, - BoolExprNodeStack* parentStack, StreamType shellStream); +static void genDeliverUnmapped(CompilerScratch* csb, const BoolExprNodeStack& parentStack, + BoolExprNodeStack& deliverStack, MapNode* map, StreamType shellStream); static ValueExprNode* resolveUsingField(DsqlCompilerScratch* dsqlScratch, const MetaName& name, ValueListNode* list, const FieldNode* flawedNode, const TEXT* side, dsql_ctx*& ctx); @@ -1238,14 +1238,6 @@ void ProcedureSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - return generate(tdbb, opt); -} - -// Compile and optimize a record selection expression into a set of record source blocks (rsb's). -ProcedureScan* ProcedureSourceNode::generate(thread_db* tdbb, Optimizer* opt) -{ - SET_TDBB(tdbb); - const auto csb = opt->getCompilerScratch(); const string alias = opt->makeAlias(stream); @@ -1585,20 +1577,6 @@ bool AggregateSourceNode::containsStream(StreamType checkStream) const RecordSource* AggregateSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - BoolExprNodeStack conjunctStack; - for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) - conjunctStack.push(iter); - - return generate(tdbb, opt, &conjunctStack, stream); -} - -// Generate a RecordSource (Record Source Block) for each aggregate operation. -// Generate an AggregateSort (Aggregate SortedStream Block) for each DISTINCT aggregate. -RecordSource* AggregateSourceNode::generate(thread_db* tdbb, Optimizer* opt, - BoolExprNodeStack* parentStack, StreamType shellStream) -{ - SET_TDBB(tdbb); - const auto csb = opt->getCompilerScratch(); rse->rse_sorted = group; @@ -1606,8 +1584,10 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, Optimizer* opt, // Zip thru stack of booleans looking for fields that belong to shellStream. // Those fields are mappings. Mappings that hold a plain field may be used // to distribute. Handle the simple cases only. - BoolExprNodeStack deliverStack; - genDeliverUnmapped(csb, &deliverStack, map, parentStack, shellStream); + BoolExprNodeStack parentStack, deliverStack; + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + parentStack.push(*iter); + genDeliverUnmapped(csb, parentStack, deliverStack, map, stream); // try to optimize MAX and MIN to use an index; for now, optimize // only the simplest case, although it is probably possible @@ -1635,7 +1615,7 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, Optimizer* opt, rse->flags |= RseNode::FLAG_OPT_FIRST_ROWS; } - RecordSource* const nextRsb = Optimizer::compile(tdbb, csb, rse, &deliverStack); + RecordSource* const nextRsb = opt->compile(rse, &deliverStack); // allocate and optimize the record source block @@ -1922,27 +1902,15 @@ bool UnionSourceNode::containsStream(StreamType checkStream) const RecordSource* UnionSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - StreamList keyStreams; - computeDbKeyStreams(keyStreams); - - BoolExprNodeStack conjunctStack; - for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) - conjunctStack.push(iter); - - return generate(tdbb, opt, keyStreams.begin(), keyStreams.getCount(), &conjunctStack, stream); -} - -// Generate an union complex. -RecordSource* UnionSourceNode::generate(thread_db* tdbb, Optimizer* opt, const StreamType* streams, - FB_SIZE_T nstreams, BoolExprNodeStack* parentStack, StreamType shellStream) -{ - SET_TDBB(tdbb); - const auto csb = opt->getCompilerScratch(); HalfStaticArray rsbs; const ULONG baseImpure = csb->allocImpure(FB_ALIGNMENT, 0); + BoolExprNodeStack parentStack; + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + parentStack.push(*iter); + NestConst* ptr = clauses.begin(); NestConst* ptr2 = maps.begin(); @@ -1956,9 +1924,9 @@ RecordSource* UnionSourceNode::generate(thread_db* tdbb, Optimizer* opt, const S // hvlad: don't do it for recursive unions else they will work wrong ! BoolExprNodeStack deliverStack; if (!recursive) - genDeliverUnmapped(csb, &deliverStack, map, parentStack, shellStream); + genDeliverUnmapped(csb, parentStack, deliverStack, map, stream); - rsbs.add(Optimizer::compile(tdbb, csb, rse, &deliverStack)); + rsbs.add(opt->compile(rse, &deliverStack)); // hvlad: activate recursive union itself after processing first (non-recursive) // member to allow recursive members be optimized @@ -1966,17 +1934,20 @@ RecordSource* UnionSourceNode::generate(thread_db* tdbb, Optimizer* opt, const S csb->csb_rpt[stream].activate(); } + StreamList keyStreams; + computeDbKeyStreams(keyStreams); + if (recursive) { fb_assert(rsbs.getCount() == 2 && maps.getCount() == 2); // hvlad: save size of inner impure area and context of mapped record // for recursive processing later return FB_NEW_POOL(*tdbb->getDefaultPool()) RecursiveStream(csb, stream, mapStream, - rsbs[0], rsbs[1], maps[0], maps[1], nstreams, streams, baseImpure); + rsbs[0], rsbs[1], maps[0], maps[1], keyStreams, baseImpure); } return FB_NEW_POOL(*tdbb->getDefaultPool()) Union(csb, stream, clauses.getCount(), rsbs.begin(), - maps.begin(), nstreams, streams); + maps.begin(), keyStreams); } // Identify all of the streams for which a dbkey may need to be carried through a sort. @@ -2351,7 +2322,7 @@ RecordSource* WindowSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /* const auto csb = opt->getCompilerScratch(); return FB_NEW_POOL(*tdbb->getDefaultPool()) WindowedStream(tdbb, opt, - windows, Optimizer::compile(tdbb, csb, rse, NULL)); + windows, opt->compile(rse, NULL)); } bool WindowSourceNode::computable(CompilerScratch* csb, StreamType stream, @@ -2998,7 +2969,6 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr if (opt->isLeftJoin()) { // Push all conjuncts except "missing" ones (e.g. IS NULL) - for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter) conjunctStack.push(iter); } @@ -3009,15 +2979,14 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr conjunctStack.push(iter); } - return Optimizer::compile(tdbb, csb, this, &conjunctStack); + return opt->compile(this, &conjunctStack); } // Push only parent conjuncts to the outer stream - for (auto iter = opt->getConjuncts(false, true); iter.hasData(); ++iter) conjunctStack.push(iter); - return Optimizer::compile(tdbb, csb, this, &conjunctStack); + return opt->compile(this, &conjunctStack); } // Check that all streams in the RseNode have a plan specified for them. @@ -3579,19 +3548,22 @@ static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Form } // Make new boolean nodes from nodes that contain a field from the given shellStream. -// Those fields are references (mappings) to other nodes and are used by aggregates and union rse's. -static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverStack, MapNode* map, - BoolExprNodeStack* parentStack, StreamType shellStream) +// Those fields are references (mappings) to other nodes and are used by aggregates and unions. +static void genDeliverUnmapped(CompilerScratch* csb, + const BoolExprNodeStack& conjunctStack, + BoolExprNodeStack& deliverStack, + MapNode* map, + StreamType shellStream) { MemoryPool& pool = csb->csb_pool; - for (BoolExprNodeStack::iterator stack1(*parentStack); stack1.hasData(); ++stack1) + for (BoolExprNodeStack::const_iterator iter(conjunctStack); iter.hasData(); ++iter) { - BoolExprNode* const boolean = stack1.object(); + const auto boolean = iter.object(); // Handle the "OR" case first - BinaryBoolNode* const binaryNode = nodeAs(boolean); + const auto binaryNode = nodeAs(boolean); if (binaryNode && binaryNode->blrOp == blr_or) { BoolExprNodeStack orgStack, newStack; @@ -3599,17 +3571,17 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS orgStack.push(binaryNode->arg1); orgStack.push(binaryNode->arg2); - genDeliverUnmapped(csb, &newStack, map, &orgStack, shellStream); + genDeliverUnmapped(csb, orgStack, newStack, map, shellStream); if (newStack.getCount() == 2) { - BoolExprNode* const newArg2 = newStack.pop(); - BoolExprNode* const newArg1 = newStack.pop(); + const auto newArg2 = newStack.pop(); + const auto newArg1 = newStack.pop(); - BinaryBoolNode* const newBinaryNode = + const auto newBinaryNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or, newArg1, newArg2); - deliverStack->push(newBinaryNode); + deliverStack.push(newBinaryNode); } else { @@ -3622,8 +3594,8 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS // Reduce to simple comparisons - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - MissingBoolNode* const missingNode = nodeAs(boolean); + const auto cmpNode = nodeAs(boolean); + const auto missingNode = nodeAs(boolean); HalfStaticArray children; if (cmpNode && @@ -3646,7 +3618,7 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS for (indexArg = 0; (indexArg < children.getCount()) && !mappingFound; ++indexArg) { - FieldNode* fieldNode = nodeAs(children[indexArg]); + const auto fieldNode = nodeAs(children[indexArg]); if (fieldNode && fieldNode->fieldStream == shellStream) mappingFound = true; @@ -3657,12 +3629,12 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS // Create new node and assign the correct existing arguments - BoolExprNode* deliverNode = NULL; + AutoPtr deliverNode; HalfStaticArray newChildren; if (cmpNode) { - ComparativeBoolNode* const newCmpNode = + const auto newCmpNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, cmpNode->blrOp); newChildren.add(newCmpNode->arg1.getAddress()); @@ -3672,7 +3644,7 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS } else if (missingNode) { - MissingBoolNode* const newMissingNode = FB_NEW_POOL(pool) MissingBoolNode(pool); + const auto newMissingNode = FB_NEW_POOL(pool) MissingBoolNode(pool); newChildren.add(newMissingNode->arg.getAddress()); @@ -3691,11 +3663,11 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS // forget to leave aggregate-functions alone in case of aggregate rse). // Because this is only to help using an index we keep it simple. - FieldNode* fieldNode = nodeAs(children[indexArg]); + const auto fieldNode = nodeAs(children[indexArg]); if (fieldNode && fieldNode->fieldStream == shellStream) { - const USHORT fieldId = fieldNode->fieldId; + const auto fieldId = fieldNode->fieldId; if (fieldId >= map->sourceList.getCount()) okNode = false; @@ -3703,7 +3675,7 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS { // Check also the expression inside the map, because aggregate // functions aren't allowed to be delivered to the WHERE clause. - ValueExprNode* value = map->sourceList[fieldId]; + const auto value = map->sourceList[fieldId]; okNode = value->unmappable(map, shellStream); if (okNode) @@ -3717,10 +3689,8 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS } } - if (!okNode) - delete deliverNode; - else - deliverStack->push(deliverNode); + if (okNode) + deliverStack.push(deliverNode.release()); } } diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index 25c35253ad..b77f3d0dfe 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -489,9 +489,6 @@ public: virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); -private: - ProcedureScan* generate(thread_db* tdbb, Optimizer* opt); - public: QualifiedName dsqlName; Firebird::string alias; @@ -574,9 +571,6 @@ public: private: void genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map); - RecordSource* generate(thread_db* tdbb, Optimizer* opt, BoolExprNodeStack* parentStack, - StreamType shellStream); - public: NestConst dsqlGroup; NestConst dsqlRse; @@ -637,10 +631,6 @@ public: virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); -private: - RecordSource* generate(thread_db* tdbb, Optimizer* opt, const StreamType* streams, - FB_SIZE_T nstreams, BoolExprNodeStack* parentStack, StreamType shellStream); - public: RecSourceListNode* dsqlClauses; RseNode* dsqlParentRse; diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 879c4e3960..57c40a941e 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -596,6 +596,32 @@ Optimizer::~Optimizer() // Compile and optimize a record selection expression into a set of record source blocks // +RecordSource* Optimizer::compile(RseNode* rse, BoolExprNodeStack* parentStack) +{ + Optimizer subOpt(tdbb, csb, rse); + const auto rsb = subOpt.compile(parentStack); + + if (parentStack) + { + // If any parent conjunct was utilized, update our copy of its flags + + for (auto subIter = subOpt.getParentConjuncts(); subIter.hasData(); ++subIter) + { + for (auto selfIter = getConjuncts(); selfIter.hasData(); ++selfIter) + { + if (*selfIter == *subIter) + { + fb_assert(!(selfIter & (CONJUNCT_USED | CONJUNCT_MATCHED))); + selfIter |= (subIter & (CONJUNCT_USED | CONJUNCT_MATCHED)); + break; + } + } + } + } + + return rsb; +} + RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) { // If there is a boolean, there is some work to be done. First, @@ -611,16 +637,16 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) SortNode* project = rse->rse_projection; SortNode* aggregate = rse->rse_aggregate; - BoolExprNodeStack conjunct_stack; - unsigned conjunct_count = 0; + BoolExprNodeStack conjunctStack; + unsigned conjunctCount = 0; // put any additional booleans on the conjunct stack, and see if we // can generate additional booleans by associativity--this will help // to utilize indices that we might not have noticed if (rse->rse_boolean) - conjunct_count = decompose(rse->rse_boolean, conjunct_stack); + conjunctCount = decompose(rse->rse_boolean, conjunctStack); - conjunct_count += distributeEqualities(conjunct_stack, conjunct_count); + conjunctCount += distributeEqualities(conjunctStack, conjunctCount); // AB: If we have limit our retrieval with FIRST / SKIP syntax then // we may not deliver above conditions (from higher rse's) to this @@ -629,8 +655,8 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) parentStack = nullptr; // Set base-point before the parent/distributed nodes begin. - const unsigned base_count = conjunct_count; - baseConjuncts = base_count; + const unsigned baseCount = conjunctCount; + baseConjuncts = baseCount; // AB: Add parent conjunctions to conjunct_stack, keep in mind // the outer-streams! For outer streams put missing (IS NULL) @@ -646,13 +672,13 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) // allowed = booleans that can never evaluate to NULL/Unknown or turn // NULL/Unknown into a True or False. - unsigned parent_count = 0, distributed_count = 0; - BoolExprNodeStack missing_stack; + unsigned parentCount = 0, distributedCount = 0; + BoolExprNodeStack missingStack; if (parentStack) { for (BoolExprNodeStack::iterator iter(*parentStack); - iter.hasData() && conjunct_count < MAX_CONJUNCTS; ++iter) + iter.hasData() && conjunctCount < MAX_CONJUNCTS; ++iter) { BoolExprNode* const node = iter.object(); @@ -661,30 +687,30 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) // parent missing conjunctions shouldn't be // distributed to FULL OUTER JOIN streams at all if (!isFullJoin()) - missing_stack.push(node); + missingStack.push(node); } else { - conjunct_stack.push(node); - conjunct_count++; - parent_count++; + conjunctStack.push(node); + conjunctCount++; + parentCount++; } } // We've now merged parent, try again to make more conjunctions. - distributed_count = distributeEqualities(conjunct_stack, conjunct_count); - conjunct_count += distributed_count; + distributedCount = distributeEqualities(conjunctStack, conjunctCount); + conjunctCount += distributedCount; } // The newly created conjunctions belong to the base conjunctions. // After them are starting the parent conjunctions. - baseParentConjuncts = baseConjuncts + distributed_count; + baseParentConjuncts = baseConjuncts + distributedCount; // Set base-point before the parent IS NULL nodes begin - baseMissingConjuncts = conjunct_count; + baseMissingConjuncts = conjunctCount; // Check if size of optimizer block exceeded. - if (conjunct_count > MAX_CONJUNCTS) + if (conjunctCount > MAX_CONJUNCTS) { ERR_post(Arg::Gds(isc_optimizer_blk_exc)); // Msg442: size of optimizer block exceeded @@ -693,29 +719,29 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) // Put conjunctions in opt structure. // Note that it's a stack and we get the nodes in reversed order from the stack. - conjuncts.grow(conjunct_count); + conjuncts.grow(conjunctCount); int nodeBase = -1, j = -1; - for (unsigned i = conjunct_count; i > 0; i--, j--) + for (unsigned i = conjunctCount; i > 0; i--, j--) { - BoolExprNode* const node = conjunct_stack.pop(); + BoolExprNode* const node = conjunctStack.pop(); - if (i == base_count) + if (i == baseCount) { // The base conjunctions - j = base_count - 1; + j = baseCount - 1; nodeBase = 0; } - else if (i == conjunct_count - distributed_count) + else if (i == conjunctCount - distributedCount) { // The parent conjunctions - j = parent_count - 1; + j = parentCount - 1; nodeBase = baseParentConjuncts; } - else if (i == conjunct_count) + else if (i == conjunctCount) { // The new conjunctions created by "distribution" from the stack - j = distributed_count - 1; + j = distributedCount - 1; nodeBase = baseConjuncts; } @@ -724,14 +750,14 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) } // Put the parent missing nodes on the stack - for (BoolExprNodeStack::iterator iter(missing_stack); - iter.hasData() && conjunct_count < MAX_CONJUNCTS; ++iter) + for (BoolExprNodeStack::iterator iter(missingStack); + iter.hasData() && conjunctCount < MAX_CONJUNCTS; ++iter) { BoolExprNode* const node = iter.object(); - conjuncts.grow(conjunct_count + 1); - conjuncts[conjunct_count].node = node; - conjunct_count++; + conjuncts.grow(conjunctCount + 1); + conjuncts[conjunctCount].node = node; + conjunctCount++; } // Clear the csb_active flag of all streams in the RseNode diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index a82fd91236..c516f78f1e 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -324,6 +324,14 @@ public: return ConjunctIterator(begin, end); } + ConjunctIterator getParentConjuncts() + { + const auto begin = conjuncts.begin() + baseParentConjuncts; + const auto end = conjuncts.end(); + + return ConjunctIterator(begin, end); + } + ConjunctIterator getConjuncts(bool inner = false, bool outer = false) { const auto begin = conjuncts.begin() + (outer ? baseParentConjuncts : 0); @@ -357,16 +365,14 @@ public: } } - static RecordSource* compile(thread_db* tdbb, - CompilerScratch* csb, - RseNode* rse, - BoolExprNodeStack* parentStack = nullptr) + static RecordSource* compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse) { - return Optimizer(tdbb, csb, rse).compile(parentStack); + return Optimizer(tdbb, csb, rse).compile(nullptr); } ~Optimizer(); + RecordSource* compile(RseNode* rse, BoolExprNodeStack* parentStack); void compileRelation(StreamType stream); void generateAggregateDistincts(MapNode* map); RecordSource* generateRetrieval(StreamType stream, diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index fc577c3ef0..7fcd8acf1e 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -1191,7 +1191,7 @@ namespace Jrd public: Union(CompilerScratch* csb, StreamType stream, FB_SIZE_T argCount, RecordSource* const* args, NestConst* maps, - FB_SIZE_T streamCount, const StreamType* streams); + const StreamList& streams); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -1231,7 +1231,7 @@ namespace Jrd RecursiveStream(CompilerScratch* csb, StreamType stream, StreamType mapStream, RecordSource* root, RecordSource* inner, const MapNode* rootMap, const MapNode* innerMap, - FB_SIZE_T streamCount, const StreamType* innerStreams, + const StreamList& innerStreams, ULONG saveOffset); void open(thread_db* tdbb) const override; diff --git a/src/jrd/recsrc/RecursiveStream.cpp b/src/jrd/recsrc/RecursiveStream.cpp index 76979db678..837b43b419 100644 --- a/src/jrd/recsrc/RecursiveStream.cpp +++ b/src/jrd/recsrc/RecursiveStream.cpp @@ -39,13 +39,13 @@ using namespace Jrd; RecursiveStream::RecursiveStream(CompilerScratch* csb, StreamType stream, StreamType mapStream, RecordSource* root, RecordSource* inner, const MapNode* rootMap, const MapNode* innerMap, - FB_SIZE_T streamCount, const StreamType* innerStreams, + const StreamList& innerStreams, ULONG saveOffset) : RecordStream(csb, stream), m_mapStream(mapStream), m_root(root), m_inner(inner), m_rootMap(rootMap), m_innerMap(innerMap), - m_innerStreams(csb->csb_pool), + m_innerStreams(csb->csb_pool, innerStreams), m_saveOffset(saveOffset) { fb_assert(m_root && m_inner && m_rootMap && m_innerMap); @@ -54,11 +54,6 @@ RecursiveStream::RecursiveStream(CompilerScratch* csb, StreamType stream, Stream m_cardinality = root->getCardinality() * inner->getCardinality(); m_saveSize = csb->csb_impure - saveOffset; - m_innerStreams.resize(streamCount); - - for (FB_SIZE_T i = 0; i < streamCount; i++) - m_innerStreams[i] = innerStreams[i]; - // To make aggregates, unions and nested recursions inside the inner stream work correctly, // we need to add all the child streams as well. See CORE-3683 for the test case. m_inner->findUsedStreams(m_innerStreams, true); diff --git a/src/jrd/recsrc/Union.cpp b/src/jrd/recsrc/Union.cpp index 94f9beb544..6ad1f3bb39 100644 --- a/src/jrd/recsrc/Union.cpp +++ b/src/jrd/recsrc/Union.cpp @@ -35,9 +35,9 @@ using namespace Jrd; Union::Union(CompilerScratch* csb, StreamType stream, FB_SIZE_T argCount, RecordSource* const* args, NestConst* maps, - FB_SIZE_T streamCount, const StreamType* streams) + const StreamList& streams) : RecordStream(csb, stream), m_args(csb->csb_pool), m_maps(csb->csb_pool), - m_streams(csb->csb_pool) + m_streams(csb->csb_pool, streams) { fb_assert(argCount); @@ -55,11 +55,6 @@ Union::Union(CompilerScratch* csb, StreamType stream, for (FB_SIZE_T i = 0; i < argCount; i++) m_maps[i] = maps[i]; - - m_streams.resize(streamCount); - - for (FB_SIZE_T i = 0; i < streamCount; i++) - m_streams[i] = streams[i]; } void Union::open(thread_db* tdbb) const From aa13dff750246e4146344e6ab44df77ecca0b17f Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 1 Apr 2022 15:52:56 +0300 Subject: [PATCH 145/187] Invert flags order to match generateRetrieval() --- src/jrd/RecordSourceNodes.cpp | 4 ++-- src/jrd/optimizer/Optimizer.cpp | 4 ++-- src/jrd/optimizer/Optimizer.h | 2 +- src/jrd/optimizer/Retrieval.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 345d63f9b5..05372e42cd 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -2969,7 +2969,7 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr if (opt->isLeftJoin()) { // Push all conjuncts except "missing" ones (e.g. IS NULL) - for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter) + for (auto iter = opt->getConjuncts(false, true); iter.hasData(); ++iter) conjunctStack.push(iter); } } @@ -2983,7 +2983,7 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr } // Push only parent conjuncts to the outer stream - for (auto iter = opt->getConjuncts(false, true); iter.hasData(); ++iter) + for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter) conjunctStack.push(iter); return opt->compile(this, &conjunctStack); diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 57c40a941e..5b8efa7303 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -804,7 +804,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) // Apply local booleans, if any. Note that it's done // only for inner joins and outer streams of left joins. - auto iter = getConjuncts(false, !isInnerJoin()); + auto iter = getConjuncts(!isInnerJoin(), false); rsb = applyLocalBoolean(rsb, localStreams, iter); } @@ -2786,7 +2786,7 @@ RecordSource* Optimizer::generateRetrieval(StreamType stream, BoolExprNode* boolean = nullptr; double filterSelectivity = MAXIMUM_SELECTIVITY; - for (auto iter = getConjuncts(innerFlag, outerFlag); iter.hasData(); ++iter) + for (auto iter = getConjuncts(outerFlag, innerFlag); iter.hasData(); ++iter) { if (!(iter & CONJUNCT_USED) && !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index c516f78f1e..3ceb20c135 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -332,7 +332,7 @@ public: return ConjunctIterator(begin, end); } - ConjunctIterator getConjuncts(bool inner = false, bool outer = false) + ConjunctIterator getConjuncts(bool outer = false, bool inner = false) { const auto begin = conjuncts.begin() + (outer ? baseParentConjuncts : 0); const auto end = inner ? begin + baseMissingConjuncts : conjuncts.end(); diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 2df0b3e71c..1755e8f1c4 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -262,7 +262,7 @@ InversionCandidate* Retrieval::getInversion() if (finalCandidate) return finalCandidate; - auto iter = optimizer->getConjuncts(innerFlag, outerFlag); + auto iter = optimizer->getConjuncts(outerFlag, innerFlag); InversionCandidate* invCandidate = nullptr; From 5c93044184a6e5deec11296c03d002a7ec3490ed Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 1 Apr 2022 15:54:24 +0300 Subject: [PATCH 146/187] Fixed cardinality for full outer joins --- src/jrd/recsrc/FullOuterJoin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index 3725560906..d7bcbe9e55 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -43,7 +43,7 @@ FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSou fb_assert(m_arg1 && m_arg2); m_impure = csb->allocImpure(); - m_cardinality = arg1->getCardinality() * arg2->getCardinality(); + m_cardinality = arg1->getCardinality() + arg2->getCardinality(); } void FullOuterJoin::open(thread_db* tdbb) const From e4a7614aeb61c1568e3421fcea1c9cb204f599d1 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 2 Apr 2022 00:07:13 +0000 Subject: [PATCH 147/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index e7a55b3bd6..8b12e50288 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:448 + FORMAL BUILD NUMBER:451 */ -#define PRODUCT_VER_STRING "5.0.0.448" -#define FILE_VER_STRING "WI-T5.0.0.448" -#define LICENSE_VER_STRING "WI-T5.0.0.448" -#define FILE_VER_NUMBER 5, 0, 0, 448 +#define PRODUCT_VER_STRING "5.0.0.451" +#define FILE_VER_STRING "WI-T5.0.0.451" +#define LICENSE_VER_STRING "WI-T5.0.0.451" +#define FILE_VER_NUMBER 5, 0, 0, 451 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "448" +#define FB_BUILD_NO "451" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 13d3422935..10248ef714 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=448 +BuildNum=451 NowAt=`pwd` cd `dirname $0` From f023ba995719bc53e7ede6786cee9424ea59bb51 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sat, 2 Apr 2022 09:05:10 +0300 Subject: [PATCH 148/187] Enable merge joins. Attempted a cost-based choice between hash and merge joins. --- src/jrd/optimizer/Optimizer.cpp | 143 +++++++++++++++++--------------- src/jrd/recsrc/HashJoin.cpp | 9 ++ src/jrd/recsrc/RecordSource.h | 2 + 3 files changed, 89 insertions(+), 65 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 5b8efa7303..10d8760b08 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -294,8 +294,8 @@ namespace if (node1 == node2) return true; - const FieldNode* fieldNode1 = nodeAs(node1); - const FieldNode* fieldNode2 = nodeAs(node2); + const auto fieldNode1 = nodeAs(node1); + const auto fieldNode2 = nodeAs(node2); if (fieldNode1 && fieldNode2) { @@ -1654,7 +1654,6 @@ void Optimizer::checkSorts() sort = rse->rse_sorted = nullptr; } } - } // Examine the ORDER BY and DISTINCT clauses; if all the fields in the @@ -2343,102 +2342,116 @@ bool Optimizer::generateEquiJoin(RiverList& orgRivers) if (!riverCount) return false; - HalfStaticArray rsbs; - HalfStaticArray keys; - - bool useMergeJoin = false; - - // AB: Get the lowest river position from the rivers that are merged + // Prepare rivers for joining StreamList streams; RiverList rivers; - unsigned number = 0, lowestPosition = MAX_ULONG; + HalfStaticArray keys; + unsigned position = 0, maxCardinalityPosition = 0, lowestPosition = MAX_ULONG; + double maxCardinality1 = 0, maxCardinality2 = 0; - for (River** iter = orgRivers.begin(); iter < orgRivers.end(); number++) + for (auto iter = orgRivers.begin(); iter < orgRivers.end(); position++) { - River* const river = *iter; - - if (!(TEST_DEP_BIT(selected_rivers, number))) + if (!(TEST_DEP_BIT(selected_rivers, position))) { iter++; continue; } - if (number < lowestPosition) - lowestPosition = number; + const auto river = *iter; + + // Get the lowest river position + + if (position < lowestPosition) + lowestPosition = position; + + // Find position of the river with maximum cardinality + + const auto rsb = river->getRecordSource(); + const auto cardinality = rsb->getCardinality(); + + if (cardinality > maxCardinality1) + { + maxCardinality2 = maxCardinality1; + maxCardinality1 = cardinality; + maxCardinalityPosition = rivers.getCount(); + } + else if (cardinality > maxCardinality2) + maxCardinality2 = cardinality; streams.join(river->getStreams()); rivers.add(river); orgRivers.remove(iter); - auto rsb = river->getRecordSource(); + // Collect keys to join on - // Collect RSBs and keys to join + keys.add(FB_NEW_POOL(getPool()) NestValueArray(getPool())); - const auto key = FB_NEW_POOL(getPool()) SortNode(getPool()); - - if (useMergeJoin) - { - ValueExprNode*** selected_class; - - for (selected_class = selected_classes.begin(); - selected_class != selected_classes.end(); ++selected_class) - { - key->direction.add(ORDER_ASC); // Ascending sort - key->nullOrder.add(NULLS_DEFAULT); // Default nulls placement - key->expressions.add((*selected_class)[number]); - } - - rsb = generateSort(river->getStreams(), nullptr, rsb, key, favorFirstRows(), false); - } - else - { - ValueExprNode*** selected_class; - - for (selected_class = selected_classes.begin(); - selected_class != selected_classes.end(); ++selected_class) - { - key->expressions.add((*selected_class)[number]); - } - } - - // It seems that rivers are already sorted by their cardinality. - // For a hash join, we need to choose the smallest ones as inner sub-streams, - // hence we reverse the order when storing them in the temporary arrays. - - if (useMergeJoin) - { - rsbs.add(rsb); - keys.add(&key->expressions); - } - else - { - rsbs.insert(0, rsb); - keys.insert(0, &key->expressions); - } + for (const auto eq_class : selected_classes) + keys.back()->add(eq_class[position]); } - fb_assert(rsbs.getCount() == keys.getCount()); + const bool hashOverflow = (maxCardinality2 > HashJoin::maxCapacity()); + + // If any of to-be-hashed rivers is too large to be hashed efficiently, + // then prefer a merge join instead of a hash join. + + const bool useMergeJoin = hashOverflow; // Build a join stream - RecordSource* rsb = nullptr; + HalfStaticArray rsbs; + RecordSource* finalRsb = nullptr; if (useMergeJoin) { - rsb = FB_NEW_POOL(getPool()) + position = 0; + for (const auto river : rivers) + { + const auto sort = FB_NEW_POOL(getPool()) SortNode(getPool()); + + for (const auto key : *keys[position++]) + { + fb_assert(river->isReferenced(key)); + + sort->direction.add(ORDER_ASC); // ascending sort + sort->nullOrder.add(NULLS_DEFAULT); // default nulls placement + sort->expressions.add(key); + } + + const auto rsb = generateSort(river->getStreams(), nullptr, + river->getRecordSource(), sort, favorFirstRows(), false); + + rsbs.add(rsb); + } + + finalRsb = FB_NEW_POOL(getPool()) MergeJoin(csb, rsbs.getCount(), (SortedStream**) rsbs.begin(), keys.begin()); } else { - rsb = FB_NEW_POOL(getPool()) + // Ensure that the largest river is placed at the first position. + // It's important for a hash join to be efficient. + + const auto maxCardinalityRiver = rivers[maxCardinalityPosition]; + rivers[maxCardinalityPosition] = rivers[0]; + rivers[0] = maxCardinalityRiver; + + const auto maxCardinalityKey = keys[maxCardinalityPosition]; + keys[maxCardinalityPosition] = keys[0]; + keys[0] = maxCardinalityKey; + + for (const auto river : rivers) + rsbs.add(river->getRecordSource()); + + finalRsb = FB_NEW_POOL(getPool()) HashJoin(tdbb, csb, rsbs.getCount(), rsbs.begin(), keys.begin()); } // Pick up any boolean that may apply - rsb = applyLocalBoolean(rsb, streams, iter); + finalRsb = applyLocalBoolean(finalRsb, streams, iter); - const auto finalRiver = FB_NEW_POOL(getPool()) River(csb, rsb, rivers); + const auto finalRiver = FB_NEW_POOL(getPool()) River(csb, finalRsb, rivers); orgRivers.insert(lowestPosition, finalRiver); return true; diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index a960489080..5fcee2fa25 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -45,6 +45,15 @@ using namespace Jrd; static const ULONG HASH_SIZE = 1009; static const ULONG BUCKET_PREALLOCATE_SIZE = 32; // 256 bytes per slot +unsigned HashJoin::maxCapacity() +{ + // Binary search across 1000 collisions is computationally similar to + // linear searc across 10 collisions. We use this number as a rough + // estimation of whether the lookup performance is likely to be acceptable. + return HASH_SIZE * 1000; +} + + class HashJoin::HashTable : public PermanentStorage { class CollisionList diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 7fcd8acf1e..61a67342ee 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -1092,6 +1092,8 @@ namespace Jrd void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; + static unsigned maxCapacity(); + private: ULONG computeHash(thread_db* tdbb, Request* request, const SubStream& sub, UCHAR* buffer) const; From f179eb489ea60217910cf15a1832a34060c71318 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sat, 2 Apr 2022 09:10:48 +0300 Subject: [PATCH 149/187] Make plans consistent between debug and release builds --- src/jrd/recsrc/RecordSource.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index eec46ca288..67c68191f2 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -39,9 +39,8 @@ using namespace Firebird; using namespace Jrd; -#ifdef DEV_BUILD -#define PRINT_OPT_INFO // print optimizer info (cardinality, cost) in plans -#endif +// Disabled so far, should be uncommented for debugging/testing +//#define PRINT_OPT_INFO // print optimizer info (cardinality, cost) in plans // Record source class From 747d2ed2c2724e95513835ad3c2a583d6288f900 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sat, 2 Apr 2022 11:38:26 +0300 Subject: [PATCH 150/187] Fixed #7164: Multi-way hash/merge joins are impossible for expression-based keys --- src/jrd/optimizer/Optimizer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 10d8760b08..aa1f55c31f 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -2289,8 +2289,8 @@ bool Optimizer::generateEquiJoin(RiverList& orgRivers) { for (eq_class = classes; eq_class < last_class; eq_class += orgCount) { - if (fieldEqual(node1, classes[i]) || - fieldEqual(node2, classes[j])) + if (node1->sameAs(classes[i], false) || + node2->sameAs(classes[j], false)) { break; } From 4e813e0e37c8297a4710d06175857d96209287b6 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sat, 2 Apr 2022 13:14:19 +0300 Subject: [PATCH 151/187] Fixed regression in outer joins --- src/jrd/optimizer/Optimizer.cpp | 10 ++++++---- src/jrd/optimizer/Optimizer.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index aa1f55c31f..93045db59b 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -596,14 +596,16 @@ Optimizer::~Optimizer() // Compile and optimize a record selection expression into a set of record source blocks // -RecordSource* Optimizer::compile(RseNode* rse, BoolExprNodeStack* parentStack) +RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack) { - Optimizer subOpt(tdbb, csb, rse); + Optimizer subOpt(tdbb, csb, subRse); const auto rsb = subOpt.compile(parentStack); - if (parentStack) + if (parentStack && subOpt.isInnerJoin()) { - // If any parent conjunct was utilized, update our copy of its flags + // If any parent conjunct was utilized, update our copy of its flags. + // Currently used for inner joins only, although could also be applied + // to conjuncts utilized for outer streams of outer joins. for (auto subIter = subOpt.getParentConjuncts(); subIter.hasData(); ++subIter) { diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index 3ceb20c135..a7e44db850 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -372,7 +372,7 @@ public: ~Optimizer(); - RecordSource* compile(RseNode* rse, BoolExprNodeStack* parentStack); + RecordSource* compile(RseNode* subRse, BoolExprNodeStack* parentStack); void compileRelation(StreamType stream); void generateAggregateDistincts(MapNode* map); RecordSource* generateRetrieval(StreamType stream, From 2e8bfcb1b080a06ce06b1bc338e7d873dcbd7c65 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sun, 3 Apr 2022 00:06:43 +0000 Subject: [PATCH 152/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 8b12e50288..629631d11c 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:451 + FORMAL BUILD NUMBER:455 */ -#define PRODUCT_VER_STRING "5.0.0.451" -#define FILE_VER_STRING "WI-T5.0.0.451" -#define LICENSE_VER_STRING "WI-T5.0.0.451" -#define FILE_VER_NUMBER 5, 0, 0, 451 +#define PRODUCT_VER_STRING "5.0.0.455" +#define FILE_VER_STRING "WI-T5.0.0.455" +#define LICENSE_VER_STRING "WI-T5.0.0.455" +#define FILE_VER_NUMBER 5, 0, 0, 455 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "451" +#define FB_BUILD_NO "455" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 10248ef714..ab08ba1dbb 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=451 +BuildNum=455 NowAt=`pwd` cd `dirname $0` From d2adb6e3d2f51084266ffb736f0f63be3ab7b6f0 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sun, 3 Apr 2022 10:12:35 +0300 Subject: [PATCH 153/187] This should fix crashes surfaced by my yesterday's commit --- src/dsql/ExprNodes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index cc030621e6..a990e1488d 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -260,7 +260,7 @@ bool ExprNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other bool ExprNode::sameAs(const ExprNode* other, bool ignoreStreams) const { - if (other->getType() != getType()) + if (!other || other->getType() != getType()) return false; NodeRefsHolder thisHolder; From 0a4e1f2229146bb31c1edb783a076e714b61a6c9 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Sun, 3 Apr 2022 14:01:03 +0300 Subject: [PATCH 154/187] Fixed #7161 : Update zlib to 1.2.12 --- extern/zlib/Readme.txt | 2 +- extern/zlib/zlib.exe | Bin 287760 -> 301177 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/zlib/Readme.txt b/extern/zlib/Readme.txt index 16f388c16e..6ba7036a7a 100644 --- a/extern/zlib/Readme.txt +++ b/extern/zlib/Readme.txt @@ -4,7 +4,7 @@ architectures. The source code of zlib library was downloaded from - http://zlib.net/zlib1211.zip + http://zlib.net/zlib1212.zip It was built with MSVC17 compilers using commands specified at win32/Makefile.msc: diff --git a/extern/zlib/zlib.exe b/extern/zlib/zlib.exe index cfe5367e63d422a2d6433b299e10c9bce0365c0f..d0d734e26e8a535f82f967af12eca686cb46913a 100644 GIT binary patch delta 196154 zcmagH3tW^%_Xobu!@@4G+?1P&a#c}LQ4mqQ;AIh0a1jMj0nJND7tMCjNsPc8k_?+DVSyTnJR-`rJ5?as#K>Qo9hIsnpA@&PsvTC+9In|^$zHX6^Q#HUWrhZ?1&?(c4T*k zs!H~tG|1cN?xNaAS!-NWo5R>Sd5S9v#>>xzckeRDxM-yjS${Dn$t|qA+{;_F2r#0) z7~wXSB8R$%xu{eCsP3@G)| ztTMOilylZJ&Yw+@REn>8QM)ScB!8<-rLvni{|qh3QPQq0w0MDn>SRq-Pzeftgo0~Z zn*qs;yO5P>4~Thpyy^qPA!MoCRjSdG(9G9XhEGs+uePR@->H7p5H>-@=LV=$*0f!g z-1Z6Oxqp`D{=ugRNYx2_yTmUPODaTUdjPK-uQFKEF7l<6J+lf?WVrAJ;K6R9xXg$8 zvQN|tJ$`YJ~=dZ@&^eKMKLw6>Pswa;rRe70&JbR z1v##~Y=BBtT5almI3BG)4-EfZrLsjyD%If-2cQoCh0+|`pXkoQU>@Xyp0Vh8QVoi` z89kLcevQ?+M#A4F(x*)07XP5PPEw7?-DpiO@Yz~jp0d!YFE{WgV%j=`*gFHU zu%^|6ga;X-8cOEJ_k*{NqYI?|5o@N^~31Kg$N-DI)YE1=A zAp3Hz>F8^3?(fB#G=sxtx)J%~g0snp6DMbNotzwJgS83acGtT|eHe6H{A)}tYkCy=LgJ@Hd?Mm9KPut{h`XFfnJ$&*Wm@v)mFF!i&s)S_6M07Dsd)v( zH?5fkjexW(FBj>Jj-(rZQY5!Ik{Vu!B$P_|&fS2N?|fbS?!j-Z+djdadYcHedVcLnO$E&h=?i_YZ*R^LkG)^`jKL;A@xUQ_)c%{&{V5NW!nj^DuEKqZR5Pat_ zZ?CcX%@~s`{xXEx*`qU+R`W=7lvd%#8Lo1_|xzTNmhc7XxCWUvP`0jAtcx@{a- z=x^2YOatFGn?3Ipo!pF!e5Gd2$r}6PI^Lfs)>O4ow@1%Lw<~&WW>&UkQQK>)p#JzA zGzwZJ?C?C0h3J0q6Z_k%hkWB_7UUi3o(O&c-1HM0>m4<)5tPvoZ}Ug(ckFiAl_eb4Z4QiHJ)MyY1c2`wSMcoU-et<~jD^{21Hk6T;Gcd?Gb=XmtMUy_wHY zB0sODDu#*|&thwQ21t`oB5FwlWwrbU3c*e!aL1upRo5p*X(gj3i`${!a+DK7h9?k# zo7s*=Krm^kv8D&}KoqbS>G&6fn|I%0GmDCRnvFUm*c@MXk4=P<50t!$oztZ5;e~g} zqiP~D3lMt(&!CK)yc))Q`*;}kk0&9qr>j}cn$VK5NsTLHrRG?ZY~Zy}=E_YaMO{{% zWAq`AJt3RNf=Mcnz|SB*$1=u}r_G;}S7Ux8O0Bg$a@%fq#Qt=~bhcTYu}g^A&?1#> z>P%UcsmzVi&dgGN!DKv{t>{%@Q(2zY;6unRL;vtOQk^Y(#>T|A}0x3grGI<`<% z=wUbM$|nxvDVe-~7F*QyO5g#I`tPj4Gub@<#J;I$9GExxZ_s-dq}GpDmFqpNdQSsy zMVZ_hyD>^_4_LUL9rfQOtKU5^Kfpzn4g1;RpaltQfNiMNYyZ{6 zz`w)dC>R_*aA-ZG+hCZUVX&LD_Akn+G2Kj|_J9U}A@sBFt;m?JR&l?{Y*Tnl|BlK9 z?5k8+rTADTsTZ50#*tW`B)Q%!`cluD!>4I>Q@l*iMn;Sug88R1y4uSB#DIK)pEaXP z1;}%HhCSdW>dncivHgUMswyhgMbG{dkt4-3O=Ohh!(t-aO_$eUKYbFc zvc2(>immNCR&$N;ZjNQOedkL#tY37W*srd@u#;MRXik$!b^>V(E94z*?IfX7Rdrd5 zqRC@fN%V7?J`~?Oh6VL|S~HU3ea5iu{g!CbDgNGQ=H5T1$IROQVgudwAmuL|%?!x* z{y*}qlpj5sRrIfx4~}9v1A3}^U$NWSQv-U_n7%R~Nb_$(QH^3p2Rx@aLGgkluoTBO z#1v59U5n{0pRQy*2KLr8Qtrx;Y|_9IdEa}iZeXT-ypr`C)J?9dWQl{qhaJRtC~f9D zc4G)c)}Q{ev+My?LOim3w$|7Kjv$`Dp$5uKtD+&StYn)8^;)=3puoJUA>iK5RG|vV zU7g9QYs5lxB3Zn_q^C-uv&=W`M$f&zMopz;^MR(sAV$IDFReCehF07D!7Rwk25cwu z9XwBNsAS6rPnYJhPX`;OYM%kM*M<|d#LTR?3(D0@ufvzOzB_w@bQZg;RZ3+%lDlF%?6#Mqy4b7>?q;^TU+0%;UB z$)43URr9EqM#WJtrBW}g)+$%gDPOWT&n80yEaMHvZb*)DB~$k`isri!RuU zAr+dEZ66*l-DYQoKTvW8q=@ys7ey1>_=Q_`J0{8dV%1jj%>;)C9|stwhLOf%-C*bB zQm)d>v7@atb0+@a7z&y_JfvMztyAm&2hjRv=3QXGc)56G5#6;}&4a8;i%1YLM zM3j8xTV_D;=_OV)B7Ei#U`a{i)=82@-)hx27%aKXd{_g3HIpVxNLt;t{EU*@W;ZqP z?HH6S&hR@Tw>2lnXG?X9dlok%oinuh{sn~nHKMnCu#$z043m#kvN0oj^)`Y_p?yu> zol7$5tgu1}_6UTjoqs==tsObM_p<+g%x4C(??&zkd4w?2e3hB)SnU0vpHF_GBMak| zy8w>Mt7jvnWG3VQk`r#Q)Lh ze|-g={*7$Ez?MuLET6f+UYPizylg)kt{)$^OxSsr6s;-nm1wlNK=mayaC(r{8upWJd7#TAz0}PuLdM{<#a(bY zmf7`-f;IozrkZZx$tbrhb+z^)_2JXKWcF9;fPti&160_l48Za%75iGtnxTS~QwBbz z9vv`*y6MhYK)kTyVQG_tJoW=l%gBw-*khC9ysvzYOih*{b0*PY?@WHIOd5p^VfO%1y%CVYJGdGLQ zGQxaq?J!?km0chKW)gMp})%cl5jLu_>Vu?Vui4{fV!t zDvaoz>p_wkL%VYjq`uq2-kH)5KR@_PDk1u{R|0KDb7-zl}w)|>d zjTIUSc98*+fqt5SfR;*dWKFxtFQG?h2dbpqEIVn`@b{5ESwniq9xxTCik_czpw9#U z1*1p|4?^A7V!NqYV_vLQ)#`U8BW^DV*zKr+7J85z0KZ>`y&kb}CGE7L-j-__6RwKBuEj-3+h=jJq^nyZAg#6frZA8}wW*@on@M`U+y&KJ+^CjHDeq(rP3~1u zzREX>#{7_4wb&e{vd7s2uA#q`wuG|l#z9Fdv_;x1EaGmTkpx=PCG0+cauX;?ZN;+E zMw!uAL$DL}681yg)bUa*^Gxd}#js&%Gee?Mjor=M&FGyfY`73bvKxY1x>69$cBF;K zZTs2bv|)1Ves(o&0tE@_{k)n{0aixT7QpwjN79E12o?yP1M*gSsNA%lolc+8i%{cE zCZ>h>Y(17?HwD{G{&tfuR30!d?q_4B#mgXJoVGNm5tuDq3#knzsQgy@6WG`RG2`?`Pf_$rKnehEcFSBff;hyQ~`8-RC`_;9qHo zAd4*d2``JTtO3Q7Un5A3W3bjdq*bkX$X!)C34T#H-@GXAiBu)tN~ophjjq^=x5$bc z1ZAkbeob+(%BX`)t26Mqk&>#Vm$}$qGML;kY*Q)iU1FW4=Hn4nQv5BGbot~i)2d*K zqY26%8@{2C_dr#!YSeMnE#3h5Z}>u~%i<41xUrYe0b3bHe8g3kUC%CxJxZ)7LCS1x z(%dLtQ?N2yhlsz)r}QM)n@;R?DLZfSOg7i5lLAgG|I%DXnu-?pJnHxNyUJ|C^q$?x zJ!@Xw1|6+4PRJ@)113&Pa+i2|H9I!lKWsl-f=XM99Cv*U*&Jxm%n0}p(^q12`2!IW z`)hh1?J^?R3j`xGhnJAXSQVwC36NB*E$RpNMD0=3(MM%ojLK16Xim~ixHf8!HMtmg zDe`hKrHZxrWUj2eB_*rY7>BLBrKYgo+FRZ#%^G9RwYRb<^uvVrfJh|O83UA0;B~4g zbe&aGDC5rqf9hE^CCV}LO?64Qv76bZGWtz6CQLTEPBzB2^r|9CRHTZks8Q<7HdkZF zWMhEYrZ#$%Rj&%Da;1zas+r`jri$vUQ6@&I&GWUYTB&_L)ZREX*n~1t^uAes%Y`fx^;&7( zgMxBgwPl>IWt_idoYqlEhlyW>VOgX2K!K)dm&5KLqSjn(CRC}Soo=va=s*}wJ&UG9 zO7T_FwfW{Oe^p-D$vNPxx?6}vC7ORGxsmR1XeQCNf}zB zW`jK5lA*4#(f+M!3rWlnUs`0Msg2(h4zlF7@%$6efTxr;z7`Rf(aOzCe&7>_y=zTt zMr7^9sX#VA*^wnY!Q}#Wr~_v50J>6V zHK42S?eaaPH}LZ zq7_`H=w`jo-pLB_&ld*!wN8IejV-N-UCIgyc?sJVCCwj$@2br7wfJGfMDjC8tU6t? z)59k^)=ObEw<&smXh2t(&lWA#CO?6-Han7bu6wda%kN%dSF#6;8HrBbP{-F`Y|ztU z9gnx_PaDu!`!bz9Alb3_K8mi?VL|Mb-C@qRaz7uLrO(Pqp#8~7c%}ZUFt*l?!ak*& zF+(6paTf}uOiYVb{tSBRyIGMga}m1a%=LDV(5lSGwEXCI?9rTF@}W!2nxmI%zGDqJ zBjob$n9uCMprzmz&F%(ER5^o1tle(9$>0Bujhda|?rc2_{C~~vFMZCA&F-52!QYTi z;cCy+K5$E&Qx@kZAXd5Oj#@~E59 z+vc$)f@C4ahH@5y-n>rw^VG;fJ|Y!^v&;P$*g$;Bkg%>TjZ+GJ|X=aS4Gd+>RA zRD?|oywQM!^zGq*d%4Mc?&<1m)Ke8qH&Ur&0iG6 z<}Ff}&kq&fGAY z+zE4a#-eOj|8g{Qa+IwZ-A26-W&7C~i?)5?%#OC5=!Bhg#$s%ToVhW!y`8YPSk2<+ z`Xymq z_q}1aC)MN_eJ!)pmSx&nRnrITFyK%AH@yA>_Un@IJ*EKeoHDgi6SR^tSqt@U`-n>w zZS$la5dn7*lyTZxsUA4ydH(qGRasNjwi3?P7e>k+ob4+Nl{J5}&kOsw58-I?84h53 z;qad4en!fz&~DM={7sAft5tuK9J_FPj;dsahiAxxZm~TNkM-aBI!2|BDu)c;GP_Zi z&F8+(Zaw@(N!3rH&&HUGwb0R8lP9$EZU?Nta*B^Y6p*0^)-3i_nTvJkJDpf|J+lsP zT9u6Bkt-4%BcX3GM_`-rtpKbk_EnjD6*m|*I39=R2(XqP9SUU4d=NUOwtWQHK#YjF zSWN?RBE?rSy7Ik%!e}#}&|#g|@yV1t!@D4W`BwR8gnt`#C0z6t3BUe}ecoHM>;v}l zqXCiToAPpvu!>uH1A6WM!u#pb6#4T8Ht4a)-sNxsVw*_bSHqWNsI~Et2ys@7{Y=&0 zH1fXJSBdMqaXNoO+c zRSdsv0C4T5QbM1-7nO9}qLFw8@CpY}#-nesoTcG2A|MWM6Tmxe5Z3`-MH(J(aTTBo zh|!gs5BE^GOE{C?L0r){7s~laYLaTpbAMkx0tF7o9~MsacE00Pc4ld!XKY70=T+vn zEUn~g$O5hg(Ul!{{v2wtn<}~H6@-VD;fJX4o4bg3V@}S1S|zQ)svd`04O|7}4f@~9 z_4^ckwfUy>gik><5l@felhC>ruT(@~l^o&E6Z5OJ*0hZog|ec_%*4$QU%DTlH4dIi zcR#kF0=g!$S%gizT!amLjR=qMl_IR;%XTr{<8l3yL~;v{6JZk{Ai@S7DZ(SXhY0I< zS3>X3%#R1cC;!6Znf<;Sv6*2&}xy~#j8ZPg;$EOiSHF*1K%aWBYc|(>-grEScb7Xb2D}u zI!AzZ@k|kJ;ZsD|#3zWbfhURZ2#*tC9UnkNR{Cg2L@UKL8 zgr5;%9Y3){)L3uqGIW~&?c$q7xP_OCu!*k`VFO<&!Xtc{2H-mHXNf?UDgxic!%TWA$a$@2-7hTc zsem42U}p0rSpUR6j|@yQBC~ncFU;^%z<9DDv-yaQ{LYP?wk0vMpiy9k{7!un__kq^@3ypPJhDFpDPuiH*(|zU8f3bw8A5{Nx181*0o=%nLE28E9 zJY7C?A6W0$ojSLraModY!g*>3&wBzHj=7qwQlv)*D3011lZAbl67vd?{C6IES2|duSX%j4l4+?byfi?#Nq(Y99PN8(k9Vcl9y4Bt_FJmS_YB8htK2cMpv-l=v?iFv#BdjQw4*&}lla z`Nv^Cs%^)wfmLBnH+8}e-0H|(>D%CZC(qSa!iQ znvH&DU^H;!E=)=)j#>VWZ4LwoX{y?>w*szKslk;3+)H`unIBySW5RAW$LcbbZ_(OJ zej69$DzsK9!p}TEMrEF#sKP-%^-1?1*g11-$e}{C-p#zIRb?KvO=TUmt*C3JH9elc z1UTziI!br*4(ZQ}8yfj4D_c}L)o&Wqx+Pz&|F0pURhI+Yfh1;%b4MEO-&$_J}?vwljt-!6;ytyz~<9;}h zzwQYs@DUhZaTiA9`4Q0EG@H9dq+M3lWzK1}+OeOEqS(y%Z~Lgam| zSo}S11{cqRkxU-WsvX4p@(b)qOHOQy3n=S(S#^;Q?hb>0AG&*&#!c7EwX4ARDay}n zExSGjNgJc&aX+y68wbml6YTkoZ^%FY$&#!S<$0xSt<^s!vJ^Ab z;+6x)cbVN%tTuKbhd3_w#lc}-_O;M($_et=t5xi1N!0AW;n8vE3iATBigp7Io_S~z$~sWO__j_I4@<0N8SSkch^qx+#Va}~WcnAFVwt?ySxfUA zI<2A$mG4qtKK>P{Jycjw4m3W5upEba*OSgzOzk*@>xB#)&O_+wl)wv=*;kt~HAYWU1AYt@eI9seXyN`%vWupkRbeR};idP{e z?i2@O$6?9y=YJJpw&X=y@?tD`v6j4e_{yy5qhMFK@~|?PBk8G#yK(;I`Bo_# z{`|DOxen439i-z$po>Dl%S)WS(<+P;Z*p`01i=2*FQDuue(Lz(s4eb8EN2_%9$%Zgx|YRcS)I*328C;NS|^7e*ATi_#%X_M}Tj z4=AO=x%?XlFcd8m)TnLQJc(MOziD1M!^ISU70Sf7m?cd&`-()5!tZ zZ&Y5n$&W!aNLt`W8B#z5h%A*=zNX77pKcc0YYrKdcm=>%`^a_4qgK+)o2esokg(&f z7n|~ePpmB9CotFYJ8es9Zxyle`)B{f|A^IW!wZAt-7DDP7y8L-H?S)&4AP9W05$zH z*8RnG?gta#cdzj&dxpLF;&`{M*q7TDu&Xad_IU(RRIj8R%r5ld8{!;jDlG31r*PO< zRO%`lxnsS&%*<+b43WM3*!3OZy?dD)o-aT0e9h8YX6b5G>W^@ePC5f;h`99weLCu; zDCsjc|D{;<@9<18X{W!mmi_ys2>%pZnk(OV6vB<`X6O}}zq*Gty);Y?aA!d~he_YF zw4Djv-q3=N6#w#EA8VSA^0fj}JpfB~mmzG&&QP7MKXTE%r@1nK)$R=Iy1g8?wDQ$H zrGE;#jeNpN_V>;RIoyZ!dU^jmpC{4Qvpf`P1xIeERL!4Sj_$|F4o>C_JQRIyE}r3n z95)lX>|r^+sRn8wPPH<$4}x;dcxsT=7)ve`M&Bw*u~bg-TVzq z6wv5&5^4EU$Qg;!diKt)zyv=S19Yyae%fwNre;|6)ds#nIIngq^%>w9th)35f=)H! zo_xXM?DnpVIA5@1H|^$H5jJy`2v75y@c5XI`*!f@Fh$7{we4diuk;&!kUT;tw@-x4 ze76Ws^Bo9L2t|`>(87*#;&j@U=E^R;64?DAXW4vb*=%RoY0H?;tKA-oM>0oAnPtA| zVp>Se`r}>E*s_VZaf(r(^Q~c1u_5C<;T8rHN?X#O#-6-=sputsO@ya;D?%*3wo@p^ zFF7;6c4nSk%AS3-N6A7+u%mZ$+$5?ob-k~O-Pf@MMkzBJ6)(nDe$JnBXp+o`(pa)}}SkX?bu%&FwYduQNVNB^z z`1WIvk6u_<6#WkHfP0vGC1W@K6~r6@Q09spumeVBfsvL3u7BPk8Lbc|;(6N!2jWGf z9r_CHP@mGzX?BQmGf)omyek^&>`R}rvOB%;{B13&CeeLTgs1uA2&qGtLRyJ*;0x^g z*Lul+KEdw17E*Eu*T<~ZM^J>{5VGEAwN69|(&o)SL*~dDW1zBiAwbtZk?^K3tYrNAm)K!IliA8izq%cpDtx)E;$OOX%vyO)u) zkU3RruT}9QY|riqfydzvz;MlX43~Y_TpFsQ%)YxvNu;RiH18p*`X4DjVZ%~~=W9Nt zzhS56c)n)4jA&wBgiom*e00oKdd!iyvmU=br9b0WS$CR#H`U=csb+<4?JYX)^jUWb zX+wsxsR5z+Sc#T52a*0nu-{`~)_!QEMd^ayKtJ=gXY15uDniizL&~ z>CqYbaff|K9-VEliO3HU@-aL*Q)71*k_r%a1ZpJ?mg#&Xdoym~;w-@z_<|5_88^)6ehwSmb5n9L~i2yma@0s7&>I= zR-xm)?YO+5Z-Q*&4NR&klQ6nKmT{SrZh7(yF-4nr#S-SXH%K#!2%C?v!F%V+KfEtY zr?>V#FSk9*#=beM=R&YsTJ0QQe{0G-p|`{USNzVNdoxtJ!S=t|SF@0)d~ujvdUJ@p z@i*4x-??(&TK4$Ad&-wS0L|{2U4$SVX8Qqp|6x}D?=bJJ=xb|Q1LlXsdwXvJ`7|nZJbN*X8t2mx=Cn+8AiMH+9ofR{h!BoS>Ebe)|KS&3(j>t%{v|t5;7l-Gwnr=W9v=#!zv~vKw?Qo>iG2iZ1E7&)|Iw zCN}J+YAQIqiaot=u(~HKR`&M3LV43d*8M*jbCaMLviUIl=H#R#{k8mZ{!AFJ{*dLo zT=By0D=HlNzH>)c7|nlNfRSm-ahmA3g5g2C?(+p=&vLzEOLsUKUWz=O6dfGG51~XR ze+R!gv?u+M-siotpkk*^3e_&cJj~=zEntJ*UL<#2z_z{}O)qhdzP)k!KR-h635V1A z%N;SzPaUxWXH0AR(ut?FPo;7qBNpl=@p zv^VQl87gn@%u#JX&ZL9v;mWMV3TARkin%(bh1}0AE%s%jNI(Y?c(8q07!vknA$&BY zLzQI8#A*>G8Tb`g3%E>OJrPDr6W@fCINxgG$AL?rw-MsPjAvC*bq7y_sOG{0EOq~c zX`>er8ksv1{|zIgOu^0bMrag3@XjyBDrS$iP|CA$bR(H6Plv$DdZUCgj6W+AMu~OH z^j%>re^f0sthjyC?cIsN8$L%R`bO)Ng$90n7UEAY1BsU|GSj;eQ9;L` z)AYWU*tqMYb5!}%ghqZdPe^GFIQRI3I4{xBZ7_cDRgBK!7~Gvrg$jU&Hc)T#^;J&rK`Nzg zn8(uJi;>%&VyoVZ4x4Zi?JlDQUa?{p+9mx(yJ@3eT9Z&W&9m9@_afx&vl)MHz=TI; zW8^IQ=%f>nAs6E?SoA-BgMw`>F;;zWLH#+Xm0Er9N{kfGsD#Jm9^gCz;ut&Y{hvK< z!_+2DlJnT4su=mkN@l8hHP9~~(6W7Z@N1>3-gZ-nfgffAKUfm}>Iwqx0FXD&MTL+! zs}U)%;ck!RhgPunKZucQvJYJO;6IX&e-_w8mK&MjXASI?54*{&Gue?3vt>4u1snM&$y?;o%d5|hDk&f9tF<-P+{#yy!yO!Z|Pb0m)_|t{p1Wb_@kZ~v+qR> zxF27xN2-;+NX=gMDYvhyEp@kn+i6EoSekCWvu zv)Q{J7ixMb7~yAMXOTzaaH=H$9 zO9=P!?z>YLA_l$0wSURPY&2%;D%5XHJIITKN4a1(F8(`C;?*2NV9%m` zCy2Uo4+t$CCo2#&yXhcR#7hu|6&Qr$=+is{5=45`x(o<(W5A93fkI*^q8j58cvW1C z3+T>3v_SMZjgNFVscZR(JhuGUupVns;DkO%LPb=H|Blhemr8tj!#wuMvHsrMG3<^9 zcka9&OtxcV<-xfu>677I>%SJt$&>u^Or;n5Jgvq^tF4NPb_VJ~21glnf74 z%V=yP_;P3)IEeN;_BXT(Gr0;eRMMgfwve_sh_)g@TQ>e|7F$!3E&o}TCw3f1w7;A#<6AGUvK_}C@Ts1P zmf&s6M%4K6%Mxq%ju-^lfn8yt9GD#9lK`tIk$?XR5k_SmeVOS$9UkaH`4$(;6WXL3 zmep-VBMZa$?^D?GpH7Z_fY9o9c1pJWJ(4}Bam+^*|IZY#U$lz9F2ZH}r76tw#Dm@E zQlT*@1pkl~mQ%9js^Fl=xkcVduMU zJO`&|^eYNo=HU3MXelNJrht}TctTLiFI3|=!kwRfg6*ga?d1i55MEd8PcyM%9m0Y~ z<{WW$HkEx-m+qA|9u`edVLmK`8R;zkWOT_?3>KERjuq{banzDV9tTm%!J86qW$M~? z^OI^^EiKIF{{UH-wVj8S`K)axwDmgN}<4^q_+v zbOLgfstPIUsiXue>MF$-_ur|<_Ja<+fI}@I%P))95jbj5YCT$RR8VzG#D|d-N2a{qfm=2Q4Yo zi^us|gef^mrq&#vXUN?JcP34Vi(?&!#4xD-w1Bot$PO<2#l$0=w{)o@}ZWjGP zp74O+30g_w{SjAEd=Zz)eNXCOL3Su1*;0Q zqHS9$FxSy@-@wz6O|o|Hh9{I>QqzsrK=5m>)nP%)7W}&*osYena~E`qcMlh%b9a%X z^Tz&EK^#?(rNNg^qWFyoY{Tc_>hobx+V6ZG9s2bc+Ap1)fL00J)p58smI@yE7rXv> zZ+Y}&*7Hnn&0+#A`4^jfW_X|-lhg4yCh0cLy^FHId&_wCuQQ=tUc+=GMyCiyWzJGr z?U^#U*vQh)Mr&3Qkzrfe>a!X0?TM`ZY`*;B6KqI*H`$WPrq*|t7e2uX>O&$@pf<#- zm;V*GjYLAXh3%2bzO%tKw5a;9>b>0#{Rg-PAb&g`e1*-tpLA0Ea=UtFN}t^hx~FXn~zNg^4If`OUMyCY7s zk*3Q=YWH*NS@?HTeX8i@r{fGSyI}9TY}I#r20!8$AnJ=*iH^Q-ob?Pv6o}? zg#WZwH3P!rJilqhO?mXm_m_fZZU7FpYg+L^HW|+b>v*XN8cVNjak1pm8!p^_BV!Y9 z=}{$$S@V`zQ&t(^!?{6i#G{!wYpOrb!_FS-*ATJof1rt#OhtY8(!))D3RRR=`;_*C z`c~Ghf$~&NnXB8eZLK%(eOo|7V_u{stI@^0QU}eB>lG>`8DgP5EJP@zT$!Tdr4BM> zN05u6mhFA?^W% zf{9=i?fH<<;*@Elcz>{p#^BM7Kw1kVM~0usFb>A5>uC13FsNZR^@0gtO;5H=iZxHx z+4%=o?d-x20U_@VrZIzrYtWdaRGrT=0iZ;Ky<;%b{W!?u5g<@<;b1oL$NX4waop#( z_+tT-CdGA41a3xXtmA!H$M4HJyz7i@ozRNCMnIt{Q z`u;Q~^b!V6nUH9;E(BQ%4m&1{vgmhNg+IZ--)5yhCCOf`?ATACJ$~#@-Eo?Ki;%X; z>KsQYe(QU7nh#{rKSzz<@E1l|e9*-@4Rr=UL%6^gq`j(b3YKYoA&bc{T#fMAiR&4o$M4(U<&Wk z-Oq{Q{@*CkKr#DmgDb>FqEl5yeZTZ+mT!v~ru&L2N*1iBM0^?Iu3Ssfo4yKhH{KqD z_$KG>Q^S9aVei={XbuyVGfzGLOR#M2%ewzE);pE(#Qss{qhi=Yzl6!}OkkE@#(Ui8 zL)-}|sHe~HtZ2G9|;xF(_I@nNE6sL6%D0zAj0%u#fW%EU&sG%eU~QV0&>S1vz5 zhIWaQ(uzJTt)qsT2<$&y)+4xaf9jn^-9zd#~+r-rYL zf_k`+L*G=m`$@1~Zn)@{`V`$ZD&WH-FaT-SEXB<9Yq%zNGin~Pg1z)>-`G2mpjTw% z*AbEcy1=hdz-RZwZnbDGZxXqwyitVyygrg$`!#9gaw4#;$gz(sXhQpV4we7kPae!i z9&t&o!+6BmlHVe`EygY%Uvi@o`0yL9Mvz8w4~0j+-nW~!>Kswm7O{7KGe-}I#>h=t z1(BuY!HxHz^m>w6hvX7e!(F2f&%!;Nfv+vG0AkSO6NsB7*JPL% zMq!}Qlua|x+b8m{mydZ445^=>F`kDUb8%n+Ud`K!7TSyOg@7s)!$+(}H}7X(U5zgh zUlH)Me~p(m_@Wl_l+pBrA76Qq3S7f5AaQm`L?fSr;f|{wv5sDU(zhl2tm`&Xhor$f z@ndhOX~UZ0RVt%T!Mvjm_s1!8MN86%b)}*}TTv{%eK%swZR8<+J7%Whnbu~HFDD@8 z{gyw5$ya)@Q-4hI9tZ7)DN3WrH}_&;e};Q^xd$}A7n}B{erWezc6+TlDjHSdgKMWa zTov{LHKp*G8l*i@q~IF*t56o{+jH27KS!3V%0c}psn)zy%hT$`mP93enYDNse{M3s zxVqTJ$ES#;4;Mk(R4rk+&a((MBi6zI#Roe!9_h%|)au)C@d3NfHmrhJabwX(!pMf; zBebT>$S`25)3KWx0%R<`?yTgRtM@(#qBU;@He0wf8o{2wHqiS_I29xJm@EGzoPB<+ z=R+wp1xyF|kq`>2`9Tq$rYXbsA`U$vD5RrC+mpb-5+gDd%3RG=c-es?vS$7d5Y`mW zQ{~U0A!z1z#Efpn7ZOP#SjJynJ*qM6q*up>u*d#N($2usMSH-;{cQhVJtA(x7N9;A z5_FKHWHmfr4Fgs;8wwX;mNjOv-~I}T8l8<6=-Y7`fVW$Cxy1`mt^;t*P$=>2EH>zR zME@Ir56Hb%2yY#*2jn)}P1m4&GJ$bX6lnmB!o_@Q7JKS?A0PUT1*#+1Lp&&py>mU{ zUf4qZ(@fTMy|0X~U%1~GAb+085^fCZi;oB4gxib}YFVtfWoF?0mLZ-#UsNEi!FTi; zMPsV>v*&N<$rtp^jUHX84~B|QMquI;`r(^$`>!S_d?Tu~%jDF$dSKuNXA8Iy*u@&LXoU+PI0%JCtN=P)(h9$^P>4(j?gDNwxn z#%GnD#O|E)M|-iqZcgamLSnECD+#`*g@+%Z5K7#pP4l&?m*ER;ka&Cm>VTBN(}UTA zw|0-5R|(_SQ<)yEOpfNuFca65OjMcV8;!7=N#j>gmDWND-)C+>S2(Wy;R#Sn8B6}V z+gu&JQ1#}cf{7WQvOx~}flA+jw^K$!;Ue*= zmfet&Z0i$nhCt)8W$gpHcLgbjmmfq{0=J{7*V&==zTHZ@lPJ)AP&e~RwaVxZ4;!54 zn)~4kL*!+L||_T>$!!rc7@P*#vnzr{TKC1C68(r}OSzEtQUK6$?NCuu;uqqfzaq(zO(>_szL9<-*li;7#}dBIG^ zma?5bN8%+=(h3$HtFSkebYbL=ae zijFpP{(uls9pyOA1Ug+aHC4XPqXoC;3j; zh9yFKbDV6v05*jvX&~G?htUcwmaJWndlJl>I!DnqgwBDu%UwQk*OXuJty&8A{|4?1 zSRPOpZCF&a%GdDAR`|F|q2qUgvk|#ke7KMtRCRn4(hyLqbrwRLI{XJV5gx~yysY{< zny=@Pe?SsbU!d^9ie;`+Q1|0hT}*!G-U(YLOP;#cZTHcNS6rpx{SHCkt?4t+b{GDp zh%ZE3U7k0OzaWy!kaXpnd@Ce3Db({(z|h^bZjT(T7~m#FmQb7V^&vRf@KIF}*WQgo z&Uhilx$`U2Lp^D@oOiJrVGq_IODzGf){T<%C2H@xI8}|ry(Z7GwE*iv6JY%2!eYby z`O6;ppw}UiB#|{$2@FNP%taBp(N9}N`+H;%!`Q={8x^mFB;`2n|M5T~Q?!il01Re0 z-y*^pe4_|cd8tQ5xJK$)B38cPyZ~UwGLY`iv*~b^dV=`A=NN|CjEQW&+h&w-D~F+6o56v~JmZ)=HbN6A+aU$gp8d>a}bMEy3$UDoj}>WXgek}eO{ zo?s$}W={iOfRQIV6+Li@B-qqic70W3A-(0A;tDgtc86RsjxPEX{Y*#6Ngx5|X9FK9 zYOUk_McBk6v=wG|X`E&=4QBJ~ic{`V|KZQ!V8A-F{)3&+mb_FXE`G3!5-TW?R+U}& zApZ)+y>rrDq)B_*t0KljiYPfulxEK+F&Bq*G)araB!zjMuES0rZ=|?4)Cr!2(I9YH z#G??G`E^Vfv7L{#6vtbN6Kmac)vz7mX}+sZLK^I#Y67q_x9#YTLt$;a&8LZw?R;Y1?6`HAN(v^_PvI|BZyyAQuIGOxm z!la{~%XBj?mr`VC%Ui%mPEJL*r_`q_gSm#I$B=yrhT#vhxZh<&^k1)3xj@;LaLbvENGylE;p%_{>Wh zhEEORyIE3TNfS^NJ+^cM^sV@80zT17-*dvULLVh`eAqSbMk76bf$FlR;H*Lmk0dOQ zdsa2!l(E0Jjr8dM+yDH4j&7A9K|!rbUr`)hr86Qd;3q^lk$)`0DE^)Z z{rTGn&BwzaB~xkG-kjO7gdc9!k%`2)=|{&pjz!$aC85JO4L*MYMRN}S-2x=WdCSbGfrAB)Onsi^gllBI1GT3=~cjI}FPhIE6=S*}G4=RI2bP@~#9PK$dx z^k|j@a~`$OZ$`xuUnxUA?^bcdS4!@+8M6eZu*!-V)=i^8n753>yTDD!6x}?oqPrjL zzqO+)M)^tW@=J)o{^^}FT71+K%R20T+U-D^mp~@1=?0-MauIi|+d?(m)eIPOdy#?X z*`WZ7qInj=95LO)yGVguhEvk9UfUlJ=F#>$6_dNb{Oapk@lY2jQQB9rtBcgvF9O=g z;o>?-T)mo6(bz>AD1RMUq3J43C~Df%a2_gBtKBlKt#yInBcjF*>wAmBz!^PO)(S@TwaEj1z zVpa#eK|B)&GpT06{+*P`S+LJ@5u=^iNWok)A1atbk7Ki6(K14EPsW#TBF3q<;O|BJ zy^6m#@mGnz_wn~J{yxQDRULKU?P~+IxpTsCt1+RlPa$IOW`G5I_XSt(s|(jpyMk9*L=*MKY(F# zII2}!LAb9V9GjU1`z1T>I(;7(#!4G+R^hnC)Q;$f$s}k{*;3vYKb74uu2itWXyBh; zpnh*G5#w*nHs4X3;+@ai8u-H)VQ1&p@#I*V!{T1L%ma|)=zV+<=QbK_UfnEwmQ8%U zkeUWCVBo(Ye&egiJ6N^F-gpI}+VKRwM!oj-BVfqqnL_khnLXJUW1V?|4nvQ}p3r}~ zlK1)h4*2Sz882ua7c{>{9H+%F4h;NTG>@#Nu-g@eKq*kOiu%GUz2eb8X^Q(fbP;6Y zHM!zgpwy?&K~z^bc{VvmX2v@9SxPdG%^dRb_RBAqbn^ZWuo*flhJZMxc|S^8GEE8BIbVo=04HZwty{S;|?h zIgFC;1WElOkI>6=M@d&6BuZBDwW8#1QPOm);&zZU$bFt9+A~zD7#b`^#-1D`rXpCx zb2mJPMy{h7+xQ(kzPE*ga}3wVAHk!>PyKVJqAVEN%E7OH#fQO?4^DON&?w>^>NdU& zWpKTvLHSCUn(x4x{+K+Q=&vl`X!&-`pn|>VM3|)jxbt!VV4BjPV9!Hp&4ZuB;E*}{ z06qUVkpZdYV~BDNq`gi|)Ph}t2J&X%rP@S61m-6ZIYLBqyi5=|!rcTBhg;_F8-l&2 z90P`IhI81j0Em7Tz4Q$sY z;3@)|f_ASNm%L@Vi`G>aS|A>u)~w92TiO4Aw{BO{6w?eu0so|?mXwxO)}0Ma?XQ}- zzxSC36td6v_x*lfUwyIjJoC()`FG~bIcLtC*$JBX!i{WB*Mn#s<|R>3tYEJbYRn$b zd-PyVz4=#uXAjn6 z;ahqzd(LC{I^Dl4R_B>ecooMQMfO#AP`klv0T@~hUZa@R0&ge5`-#5P*`dW0|HdOg zCj#_sQ7krc8`b&7as19G)^*M$nh{t@q#z!Fl&E!4zG(b`MnT$@S7LUN@@D_fsFB+C zXlZTLK?3$ZmdBQZu@>B5C907J3wfO@213jw{#g_lm03UYPSMO73%|5jjY^iq=scbM z1fuk(;$=r zYK3L?0%du1EdMB)4ZPvf5Ui$;rqOEp+z@W;$)ZzMfy9F&_gJVQ!+%0UO`JxNE^!*Z=b<}21#E|c!;i5f>PX9#j!HvQX+!Q+ne>!6jUu(- zESSU9UaW&Hl}h`}Nu^1fPn1R|{2G6|7x>Lrf8eFPn3W&v#rj$91@OXX6i$|8J1$@0 zmfp-Z=w7TXMK>X4@L5a_8kZ6RITRpEPsadZ!Q7w1p?BgBe&IR2*|5=f%G$gxj-IFf zua@L|FP@=pUPjI~k_eHeF&M~Y(-hs317Z~kZ7|vx7BEc1IrCrmf!^$9-_4k)gmENW z=7Y>hyI_H1A@mHEa-57q_aJUvbKDSwCCix3ll9(Ds2%YF8~VxRh~PwsN_Wcy^CGeFplTjT)asertKLN^OZx15Dni$B>kF4jL zV_2`7NHid1UO^W6;BzWuxKS*VVWTKM$A67s$@exp!I^UjJG2@>2!RH&+=!HpZmC*H);ZrlzO3uKKUOQsF-QDaD7 z`;otL6Bv;b37C`Q0OFb%S#8yE0`Mew->W(tF%x+;#?~a<1ZSD8d9_pzz#W4~r8sG9 zpce^vmM~jMz;aJLu>BbjN4Qr{!rsjC0+tuDbWO&iysW4eB3sfreotT4(?^j-b3$Dc z5au;~x!pj=5MTm{tn`Uw>lw9l(FMH`)yiuFn(S-y$Q|_=z^jAw4G5IqJ5)pl?$1)m#ijYpuOtQ?(8pBNkC- z5_1V9G?2tC^gr_F?93+HjcH1LvL)}@nxiyvK;Lk?qynv~b{%|BhF54#5CNU74fdsP z2$fHXdro5lx756~@iNu^C?5q`F&hQ6a$s`0Sy@SX5F_D`kw9lzG4+_Zj2Qt#2t32w zO+F{!IZT|ADeh(`IGf#-0sPd<~bGeT6$=h=9U6dUF9ygFwOun(`(%Zf7K3BI*c z)|Sw6`ouYYpdU+&F2F}+Es18zT3SZ}D@guB!j2+^_v(*Xytsys=+E+V*2j~}OESVZ z*K)DNxxEEeXfu!x-a<9`PW_)!J&sg)g@x~sl%HKVbstDV*O+(s+5W6|JLvM)U?_@5 z@Ti;FjlOuGU?6LrG^jw2qcVE&pRa)w5bK6~hmJ2EBL>MZS@c7QfrUz^d1SG-fw3g* zPkWifCiF5i!Ivqr4i!Hlz`2D#Ip6jvzF?g&N!0t0YIG=-e{eIq$@VsE*_s>p5*|aF z=){p&5#$$dX1#LnCBVh=#9SH976me#Dsp9*A+ltcDxj1@x&++7MPZB>DnqNdMTSPv zAE9hZtXwYt9?N3cfB5QHmT4mbhHEcIVf&B>+R^3k;i>j)T+e@tWf^+YJKQ;dE!MAG z$3GarChAYE=Qj*wvA8IK4;#q(>fep!GY7I8ZK2+fM_mcK3fL@-)OK20JlO(M~YB9lng$4n^8!%ff z>QzX5FU%l5ly(Y*)4-E!Q4CQ%?Ht!Q4)59to6k`A?60q|1MCz+%3u5Nf;cR|XxE}R z=DJN@0bvBnKv7gkXiFs?F47e?;JGMFmfs-DS1RbU7{|}Xv3~l8V|cfCcGKX$LX$s@hRB|re z+|ep>G)2z&L*y;|a6B8YU)G!V9>m7l0iJzo0krP z&Y`q7e{K-2lDrEYUzCap+|dgU6V?6jmNJPR4R~x6V|ww=2C+v6#DWPyqPOt{hLV8n zJspOvf0zQB;C9^-N-B>>3bCRWfBY6U)GSv7BHNV_M1%R!TiD<1=44>6w5-8K>GumF zN9fRR3%^lUtVH*z_3(>xW*DDyE9+0V^T@4iivIp#{N$~yk3MS{4@+P-QT)vbEJy$K z5WXyd4c6a!lCMu-yY(-x=Sv2&SjU@SNjHpbikBO)x{@9d3rt1U>0%W{x9rb8U%{I9 zXO~j6wm-{G@Dqbs(oGj=;w`$37^y~7s(G0^1sus!mcCAupAWf>9W>Iqf$MhG@f#A^ zA$`eh{L4f(nyS%1iP>*j519^JOJF9DHBVF=2MH{vXLr#ui8e?#FNx*rr*7tZlh`Qz zl)>CQ1gtyaT|>ZGn^6JqDtYklb1H$OnuGN(4Cd>GuvGmIpYiX9u=$j9_E6TdyM8b& z_$NDLo}$$U_9Z8g>BC$3s-Y}S-}^IOGnB)rRiV!lsks8 z8*fHa=m?M}CQwc(XiF~jBvqN-+(q}bRQVsn*hd`_Zv`dIMj7pI;d#mIX8r6&UXsju zbzG;FLCt*cof`gDGV6cy%fQ2ur9quHqGag_>_!2B&OkCPS^9;};|LVQ1Tvq=7HMDb zi^(h|ff9$j&q#4_b|Z*k$#-igK-;AI@&S;{X;V7-K6rCY*K3vEpQ$TAL-dAXaTua;&P;rshPG$EH_hLYtBk z-Lw(@ooH&2F6;wJnQ7~g84WIp1{t!M4Fm9S9RF-MOY1*SG;eys<1(yvK>2X)_!qin zjyR+$(^J6c$pd(rgH6zpj>4vW9a552oawtb`|+c7!=yDUx)DaaDW3jilhyl@*PEOLNaBZf@>3b!}MBp%Cdj>17V zGL#}W!B-@XH4sDeBRy8ogsK=RuAp>NgJ={nYwOt6t(DrUG1Ts9{jW;C zq7P<33xnRZk&hqAqB=hPDdM!FM7U6mFC2-j9oswnsgbN-Ru|}urNP$j-$|B$%5pY= z+|Z90%w#YnB%D16yp}*EpCu-olG~nefH6;sR9}BaQ%s22pK;UeVB0qwq=^C*5oEd3 z_*T@9z79PAX>NNAdPqkOc7^Cc*<7hInbHg*g2D<~r)Bg4ypkVsPjbM89Yomuzlo|8 z+3|P>I4s!9L2XK~_M?d~3y_&|Mj#g%0POz=)BrM^oTIellRUSDBHQd$QzNy8%>=(z zRt>)%-Y{6mgc7vT*Je2X9Zh3>c~lzfK|IT_GzOWP=cTcmEZ0^o7n94Dl7io&9H=k?v=R@*h(_`)<2 ztj7x<@)M)kur6~WfzO>wwu5EBrG&I{6!HOM*nol60AA8x6R=CRGg=J1-J03se|7|^ z)b@97@5uO)F_6J1eL-zq#Q*?e(0aW^tv=BkGp$+ELlYwRJExs?(HpD6;xbClMx`M<&I4TJeg+V^G8pZ8}590IEeY zNywe7R1knM@gsQwbfCVOiBJR8zq2^#lRg3N)N~H@abqqc+8lP$VWy@Y* zhn7)p>rmEyhv^#`2#VDpw#{GXOH#K_MB=L!r7Z)isUFShgVIYR+NL4^la#{vKqM%Mh;!Cq^{dvR}nC}v>~wZfu(RINBc zh3{h>+v)RyFEujl#tosGM2;c>Gr~JCW$hRw@Qe;@Pok`A_n~-l$T$ThH=l46BV2fC z>O<`EnYDxss9j)Ah+5#2t>$C|VwHPT}kBF1| zF!`cO5pWyABy=os^zR`AqtI-+UV4dc3scW~P488aPQTZ`nhEyCuQ008pk4P*puo*N z86Pp0#Z3D84d_yVo3y>>=|)OoFlHQ6nMD83ccUgvgYYQ%rTz3O?asnM1DZMpa(1Eu zA2bjV+DQt%1N-?~V_A;n5zGc!9IRASb~NLiGT4T=pD?fjN{mpwD;j+PpMJJr1wz_l z$^|`;Q-oeB_w%zEEc%A;@DZwe6`MEd4cs=44U9TPFuZ^Px+S4UzJLA*?1$(I`TIO z!K&Cm-f8@E6Tsh}LT^Fu6mVH2XP5j5g3cCv^l zkE!D~Okg)n>4p7|CY-B4AFgAx2+XERSY;*bs05hR;>e zwmr)Nyu~~xaTJUmtq#;3deo!s0Y#5tkdlj(=`oJ<*9= z_Z0Q9*?0)8_sHM*@^_w328Hr>4t`Z-8h*vU3&9WhcP7_Xy-7{vU|~y7|3u%NF>=KP zANxZfpu=3Qv>a{8FlYyYR)B^gOTyV1@kGz@7@53x1g)nhOXE670 z_i$YX_!w>Rc0s5}&W|>iotJZHjj*FQ3@S!TFHU1p!m=G)0$#&JSEPl>Vh^>eWe44e zDe7M0cV@CK9Z9)|kq>0P?uM68{!k{n)z=eUAM(ZqGS?JlV0=U)Fp0KXZju^RVwE1K zMW+&0AdNVOTB4_eW2v^z*JW)#Lv3;IDAJNb)wvUV_*~VwZ>|y{Mzqem5qYSZxh9bc zc?nevT*+ARkd7n7UIhiuGu%JOUp4mfDXUn?+4mLXb0P)IZ&U z3NFvR8A$xgXHkdzU+GJCkU5HY12E}OsC3&xdQOA5Ro2z6fmO<9`Y;r3C1Yx{Nj6oot z^m`^h>qeBPee$!kkLl5?U!bMxyA$!^tF+NXvY`pB+Y2YtP+?jtH77oNBR2(htM@*>LBK}5SNP7o?9-PkiRQ@a~pNIQHdBOSy^mQ78`NpAIffP`PR|>G@!O`X%#_z{s=}+ zuF{Fes8Ke}z-+*l2-O-k`cz(XchxZU;}5R2U_O<*zcYKH)ZEA1XDc|3 zAT$%4Qq1DNxS86sj8b&yEZay|pDi=`>uu06os&AIwUb$?9t?J$DXgn+&NE1wi*w7R zr>jw8)fo?4S1Wi+TS*KYSsUpxb@hrmA))5KdevkO7#bjFD$av~V_wNM#JSF$i#78^ z5^|4D@-N*OW8P#)P@E0L?YA>9Il&= zlrfd=mBhVNNU|HnX!V?;WKF|G#g@d~jv7b~=Yl`s3{y(xMkVv4x#Ds3Aa+Mp=Q_BO z^xw4x9rTPYkidRC__Z8VoZ zp&Jg&Uz(=#I7uub{n@$6g<=Et>rnG{A$S`;hgG>p$U8&M4KKc&O633N!7=9aHkC7}=;dQ3|D^2~Gf||61we=af#JZz=yp@s@dP z#7y>foWr%sb@E!}+o)RL>2d9z^D-rJp41ZE-jWC>Y;MpJrJnTP)kq6VCyYr2i4>~$ zl)CgF&YY?_Rf)S(GPjv4o}hu)p(Z<)?lYH_;w9Z*ZFFRAcw_iX=^$Okz6BNy!RtgTro+M0!QY5l93&&}7*7|~ z=@TVOL0~hcnOE&l*%aWdvb0iJ`iQc0Wl;h^brx=b!my<|>hapeT=qKkdV|OXrB)k* zBu@jws8BSjteDVU;#cqrutgrDM4x4A|j~7KgOdsnlV@X zHE3xCTcNfe-Xz;D_9C1OLsDu^&-~lD;}q2 zrm49Ge~m?roUC>X!|esSE;*Jdlj-t0x=zPqQ=Jk*t1A2$e2C_NGc7NWVH5>6Jm;tR z@3KMG-U;s|w7;re@!u7#Q-`Uu3}Idq%IexB-$EsO`$ImU)3vx9C>P%SCR?;wh2sXG zPEg)Oi;e);G@&R)eDW?WBGaK6m~GJcXTx4eXQ_oq>9jT({C8QA9tzZG@kMv446q
$ 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/|bzHVo$@| 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

OakbIVWT;$`mHkFvq%oc~!9UZ)v1g~J4der~ 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

6Ge?}-n92TNgW3eRf2OBVO{W{&J%D7VD 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 Z0V4we0R>$E5C8xSfhGz83B+WN003W>d`AEP delta 183128 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%0Ako{9kf2ruB21v20DG<*`h0b742r%b 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 From 2d0ffb75f3f0e4bb0141821bd5be29253b60984c Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Mon, 4 Apr 2022 00:06:43 +0000 Subject: [PATCH 155/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 629631d11c..c7a410b4f4 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:455 + FORMAL BUILD NUMBER:457 */ -#define PRODUCT_VER_STRING "5.0.0.455" -#define FILE_VER_STRING "WI-T5.0.0.455" -#define LICENSE_VER_STRING "WI-T5.0.0.455" -#define FILE_VER_NUMBER 5, 0, 0, 455 +#define PRODUCT_VER_STRING "5.0.0.457" +#define FILE_VER_STRING "WI-T5.0.0.457" +#define LICENSE_VER_STRING "WI-T5.0.0.457" +#define FILE_VER_NUMBER 5, 0, 0, 457 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "455" +#define FB_BUILD_NO "457" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index ab08ba1dbb..4ae8808a85 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=455 +BuildNum=457 NowAt=`pwd` cd `dirname $0` From bb77ba26448a8a7d82c3a25f2ec31c91311d061c Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sun, 3 Apr 2022 11:26:55 +0300 Subject: [PATCH 156/187] Code simplification --- src/jrd/optimizer/Optimizer.cpp | 70 +++++++++------------------------ src/jrd/recsrc/RecordSource.h | 17 ++++++-- 2 files changed, 31 insertions(+), 56 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 93045db59b..b19e27f47f 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -1335,17 +1335,13 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, if (SortedStream::hasVolatileKey(desc) && !refetchFlag) sort_key->skd_flags |= SKD_separate_data; - map_item->clear(); - map_item->node = node; - map_item->flagOffset = prev_key->getSkdOffset(); + map_item->reset(node, prev_key->getSkdOffset()); map_item->desc = *desc; map_item->desc.dsc_address = (UCHAR*)(IPTR) sort_key->getSkdOffset(); prev_key = sort_key++; - FieldNode* fieldNode; - - if ( (fieldNode = nodeAs(node)) ) + if (const auto fieldNode = nodeAs(node)) { map_item->stream = fieldNode->fieldStream; map_item->fieldId = fieldNode->fieldId; @@ -1368,10 +1364,7 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, if (item.desc->dsc_dtype >= dtype_aligned) map_length = FB_ALIGN(map_length, type_alignments[item.desc->dsc_dtype]); - map_item->clear(); - map_item->fieldId = (SSHORT) item.id; - map_item->stream = item.stream; - map_item->flagOffset = flag_offset++; + map_item->reset(item.stream, (SSHORT) item.id, flag_offset++); map_item->desc = *item.desc; map_item->desc.dsc_address = (UCHAR*)(IPTR) map_length; map_length += item.desc->dsc_length; @@ -1383,24 +1376,14 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, map_length = ROUNDUP(map_length, sizeof(SINT64)); for (const auto stream : streams) { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_int64; - desc->dsc_length = sizeof(SINT64); - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; + map_item->reset(stream, SortedStream::ID_DBKEY); + map_item->desc.makeInt64(0, (SINT64*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; map_item++; - map_item->clear(); - map_item->fieldId = SortedStream::ID_TRANS; - map_item->stream = stream; - desc = &map_item->desc; - desc->dsc_dtype = dtype_int64; - desc->dsc_length = sizeof(SINT64); - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; + map_item->reset(stream, SortedStream::ID_TRANS); + map_item->desc.makeInt64(0, (SINT64*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; map_item++; } @@ -1410,43 +1393,26 @@ SortedStream* Optimizer::generateSort(const StreamList& streams, for (const auto stream : *dbkeyStreams) { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_int64; - desc->dsc_length = sizeof(SINT64); - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; + map_item->reset(stream, SortedStream::ID_DBKEY); + map_item->desc.makeInt64(0, (SINT64*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; map_item++; } for (const auto stream : *dbkeyStreams) { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY_VALID; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = CS_BINARY; - desc->dsc_length = 1; - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; + map_item->reset(stream, SortedStream::ID_DBKEY_VALID); + map_item->desc.makeText(1, CS_BINARY, (UCHAR*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; map_item++; } } for (const auto stream : streams) { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY_VALID; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = CS_BINARY; - desc->dsc_length = 1; - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; + map_item->reset(stream, SortedStream::ID_DBKEY_VALID); + map_item->desc.makeText(1, CS_BINARY, (UCHAR*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; map_item++; } diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 61a67342ee..9b0f33d981 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -546,12 +546,21 @@ namespace Jrd public: struct Item { - void clear() + void reset(NestConst _node, ULONG _flagOffset = 0) { desc.clear(); - flagOffset = fieldId = 0; - stream = 0; - node = NULL; + stream = fieldId = 0; + node = _node; + flagOffset = _flagOffset; + } + + void reset(StreamType _stream, SSHORT _fieldId, ULONG _flagOffset = 0) + { + desc.clear(); + node = nullptr; + stream = _stream; + fieldId = _fieldId; + flagOffset = _flagOffset; } StreamType stream; // stream for field id From 6cb8eacb9a43487c57f3a1da83ce7dc501a4955a Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 4 Apr 2022 10:44:45 +0300 Subject: [PATCH 157/187] Fixed index navigation in inner joins. --- src/jrd/optimizer/InnerJoin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp index 519188645b..6e9b49b3a8 100644 --- a/src/jrd/optimizer/InnerJoin.cpp +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -304,7 +304,6 @@ void InnerJoin::findBestOrder(unsigned position, // Do some initializations tail->activate(); joinedStreams[position].number = stream->stream; - position++; // Save the various flag bits from the optimizer block to reset its // state after each test @@ -322,6 +321,8 @@ void InnerJoin::findBestOrder(unsigned position, newCardinality = positionCardinality * cardinality; } + position++; + // If the partial order is either longer than any previous partial order, // or the same length and cheap, save order as "best" if (position > bestCount || (position == bestCount && newCost < bestCost)) From 10cbd12c0eb5ca54f8546b4a4de036aa97b8be1e Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 5 Apr 2022 00:06:33 +0000 Subject: [PATCH 158/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index c7a410b4f4..6f6b298148 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:457 + FORMAL BUILD NUMBER:459 */ -#define PRODUCT_VER_STRING "5.0.0.457" -#define FILE_VER_STRING "WI-T5.0.0.457" -#define LICENSE_VER_STRING "WI-T5.0.0.457" -#define FILE_VER_NUMBER 5, 0, 0, 457 +#define PRODUCT_VER_STRING "5.0.0.459" +#define FILE_VER_STRING "WI-T5.0.0.459" +#define LICENSE_VER_STRING "WI-T5.0.0.459" +#define FILE_VER_NUMBER 5, 0, 0, 459 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "457" +#define FB_BUILD_NO "459" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 4ae8808a85..84d00da909 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=457 +BuildNum=459 NowAt=`pwd` cd `dirname $0` From 446d4fa392b87dc30e9f54090c511050201305cd Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Wed, 6 Apr 2022 12:35:51 -0300 Subject: [PATCH 159/187] Fix problem found by Dmitry where node->getDesc is called after parts of csb is released. --- src/jrd/Statement.cpp | 8 +++++++- src/jrd/Statement.h | 2 +- src/jrd/dfw.epp | 6 ++++-- src/jrd/met.epp | 9 +++++---- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index b876daced9..77280ba896 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -226,7 +226,7 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) } // Turn a parsed scratch into a statement. -Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, dsc* exprDesc) { DEV_BLKCHK(csb, type_csb); SET_TDBB(tdbb); @@ -306,6 +306,12 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool const auto pool = tdbb->getDefaultPool(); + if (exprDesc) + { + fb_assert(csb->csb_node->getKind() == DmlNode::KIND_VALUE); + static_cast(csb->csb_node)->getDesc(tdbb, csb, exprDesc); + } + statement = FB_NEW_POOL(*pool) Statement(tdbb, pool, csb); tdbb->setRequest(old_request); diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index 6f68a360e1..179be5acef 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -45,7 +45,7 @@ private: Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); public: - static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, dsc* exprDesc = nullptr); static Request* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); StmtNumber getStatementId() const diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index a3a956462d..0aa107e9a3 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -2760,8 +2760,11 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* { idx.idx_expression = static_cast(MET_get_dependencies( tdbb, relation, NULL, 0, NULL, &IDX.RDB$EXPRESSION_BLR, - &idx.idx_expression_statement, &csb, work->dfw_name, obj_expression_index, 0, + nullptr, &csb, work->dfw_name, obj_expression_index, 0, transaction)); + + idx.idx_expression_statement = Statement::makeStatement(tdbb, csb, false, + &idx.idx_expression_desc); } } // end scope @@ -2769,7 +2772,6 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* idx.idx_count = 1; idx.idx_flags |= idx_expressn; - idx.idx_expression->getDesc(tdbb, csb, &idx.idx_expression_desc); idx.idx_rpt[0].idx_itype = DFW_assign_index_type(tdbb, work->dfw_name, idx.idx_expression_desc.dsc_dtype, diff --git a/src/jrd/met.epp b/src/jrd/met.epp index ad3117557f..125761d26e 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -2620,16 +2620,17 @@ void MET_lookup_index_expression(thread_db* tdbb, jrd_rel* relation, index_desc* { // scope Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); + idx->idx_expression = static_cast(MET_parse_blob( tdbb, relation, &IDX.RDB$EXPRESSION_BLR, &csb, - &idx->idx_expression_statement, false, false)); + nullptr, false, false)); + + idx->idx_expression_statement = Statement::makeStatement(tdbb, csb, false, + &idx->idx_expression_desc); } // end scope } END_FOR - if (csb) - idx->idx_expression->getDesc(tdbb, csb, &idx->idx_expression_desc); - delete csb; // if there is no existing index block for this index, create From d64868cdcd91450e3c58aa9bfa9c1090912ac358 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Wed, 16 Mar 2022 10:44:12 +0300 Subject: [PATCH 160/187] Execute ON DISCONNECT triggers in the following cases: 1. During database shutdown (gfix -shut full -force 0). 2. An attachment is deleted from MON$ATTACHMENTS by another attachment. 3. An attachment is closed by idle timeout. ON DISCONNECT triggers will still not be executed during server shutdown. --- src/jrd/jrd.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 1a92558460..bb01a831d8 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -3313,15 +3313,15 @@ void JAttachment::freeEngineData(CheckStatusWrapper* user_status, bool forceFree unsigned flags = PURGE_LINGER; - if (engineShutdown || + if (engineShutdown) + flags |= PURGE_FORCE; + + if (forceFree || (dbb->dbb_ast_flags & DBB_shutdown) || (attachment->att_flags & ATT_shutdown)) { - flags |= PURGE_FORCE; - } - - if (forceFree) flags |= PURGE_NOCHECK; + } ISC_STATUS reason = 0; if (!forceFree) @@ -8603,7 +8603,7 @@ namespace { // purge attachment, rollback any open transactions attachment->att_use_count++; - purge_attachment(tdbb, sAtt, PURGE_FORCE); + purge_attachment(tdbb, sAtt, engineShutdown ? PURGE_FORCE : PURGE_NOCHECK); } catch (const Exception& ex) { From 040db0e05a4d880296c451cdc865077d9d4f2619 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Wed, 16 Mar 2022 10:56:53 +0300 Subject: [PATCH 161/187] Add OnDisconnectTriggerTimeout parameter to firebird.conf --- builds/install/misc/firebird.conf | 12 ++++++++++++ src/common/config/config.h | 5 +++++ src/jrd/exe.cpp | 14 +++++++++++++- src/jrd/jrd.cpp | 11 +++++++---- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index c35907338d..4cec7d682b 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -611,6 +611,18 @@ #ConnectionIdleTimeout = 0 +# ---------------------------- +# +# Set number of seconds after which ON DISCONNECT trigger execution will be +# automatically cancelled by the engine. Zero means no timeout is set. +# +# Per-database configurable. +# +# Type: integer +# +#OnDisconnectTriggerTimeout = 180 + + # ---------------------------- # # How often the pages are flushed on disk diff --git a/src/common/config/config.h b/src/common/config/config.h index a7b1febc4c..f874213c32 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -176,6 +176,7 @@ enum ConfigKey KEY_ENCRYPT_SECURITY_DATABASE, KEY_STMT_TIMEOUT, KEY_CONN_IDLE_TIMEOUT, + KEY_ON_DISCONNECT_TRIG_TIMEOUT, KEY_CLIENT_BATCH_BUFFER, KEY_OUTPUT_REDIRECTION_FILE, KEY_EXT_CONN_POOL_SIZE, @@ -284,6 +285,7 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_BOOLEAN, "AllowEncryptedSecurityDatabase", false, false}, {TYPE_INTEGER, "StatementTimeout", false, 0}, {TYPE_INTEGER, "ConnectionIdleTimeout", false, 0}, + {TYPE_INTEGER, "OnDisconnectTriggerTimeout", false, 180}, {TYPE_INTEGER, "ClientBatchBuffer", false, 128 * 1024}, #ifdef DEV_BUILD {TYPE_STRING, "OutputRedirectionFile", true, "-"}, @@ -603,6 +605,9 @@ public: // set in minutes CONFIG_GET_PER_DB_KEY(unsigned int, getConnIdleTimeout, KEY_CONN_IDLE_TIMEOUT, getInt); + // set in seconds + CONFIG_GET_PER_DB_KEY(unsigned int, getOnDisconnectTrigTimeout, KEY_ON_DISCONNECT_TRIG_TIMEOUT, getInt); + CONFIG_GET_PER_DB_KEY(unsigned int, getClientBatchBuffer, KEY_CLIENT_BATCH_BUFFER, getInt); CONFIG_GET_GLOBAL_STR(getOutputRedirectionFile, KEY_OUTPUT_REDIRECTION_FILE); diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index b9284618d5..b00419452f 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -1209,7 +1209,19 @@ void EXE_execute_triggers(thread_db* tdbb, &tdbb->getAttachment()->att_original_timezone, tdbb->getAttachment()->att_current_timezone); - EXE_start(tdbb, trigger, transaction); + if (trigger_action == TRIGGER_DISCONNECT) + { + if (!trigger->req_timer) + trigger->req_timer = FB_NEW_POOL(*tdbb->getAttachment()->att_pool) TimeoutTimer(); + + const unsigned int timeOut = tdbb->getDatabase()->dbb_config->getOnDisconnectTrigTimeout() * 1000; + trigger->req_timer->setup(timeOut, isc_cfg_stmt_timeout); + trigger->req_timer->start(); + thread_db::TimerGuard timerGuard(tdbb, trigger->req_timer, true); + EXE_start(tdbb, trigger, transaction); + } + else + EXE_start(tdbb, trigger, transaction); } const bool ok = (trigger->req_operation != Request::req_unwind); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index bb01a831d8..1fa7cea8ad 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -8200,15 +8200,21 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign transaction = TRA_start(tdbb, 0, NULL); attachment->att_flags = save_flags; + // Allow cancelling while ON DISCONNECT triggers are running + tdbb->tdbb_flags &= ~TDBB_detaching; + // run ON DISCONNECT triggers EXE_execute_db_triggers(tdbb, transaction, TRIGGER_DISCONNECT); + tdbb->tdbb_flags |= TDBB_detaching; + // and commit the transaction TRA_commit(tdbb, transaction, false); } catch (const Exception& ex) { attachment->att_flags = save_flags; + tdbb->tdbb_flags |= TDBB_detaching; string s; s.printf("Database: %s\n\tError at disconnect:", attachment->att_filename.c_str()); @@ -8927,11 +8933,8 @@ ISC_STATUS thread_db::getCancelState(ISC_STATUS* secondary) if (tdbb_flags & (TDBB_verb_cleanup | TDBB_dfw_cleanup | TDBB_detaching | TDBB_wait_cancel_disable)) return FB_SUCCESS; - if (attachment) + if (attachment && attachment->att_purge_tid != Thread::getId()) { - if (attachment->att_purge_tid == Thread::getId()) - return FB_SUCCESS; - if (attachment->att_flags & ATT_shutdown) { if (database->dbb_ast_flags & DBB_shutdown) From fc85f89be486d3bce16160a798907dfc35bead52 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Wed, 16 Mar 2022 11:01:51 +0300 Subject: [PATCH 162/187] Print ON DISCONNECT trigger name with a stack trace to firebird.log when it is cancelled by a timeout It may help to determine where the trigger is hanging. --- src/jrd/exe.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index b00419452f..bf9d2942ac 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -1252,6 +1252,14 @@ void EXE_execute_triggers(thread_db* tdbb, trigger->req_flags &= ~req_in_use; ex.stuffException(tdbb->tdbb_status_vector); + + if (trigger_action == TRIGGER_DISCONNECT && + !(tdbb->tdbb_flags & TDBB_stack_trace_done) && (tdbb->tdbb_flags & TDBB_sys_error)) + { + stuff_stack_trace(trigger); + tdbb->tdbb_flags |= TDBB_stack_trace_done; + } + trigger_failure(tdbb, trigger); } From ddec610a08fa3f1a6762d693f9381745a4ecaf84 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Wed, 16 Mar 2022 11:02:25 +0300 Subject: [PATCH 163/187] Print ON DISCONNECT trigger exceptions (including cancelling) to trace if log_errors = true --- src/jrd/jrd.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 1fa7cea8ad..92b7b08cee 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -8216,6 +8216,17 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign attachment->att_flags = save_flags; tdbb->tdbb_flags |= TDBB_detaching; + if (attachment->att_trace_manager->needs(ITraceFactory::TRACE_EVENT_ERROR)) + { + FbLocalStatus status; + ex.stuffException(&status); + + TraceConnectionImpl conn(attachment); + TraceStatusVectorImpl traceStatus(&status, TraceStatusVectorImpl::TS_ERRORS); + + attachment->att_trace_manager->event_error(&conn, &traceStatus, FB_FUNCTION); + } + string s; s.printf("Database: %s\n\tError at disconnect:", attachment->att_filename.c_str()); iscLogException(s.c_str(), ex); From 1eedc88a15af09583bbb28669a6179350809f4b2 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Tue, 22 Mar 2022 14:47:45 +0300 Subject: [PATCH 164/187] Comment --- src/jrd/exe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index bf9d2942ac..b6bdc44a87 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -1218,7 +1218,7 @@ void EXE_execute_triggers(thread_db* tdbb, trigger->req_timer->setup(timeOut, isc_cfg_stmt_timeout); trigger->req_timer->start(); thread_db::TimerGuard timerGuard(tdbb, trigger->req_timer, true); - EXE_start(tdbb, trigger, transaction); + EXE_start(tdbb, trigger, transaction); // Under timerGuard scope } else EXE_start(tdbb, trigger, transaction); From 8bd4a7abca8c0605451c24983669aea1a5d43a66 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Tue, 22 Mar 2022 15:30:35 +0300 Subject: [PATCH 165/187] Allow threads which are running purge_attachment() to start new transactions during DB shutdown because it's needed for ON DISCONNECT triggers Autonomous transactions which are used inside ON DISCONNECT triggers are allowed as well. --- src/jrd/tra.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 63acfbec28..702cd463f2 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -1689,7 +1689,11 @@ jrd_tra* TRA_start(thread_db* tdbb, ULONG flags, SSHORT lock_timeout, Jrd::jrd_t Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); - if (dbb->dbb_ast_flags & DBB_shut_tran) + // Starting new transactions should be allowed for threads which + // are running purge_attachment() because it's needed for + // ON DISCONNECT triggers + if (dbb->dbb_ast_flags & DBB_shut_tran && + attachment->att_purge_tid != Thread::getId()) { ERR_post(Arg::Gds(isc_shutinprog) << Arg::Str(attachment->att_filename)); } @@ -1742,7 +1746,11 @@ jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb, Jrd::jrd_t Database* dbb = tdbb->getDatabase(); Jrd::Attachment* attachment = tdbb->getAttachment(); - if (dbb->dbb_ast_flags & DBB_shut_tran) + // Starting new transactions should be allowed for threads which + // are running purge_attachment() because it's needed for + // ON DISCONNECT triggers + if (dbb->dbb_ast_flags & DBB_shut_tran && + attachment->att_purge_tid != Thread::getId()) { ERR_post(Arg::Gds(isc_shutinprog) << Arg::Str(attachment->att_filename)); } From 63f54f109ffc54139c767ba5d4b8d868991d1fba Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Wed, 6 Apr 2022 14:47:40 -0300 Subject: [PATCH 166/187] Misc. --- src/jrd/Statement.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 77280ba896..466d90abfa 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -304,14 +304,14 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool // Build the statement and the final request block. - const auto pool = tdbb->getDefaultPool(); - if (exprDesc) { fb_assert(csb->csb_node->getKind() == DmlNode::KIND_VALUE); static_cast(csb->csb_node)->getDesc(tdbb, csb, exprDesc); } + const auto pool = tdbb->getDefaultPool(); + statement = FB_NEW_POOL(*pool) Statement(tdbb, pool, csb); tdbb->setRequest(old_request); From 1e2493a2525fa89f9f1dc0da0b96ccfd17b2e270 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Thu, 7 Apr 2022 00:06:55 +0000 Subject: [PATCH 167/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 6f6b298148..64e52e5090 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:459 + FORMAL BUILD NUMBER:467 */ -#define PRODUCT_VER_STRING "5.0.0.459" -#define FILE_VER_STRING "WI-T5.0.0.459" -#define LICENSE_VER_STRING "WI-T5.0.0.459" -#define FILE_VER_NUMBER 5, 0, 0, 459 +#define PRODUCT_VER_STRING "5.0.0.467" +#define FILE_VER_STRING "WI-T5.0.0.467" +#define LICENSE_VER_STRING "WI-T5.0.0.467" +#define FILE_VER_NUMBER 5, 0, 0, 467 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "459" +#define FB_BUILD_NO "467" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 84d00da909..d22bcbbe8a 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=459 +BuildNum=467 NowAt=`pwd` cd `dirname $0` From c7f5662d94d439d9305460cb072038f83ddd46bb Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 7 Apr 2022 09:01:57 -0300 Subject: [PATCH 168/187] Correction, thanks to Dmitry. --- src/jrd/Statement.cpp | 27 ++++++++++++++++++--------- src/jrd/Statement.h | 8 +++++++- src/jrd/dfw.epp | 9 ++++----- src/jrd/met.epp | 8 +++----- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 466d90abfa..949e430ea4 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -226,7 +226,8 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) } // Turn a parsed scratch into a statement. -Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, dsc* exprDesc) +Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, + std::function beforeCsbRelease) { DEV_BLKCHK(csb, type_csb); SET_TDBB(tdbb); @@ -302,16 +303,11 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool if (csb->csb_impure > MAX_REQUEST_SIZE) IBERROR(226); // msg 226 request size limit exceeded + if (beforeCsbRelease) + beforeCsbRelease(); + // Build the statement and the final request block. - - if (exprDesc) - { - fb_assert(csb->csb_node->getKind() == DmlNode::KIND_VALUE); - static_cast(csb->csb_node)->getDesc(tdbb, csb, exprDesc); - } - const auto pool = tdbb->getDefaultPool(); - statement = FB_NEW_POOL(*pool) Statement(tdbb, pool, csb); tdbb->setRequest(old_request); @@ -343,6 +339,19 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool return statement; } +Statement* Statement::makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc, + CompilerScratch* csb, bool internalFlag) +{ + fb_assert(csb->csb_node->getKind() == DmlNode::KIND_VALUE); + + return makeStatement(tdbb, csb, internalFlag, + [&] + { + node = static_cast(csb->csb_node); + node->getDesc(tdbb, csb, &desc); + }); +} + // Turn a parsed scratch into an executable request. Request* Statement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) { diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index 179be5acef..2c8cb07661 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -24,6 +24,7 @@ #include "../include/fb_blk.h" #include "../jrd/exe.h" #include "../jrd/EngineInterface.h" +#include namespace Jrd { @@ -45,7 +46,12 @@ private: Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); public: - static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, dsc* exprDesc = nullptr); + static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, + std::function beforeCsbRelease = nullptr); + + static Statement* makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc, + CompilerScratch* csb, bool internalFlag); + static Request* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); StmtNumber getStatementId() const diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 0aa107e9a3..fcc9106463 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -2758,13 +2758,12 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* if (!IDX.RDB$EXPRESSION_BLR.NULL) { - idx.idx_expression = static_cast(MET_get_dependencies( - tdbb, relation, NULL, 0, NULL, &IDX.RDB$EXPRESSION_BLR, + MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR, nullptr, &csb, work->dfw_name, obj_expression_index, 0, - transaction)); + transaction); - idx.idx_expression_statement = Statement::makeStatement(tdbb, csb, false, - &idx.idx_expression_desc); + idx.idx_expression_statement = Statement::makeValueExpression(tdbb, + idx.idx_expression, idx.idx_expression_desc, csb, false); } } // end scope diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 125761d26e..bfffdfc7a8 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -2621,12 +2621,10 @@ void MET_lookup_index_expression(thread_db* tdbb, jrd_rel* relation, index_desc* { // scope Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - idx->idx_expression = static_cast(MET_parse_blob( - tdbb, relation, &IDX.RDB$EXPRESSION_BLR, &csb, - nullptr, false, false)); + MET_parse_blob(tdbb, relation, &IDX.RDB$EXPRESSION_BLR, &csb, nullptr, false, false); - idx->idx_expression_statement = Statement::makeStatement(tdbb, csb, false, - &idx->idx_expression_desc); + idx->idx_expression_statement = Statement::makeValueExpression(tdbb, + idx->idx_expression, idx->idx_expression_desc, csb, false); } // end scope } END_FOR From 96f32c95f6d8c0d5131c8d46c418605b98ac914f Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 7 Apr 2022 14:33:23 -0300 Subject: [PATCH 169/187] Fix #7167 - Incorrect transliteration of field names in constraint violation errors. --- src/jrd/btr.cpp | 4 ++-- src/jrd/filters.cpp | 2 +- src/jrd/mov.cpp | 10 +++++----- src/jrd/mov_proto.h | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 92965d5a72..15aecc733b 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -6140,7 +6140,7 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re { bool notNull = false; const dsc* const desc = BTR_eval_expression(tdbb, idx, record, notNull); - value = DescPrinter(tdbb, notNull ? desc : NULL, MAX_KEY_STRING_LEN).get(); + value = DescPrinter(tdbb, notNull ? desc : NULL, MAX_KEY_STRING_LEN, CS_METADATA).get(); key += " = " + value; } else @@ -6159,7 +6159,7 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re dsc desc; const bool notNull = EVL_field(relation, record, field_id, &desc); - value = DescPrinter(tdbb, notNull ? &desc : NULL, MAX_KEY_STRING_LEN).get(); + value = DescPrinter(tdbb, notNull ? &desc : NULL, MAX_KEY_STRING_LEN, CS_METADATA).get(); key += " = " + value; if (i < idx->idx_count - 1) diff --git a/src/jrd/filters.cpp b/src/jrd/filters.cpp index 964b56dc76..ba1a1343ac 100644 --- a/src/jrd/filters.cpp +++ b/src/jrd/filters.cpp @@ -407,7 +407,7 @@ ISC_STATUS filter_format(USHORT action, BlobControl* control) d = desc; d.dsc_address = pBuff; - DescPrinter val(JRD_get_thread_data(), &d, 32); + DescPrinter val(JRD_get_thread_data(), &d, 32, CS_dynamic); str.printf(fmt2, fieldId, diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index 8b2323d5aa..1e8a618171 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -488,7 +488,7 @@ Int128 MOV_get_int128(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) namespace Jrd { -DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) +DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHORT charSetId) : maxLen(mLen) { const char* const NULL_KEY_STRING = "NULL"; @@ -501,8 +501,8 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) fb_assert(!desc->isBlob()); - const bool isBinary = (desc->isText() && desc->getTextType() == ttype_binary); - value = MOV_make_string2(tdbb, desc, isBinary ? ttype_binary : ttype_dynamic); + const bool isBinary = (desc->isText() && desc->getCharSet() == CS_BINARY); + value = MOV_make_string2(tdbb, desc, isBinary ? CS_BINARY : charSetId); const char* const str = value.c_str(); @@ -510,7 +510,7 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) { if (desc->dsc_dtype == dtype_text) { - const char* const pad = (desc->dsc_sub_type == ttype_binary) ? "\0" : " "; + const char* const pad = (desc->getCharSet() == CS_BINARY) ? "\0" : " "; value.rtrim(pad); } @@ -542,7 +542,7 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) value.resize(maxLen); - const CharSet* const cs = INTL_charset_lookup(tdbb, desc->getCharSet()); + const CharSet* const cs = INTL_charset_lookup(tdbb, charSetId); while (value.hasData() && !cs->wellFormed(value.length(), (const UCHAR*) value.c_str())) value.resize(value.length() - 1); diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index 816842a4b2..8edfd15923 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -62,7 +62,7 @@ namespace Jrd class DescPrinter { public: - DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen); + DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHORT charSetId); const Firebird::string& get() const { From e8f3abbbcce121e000ba014a50551129e459b9ee Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 8 Apr 2022 00:07:18 +0000 Subject: [PATCH 170/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 64e52e5090..baad7666aa 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:467 + FORMAL BUILD NUMBER:469 */ -#define PRODUCT_VER_STRING "5.0.0.467" -#define FILE_VER_STRING "WI-T5.0.0.467" -#define LICENSE_VER_STRING "WI-T5.0.0.467" -#define FILE_VER_NUMBER 5, 0, 0, 467 +#define PRODUCT_VER_STRING "5.0.0.469" +#define FILE_VER_STRING "WI-T5.0.0.469" +#define LICENSE_VER_STRING "WI-T5.0.0.469" +#define FILE_VER_NUMBER 5, 0, 0, 469 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "467" +#define FB_BUILD_NO "469" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index d22bcbbe8a..46723b992d 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=467 +BuildNum=469 NowAt=`pwd` cd `dirname $0` From 1a453a459146a9fa1ea4a641c3afcf16fb64f065 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 8 Apr 2022 17:00:56 +0300 Subject: [PATCH 171/187] Disable the annoying assertion --- src/jrd/jrd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 92b7b08cee..8c898155ba 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -8115,7 +8115,7 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign if (attachment && attachment->att_purge_tid == Thread::getId()) { - fb_assert(false); // recursive call - impossible ? +// fb_assert(false); // recursive call - impossible ? return; } From 1c3926ab709669c438ae59b05fa5868378a60056 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 8 Apr 2022 17:03:24 +0300 Subject: [PATCH 172/187] Parent conjuncts may be utilized multiple times, so assertion is too much paranoid --- src/jrd/optimizer/Optimizer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index b19e27f47f..d34cc4aa9a 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -613,7 +613,6 @@ RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack { if (*selfIter == *subIter) { - fb_assert(!(selfIter & (CONJUNCT_USED | CONJUNCT_MATCHED))); selfIter |= (subIter & (CONJUNCT_USED | CONJUNCT_MATCHED)); break; } From 03398f85fa809829056ab2bc6dd0d96cfc54e34f Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Sat, 9 Apr 2022 00:06:22 +0000 Subject: [PATCH 173/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index baad7666aa..33a86762c7 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:469 + FORMAL BUILD NUMBER:471 */ -#define PRODUCT_VER_STRING "5.0.0.469" -#define FILE_VER_STRING "WI-T5.0.0.469" -#define LICENSE_VER_STRING "WI-T5.0.0.469" -#define FILE_VER_NUMBER 5, 0, 0, 469 +#define PRODUCT_VER_STRING "5.0.0.471" +#define FILE_VER_STRING "WI-T5.0.0.471" +#define LICENSE_VER_STRING "WI-T5.0.0.471" +#define FILE_VER_NUMBER 5, 0, 0, 471 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "469" +#define FB_BUILD_NO "471" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 46723b992d..a93caf0371 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=469 +BuildNum=471 NowAt=`pwd` cd `dirname $0` From 988e7c202592541f21f0d609b9c68472f1deb606 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 11 Apr 2022 08:21:23 -0300 Subject: [PATCH 174/187] Fix #7124 - Inconsistent RDB$USER_PRIVILEGES after dropping identity. --- src/dsql/DdlNodes.epp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index eb05fd8375..6cb8316c99 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -5975,8 +5975,13 @@ void DropSequenceNode::deleteIdentity(thread_db* tdbb, jrd_tra* transaction, con WITH GEN.RDB$GENERATOR_NAME EQ name.c_str() { ERASE GEN; + + if (!GEN.RDB$SECURITY_CLASS.NULL) + deleteSecurityClass(tdbb, transaction, GEN.RDB$SECURITY_CLASS); } END_FOR + + deletePrivilegesByRelName(tdbb, transaction, name, obj_generator); } From 8fb1e5fca2676df165c00a5df657c18f8047c574 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Tue, 12 Apr 2022 00:06:36 +0000 Subject: [PATCH 175/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 33a86762c7..4e55cc206b 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:471 + FORMAL BUILD NUMBER:472 */ -#define PRODUCT_VER_STRING "5.0.0.471" -#define FILE_VER_STRING "WI-T5.0.0.471" -#define LICENSE_VER_STRING "WI-T5.0.0.471" -#define FILE_VER_NUMBER 5, 0, 0, 471 +#define PRODUCT_VER_STRING "5.0.0.472" +#define FILE_VER_STRING "WI-T5.0.0.472" +#define LICENSE_VER_STRING "WI-T5.0.0.472" +#define FILE_VER_NUMBER 5, 0, 0, 472 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "471" +#define FB_BUILD_NO "472" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index a93caf0371..ae139a718a 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=471 +BuildNum=472 NowAt=`pwd` cd `dirname $0` From 7096f67a3132506e0a58909d44ec08de1f0a6393 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 11 Apr 2022 22:50:27 -0300 Subject: [PATCH 176/187] Fix #7168 - Ignore missing UDR libraries during restore. --- src/dsql/StmtNodes.cpp | 6 ++++ src/jrd/ExtEngineManager.cpp | 52 +++++++++++++++++++++++--------- src/jrd/Function.epp | 2 +- src/jrd/met.epp | 6 +++- src/jrd/recsrc/ProcedureScan.cpp | 6 ++++ 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index fcf0bc36f4..5deb5c79d3 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -3234,6 +3234,12 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons Arg::Gds(isc_proc_pack_not_implemented) << Arg::Str(procedure->getName().identifier) << Arg::Str(procedure->getName().package)); } + else if (!procedure->isDefined()) + { + status_exception::raise( + Arg::Gds(isc_prcnotdef) << + Arg::Str(procedure->getName().toString())); + } const_cast(procedure.getObject())->checkReload(tdbb); diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 2b049f75ef..1fbe449a67 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -1298,6 +1298,9 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: metadata->inputParameters.assignRefNoIncr(Routine::createMetadata(udf->getInputFields(), true)); metadata->outputParameters.assignRefNoIncr(Routine::createMetadata(udf->getOutputFields(), true)); + udf->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); + udf->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); + FbLocalStatus status; RefPtr inBuilder(REF_NO_INCR, metadata->inputParameters->getBuilder(&status)); @@ -1314,12 +1317,23 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: externalFunction = attInfo->engine->makeFunction(&status, attInfo->context, metadata, inBuilder, outBuilder); - status.check(); - if (!externalFunction) + try { - status_exception::raise( - Arg::Gds(isc_eem_func_not_returned) << udf->getName().toString() << engine); + status.check(); + + if (!externalFunction) + { + status_exception::raise( + Arg::Gds(isc_eem_func_not_returned) << udf->getName().toString() << engine); + } + } + catch (const Exception&) + { + if (tdbb->getAttachment()->isGbak()) + return; + else + throw; } extInputParameters.assignRefNoIncr(inBuilder->getMetadata(&status)); @@ -1329,9 +1343,6 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: status.check(); } - udf->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); - udf->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); - const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false); const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true); @@ -1423,6 +1434,9 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ metadata->inputParameters.assignRefNoIncr(Routine::createMetadata(prc->getInputFields(), true)); metadata->outputParameters.assignRefNoIncr(Routine::createMetadata(prc->getOutputFields(), true)); + prc->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); + prc->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); + FbLocalStatus status; RefPtr inBuilder(REF_NO_INCR, metadata->inputParameters->getBuilder(&status)); @@ -1439,13 +1453,24 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ externalProcedure = attInfo->engine->makeProcedure(&status, attInfo->context, metadata, inBuilder, outBuilder); - status.check(); - if (!externalProcedure) + try { - status_exception::raise( - Arg::Gds(isc_eem_proc_not_returned) << - prc->getName().toString() << engine); + status.check(); + + if (!externalProcedure) + { + status_exception::raise( + Arg::Gds(isc_eem_proc_not_returned) << + prc->getName().toString() << engine); + } + } + catch (const Exception&) + { + if (tdbb->getAttachment()->isGbak()) + return; + else + throw; } extInputParameters.assignRefNoIncr(inBuilder->getMetadata(&status)); @@ -1455,9 +1480,6 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ status.check(); } - prc->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); - prc->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); - const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false); const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true); diff --git a/src/jrd/Function.epp b/src/jrd/Function.epp index 9396bd786b..cc54bdbd19 100644 --- a/src/jrd/Function.epp +++ b/src/jrd/Function.epp @@ -460,7 +460,7 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT throw; } - fb_assert(function->getStatement()->function == function); + fb_assert(!function->isDefined() || function->getStatement()->function == function); } else { diff --git a/src/jrd/met.epp b/src/jrd/met.epp index bfffdfc7a8..de67421056 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -3401,6 +3401,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) procedure->invoker = attachment->getUserId(procedure->owner); procedure->setImplemented(true); + procedure->setDefined(true); procedure->getInputFields().resize(P.RDB$PROCEDURE_INPUTS); procedure->getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS); procedure->setDefaultCount(0); @@ -3525,6 +3526,9 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) dbb->dbb_extManager->makeProcedure(tdbb, csb, procedure, P.RDB$ENGINE_NAME, (P.RDB$ENTRYPOINT.NULL ? "" : P.RDB$ENTRYPOINT), body.begin()); + + if (!procedure->getExternal()) + procedure->setDefined(false); } else { @@ -3553,7 +3557,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) throw; } - fb_assert(procedure->getStatement()->procedure == procedure); + fb_assert(!procedure->isDefined() || procedure->getStatement()->procedure == procedure); } else { diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index 34655a7a00..75d3a498dc 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -63,6 +63,12 @@ void ProcedureScan::open(thread_db* tdbb) const Arg::Gds(isc_proc_pack_not_implemented) << Arg::Str(m_procedure->getName().identifier) << Arg::Str(m_procedure->getName().package)); } + else if (!m_procedure->isDefined()) + { + status_exception::raise( + Arg::Gds(isc_prcnotdef) << + Arg::Str(m_procedure->getName().toString())); + } const_cast(m_procedure)->checkReload(tdbb); From 7290d40d8120b89f5432d9900f34366fe9c969bf Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 12 Apr 2022 09:34:51 -0300 Subject: [PATCH 177/187] Postfix for #7168 - use same logic of errors/warnings of functions in procedures. --- src/dsql/StmtNodes.cpp | 24 ++++++++++++++++++++++-- src/jrd/RecordSourceNodes.cpp | 20 ++++++++++++++++++++ src/jrd/recsrc/ProcedureScan.cpp | 4 ++-- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 5deb5c79d3..585bf5fa6d 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -2925,6 +2925,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr { SET_TDBB(tdbb); + const auto blrStartPos = csb->csb_blr_reader.getPos(); jrd_prc* procedure = NULL; QualifiedName name; @@ -2957,6 +2958,25 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr if (!procedure) PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); + else + { + if (procedure->isImplemented() && !procedure->isDefined()) + { + if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) + { + PAR_warning( + Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Warning(isc_modnotfound)); + } + else + { + csb->csb_blr_reader.setPos(blrStartPos); + PAR_error(csb, + Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Gds(isc_modnotfound)); + } + } + } ExecProcedureNode* node = FB_NEW_POOL(pool) ExecProcedureNode(pool); node->procedure = procedure; @@ -3237,8 +3257,8 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) cons else if (!procedure->isDefined()) { status_exception::raise( - Arg::Gds(isc_prcnotdef) << - Arg::Str(procedure->getName().toString())); + Arg::Gds(isc_prcnotdef) << Arg::Str(procedure->getName().toString()) << + Arg::Gds(isc_modnotfound)); } const_cast(procedure.getObject())->checkReload(tdbb); diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 05372e42cd..9107a9117b 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -889,6 +889,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch { SET_TDBB(tdbb); + const auto blrStartPos = csb->csb_blr_reader.getPos(); jrd_prc* procedure = NULL; string* aliasString = NULL; QualifiedName name; @@ -955,6 +956,25 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch if (!procedure) PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); + else + { + if (procedure->isImplemented() && !procedure->isDefined()) + { + if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) + { + PAR_warning( + Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Warning(isc_modnotfound)); + } + else + { + csb->csb_blr_reader.setPos(blrStartPos); + PAR_error(csb, + Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Gds(isc_modnotfound)); + } + } + } if (procedure->prc_type == prc_executable) { diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index 75d3a498dc..a6825ccaf9 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -66,8 +66,8 @@ void ProcedureScan::open(thread_db* tdbb) const else if (!m_procedure->isDefined()) { status_exception::raise( - Arg::Gds(isc_prcnotdef) << - Arg::Str(m_procedure->getName().toString())); + Arg::Gds(isc_prcnotdef) << Arg::Str(m_procedure->getName().toString()) << + Arg::Gds(isc_modnotfound)); } const_cast(m_procedure)->checkReload(tdbb); From b0c69b0d3f33622de03582ab8de6e7520cbe447b Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 12 Apr 2022 14:41:15 -0300 Subject: [PATCH 178/187] Fix #7123 - ISQL does not extract "INCREMENT BY" for IDENTITY column. --- src/isql/extract.epp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/isql/extract.epp b/src/isql/extract.epp index 24863068f3..31ca3d2cce 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -519,8 +519,24 @@ int EXTRACT_list_table(const SCHAR* relation_name, (RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_BY_DEFAULT ? "BY DEFAULT" : RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_ALWAYS ? "ALWAYS" : "")); - if (!GEN.RDB$INITIAL_VALUE.NULL && GEN.RDB$INITIAL_VALUE != 0) - isqlGlob.printf(" (START WITH %" SQUADFORMAT ")", GEN.RDB$INITIAL_VALUE); + const bool printInitial = !GEN.RDB$INITIAL_VALUE.NULL && GEN.RDB$INITIAL_VALUE != 0; + const bool printIncrement = !GEN.RDB$GENERATOR_INCREMENT.NULL && GEN.RDB$GENERATOR_INCREMENT != 1; + + if (printInitial || printIncrement) + { + isqlGlob.printf(" ("); + + if (printInitial) + { + isqlGlob.printf("START WITH %" SQUADFORMAT "%s", + GEN.RDB$INITIAL_VALUE, (printIncrement ? " " : "")); + } + + if (printIncrement) + isqlGlob.printf("INCREMENT %" SLONGFORMAT, GEN.RDB$GENERATOR_INCREMENT); + + isqlGlob.printf(")"); + } } END_FOR ON_ERROR From b7e0d36b070dd69e61bf05f09d87b9959e862887 Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Wed, 13 Apr 2022 00:06:53 +0000 Subject: [PATCH 179/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 4e55cc206b..36da3eb263 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:472 + FORMAL BUILD NUMBER:475 */ -#define PRODUCT_VER_STRING "5.0.0.472" -#define FILE_VER_STRING "WI-T5.0.0.472" -#define LICENSE_VER_STRING "WI-T5.0.0.472" -#define FILE_VER_NUMBER 5, 0, 0, 472 +#define PRODUCT_VER_STRING "5.0.0.475" +#define FILE_VER_STRING "WI-T5.0.0.475" +#define LICENSE_VER_STRING "WI-T5.0.0.475" +#define FILE_VER_NUMBER 5, 0, 0, 475 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "472" +#define FB_BUILD_NO "475" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index ae139a718a..896fa56dd9 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=472 +BuildNum=475 NowAt=`pwd` cd `dirname $0` From 94299500fe749e43a806a006973d8a50c4e36d97 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 21 Apr 2022 15:18:17 -0300 Subject: [PATCH 180/187] Adjust logic of POSIX DlfcnModule::findSymbol to avoid problems when same library is loaded through different file names (symbolic links / directly). --- src/common/os/mod_loader.h | 2 +- src/common/os/posix/mod_loader.cpp | 108 +++++++++++++++++------------ 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/common/os/mod_loader.h b/src/common/os/mod_loader.h index 933a4c896e..1c27966d91 100644 --- a/src/common/os/mod_loader.h +++ b/src/common/os/mod_loader.h @@ -73,7 +73,7 @@ public: const Firebird::PathName fileName; #ifdef LINUX - virtual bool getRealPath(Firebird::PathName& realPath) = 0; + virtual bool getRealPath(Firebird::PathName& path) = 0; #endif protected: diff --git a/src/common/os/posix/mod_loader.cpp b/src/common/os/posix/mod_loader.cpp index 9f87818e4a..ac0d61445c 100644 --- a/src/common/os/posix/mod_loader.cpp +++ b/src/common/os/posix/mod_loader.cpp @@ -46,18 +46,16 @@ class DlfcnModule : public ModuleLoader::Module { public: - DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, void* m) - : ModuleLoader::Module(pool, aFileName), - module(m) - {} - + DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, void* m); ~DlfcnModule(); - void* findSymbol(ISC_STATUS*, const Firebird::string&); - bool getRealPath(Firebird::PathName& realPath); + void* findSymbol(ISC_STATUS*, const Firebird::string&) override; + + bool getRealPath(Firebird::PathName& path) override; private: void* module; + Firebird::PathName realPath; }; static void makeErrorStatus(ISC_STATUS* status, const char* text) @@ -154,6 +152,47 @@ ModuleLoader::Module* ModuleLoader::loadModule(ISC_STATUS* status, const Firebir return FB_NEW_POOL(*getDefaultMemoryPool()) DlfcnModule(*getDefaultMemoryPool(), linkPath, module); } +DlfcnModule::DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, void* m) + : ModuleLoader::Module(pool, aFileName), + module(m), + realPath(pool) +{ +#ifdef HAVE_DLINFO + char b[PATH_MAX]; + +#ifdef HAVE_RTLD_DI_ORIGIN + if (dlinfo(module, RTLD_DI_ORIGIN, b) == 0) + { + realPath = b; + realPath += '/'; + realPath += fileName; + + if (realpath(realPath.c_str(), b)) + { + realPath = b; + return; + } + } +#endif + +#ifdef HAVE_RTLD_DI_LINKMAP + struct link_map* lm; + if (dlinfo(module, RTLD_DI_LINKMAP, &lm) == 0) + { + if (realpath(lm->l_name, b)) + { + realPath = b; + return; + } + } +#endif + +#endif + + // Error getting real path. + realPath.clear(); +} + DlfcnModule::~DlfcnModule() { if (module) @@ -165,7 +204,7 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam void* result = dlsym(module, symName.c_str()); if (!result) { - Firebird::string newSym ='_' + symName; + Firebird::string newSym = '_' + symName; result = dlsym(module, newSym.c_str()); } @@ -183,20 +222,28 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam return NULL; } + const auto& libraryPath = realPath.isEmpty() ? fileName : realPath; + + char symbolPathBuffer[PATH_MAX]; + const char* symbolPath = symbolPathBuffer; + + if (!realpath(info.dli_fname, symbolPathBuffer)) + symbolPath = info.dli_fname; + const char* errText = "Actual module name does not match requested"; - if (PathUtils::isRelative(fileName) || PathUtils::isRelative(info.dli_fname)) + if (PathUtils::isRelative(libraryPath) || PathUtils::isRelative(symbolPath)) { // check only name (not path) of the library Firebird::PathName dummyDir, nm1, nm2; - PathUtils::splitLastComponent(dummyDir, nm1, fileName); - PathUtils::splitLastComponent(dummyDir, nm2, info.dli_fname); + PathUtils::splitLastComponent(dummyDir, nm1, libraryPath); + PathUtils::splitLastComponent(dummyDir, nm2, symbolPath); if (nm1 != nm2) { makeErrorStatus(status, errText); return NULL; } } - else if (fileName != info.dli_fname) + else if (libraryPath != symbolPath) { makeErrorStatus(status, errText); return NULL; @@ -206,38 +253,11 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam return result; } -bool DlfcnModule::getRealPath(Firebird::PathName& realPath) +bool DlfcnModule::getRealPath(Firebird::PathName& path) { -#ifdef HAVE_DLINFO - char b[PATH_MAX]; + if (realPath.isEmpty()) + return false; -#ifdef HAVE_RTLD_DI_ORIGIN - if (dlinfo(module, RTLD_DI_ORIGIN, b) == 0) - { - realPath = b; - realPath += '/'; - realPath += fileName; - - if (realpath(realPath.c_str(), b)) - { - realPath = b; - return true; - } - } -#endif - -#ifdef HAVE_RTLD_DI_LINKMAP - struct link_map* lm; - if (dlinfo(module, RTLD_DI_LINKMAP, &lm) == 0) - { - if (realpath(lm->l_name, b)) - { - realPath = b; - return true; - } - } -#endif - -#endif - return false; + path = realPath; + return true; } From f130e8abd99f4680ef526a53e1abd35cc951d50d Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 21 Apr 2022 15:24:53 -0300 Subject: [PATCH 181/187] Improvement #7169 - Improve ICU version mismatch diagnostics. --- src/common/IntlUtil.cpp | 25 +++++++------- src/common/IntlUtil.h | 1 - src/common/intlobj_new.h | 15 +++++++++ src/common/unicode_util.cpp | 64 ++++++++++++++++++++++------------- src/common/unicode_util.h | 8 +++-- src/intl/lc_icu.cpp | 23 ++++++------- src/intl/ld.cpp | 66 +++++++++++++++++++++++++++---------- src/intl/ld_proto.h | 14 ++++++-- src/jrd/IntlManager.cpp | 54 +++++++++++++++++++++--------- src/jrd/IntlManager.h | 2 +- src/jrd/intl.cpp | 35 +++++++++++--------- src/jrd/intl_builtin.cpp | 35 ++++++++++++++++---- src/jrd/intl_proto.h | 10 +++--- 13 files changed, 238 insertions(+), 114 deletions(-) diff --git a/src/common/IntlUtil.cpp b/src/common/IntlUtil.cpp index cbe32c5a04..f1207c22fd 100644 --- a/src/common/IntlUtil.cpp +++ b/src/common/IntlUtil.cpp @@ -664,7 +664,7 @@ bool IntlUtil::readOneChar(Jrd::CharSet* cs, const UCHAR** s, const UCHAR* end, } -// Transform ICU-VERSION attribute (given by the user) in COLL-VERSION (to be stored). +// Add COLL-VERSION attribute. bool IntlUtil::setupIcuAttributes(charset* cs, const string& specificAttributes, const string& configInfo, string& newSpecificAttributes) { @@ -681,10 +681,19 @@ bool IntlUtil::setupIcuAttributes(charset* cs, const string& specificAttributes, map.get("ICU-VERSION", icuVersion); string collVersion; - if (!UnicodeUtil::getCollVersion(icuVersion, configInfo, collVersion)) + auto icu = UnicodeUtil::getCollVersion(icuVersion, configInfo, collVersion); + + if (!icu) return false; - map.remove("ICU-VERSION"); + if (icuVersion.isEmpty()) + { + int majorVersion, minorVersion; + UnicodeUtil::getICUVersion(icu, majorVersion, minorVersion); + icuVersion.printf("%d.%d", majorVersion, minorVersion); + map.put("ICU-VERSION", icuVersion); + } + map.remove("COLL-VERSION"); if (collVersion.hasData()) @@ -781,16 +790,6 @@ bool IntlUtil::readAttributeChar(Jrd::CharSet* cs, const UCHAR** s, const UCHAR* } -void IntlUtil::getDefaultCollationAttributes(UCharBuffer& collAttributes, charset& cs) -{ - string attributes("ICU-VERSION="); - attributes += Jrd::UnicodeUtil::getDefaultIcuVersion(); - setupIcuAttributes(&cs, attributes, "", attributes); - - collAttributes.push(reinterpret_cast(attributes.c_str()), attributes.length()); -} - - static void unicodeDestroy(texttype* tt) { delete[] const_cast(tt->texttype_name); diff --git a/src/common/IntlUtil.h b/src/common/IntlUtil.h index d7b010ef01..a8471dc13e 100644 --- a/src/common/IntlUtil.h +++ b/src/common/IntlUtil.h @@ -94,7 +94,6 @@ public: static bool setupIcuAttributes(charset* cs, const string& specificAttributes, const string& configInfo, string& newSpecificAttributes); - static void getDefaultCollationAttributes(UCharBuffer& collAttributes, charset& cs); private: static string escapeAttribute(Jrd::CharSet* cs, const string& s); diff --git a/src/common/intlobj_new.h b/src/common/intlobj_new.h index 8ba33c079a..b2dd4055fb 100644 --- a/src/common/intlobj_new.h +++ b/src/common/intlobj_new.h @@ -354,6 +354,20 @@ typedef INTL_BOOL (*pfn_INTL_lookup_texttype) ( const ASCII* config_info ); +/* typedef for texttype lookup entry-point - with status buffer */ +typedef INTL_BOOL (*pfn_INTL_lookup_texttype_with_status) ( + char* status_buffer, + ULONG status_buffer_length, + texttype* tt, + const ASCII* texttype_name, + const ASCII* charset_name, + USHORT attributes, + const UCHAR* specific_attributes, + ULONG specific_attributes_length, + INTL_BOOL ignore_attributes, + const ASCII* config_info +); + /* typedef for charset lookup entry-point */ typedef INTL_BOOL (*pfn_INTL_lookup_charset) ( charset* cs, @@ -379,6 +393,7 @@ typedef ULONG (*pfn_INTL_setup_attributes) ( #define TEXTTYPE_ENTRYPOINT LD_lookup_texttype +#define TEXTTYPE_WITH_STATUS_ENTRYPOINT LD_lookup_texttype_with_status #define CHARSET_ENTRYPOINT LD_lookup_charset #define INTL_VERSION_ENTRYPOINT LD_version #define INTL_SETUP_ATTRIBUTES_ENTRYPOINT LD_setup_attributes diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 6d3a69252b..646be61068 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -92,22 +92,24 @@ public: // System-wide ICU have no version number at entries names if (!majorVersion) { + fb_assert(false); // ASF: I don't think this code path is correct. + if (module->findSymbol(NULL, name, ptr)) return; } else { // ICU has several schemas for entries names - const char* patterns[] = + const char* const patterns[] = { - "%s_%d", "%s_%d_%d", "%s_%d%d", "%s", NULL + "%s_%d", "%s_%d_%d", "%s_%d%d", "%s" }; string symbol; - for (const char** p = patterns; *p; ++p) + for (auto pattern : patterns) { - symbol.printf(*p, name, majorVersion, minorVersion); + symbol.printf(pattern, name, majorVersion, minorVersion); if (module->findSymbol(NULL, symbol, ptr)) return; } @@ -323,6 +325,7 @@ private: getEntryPoint("ucnv_open", module, ucnv_open); getEntryPoint("ucnv_close", module, ucnv_close); getEntryPoint("ucnv_fromUChars", module, ucnv_fromUChars); + getEntryPoint("u_getVersion", module, u_getVersion); getEntryPoint("u_tolower", module, u_tolower); getEntryPoint("u_toupper", module, u_toupper); getEntryPoint("u_strCompare", module, u_strCompare); @@ -381,8 +384,11 @@ public: if (o) { - o->vMajor = majorVersion; - o->vMinor = minorVersion; + UVersionInfo versionInfo; + o->u_getVersion(versionInfo); + + o->vMajor = versionInfo[0]; + o->vMinor = versionInfo[1]; } return o; @@ -509,15 +515,15 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, else { // ICU has several schemas for placing version into file name - const char* patterns[] = + const char* const patterns[] = { - "%d_%d", "%d%d", NULL + "%d_%d", "%d.%d", "%d%d" }; PathName s, filename; - for (const char** p = patterns; *p; ++p) + for (auto pattern : patterns) { - s.printf(*p, majorVersion, minorVersion); + s.printf(pattern, majorVersion, minorVersion); filename.printf(templateName, s.c_str()); module = ModuleLoader::fixAndLoadModule(NULL, filename); @@ -1171,7 +1177,7 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c getVersions(configInfo, versions); if (versions.isEmpty()) - gds__log("No versions"); + gds__log("No ICU versions specified"); string version = icuVersion.isEmpty() ? versions[0] : icuVersion; if (version == "default") @@ -1298,6 +1304,13 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c } +void UnicodeUtil::getICUVersion(ICU* icu, int& majorVersion, int& minorVersion) +{ + majorVersion = icu->majorVersion; + minorVersion = icu->minorVersion; +} + + UnicodeUtil::ConversionICU& UnicodeUtil::getConversionICU() { if (convIcu) @@ -1411,13 +1424,13 @@ string UnicodeUtil::getDefaultIcuVersion() } -bool UnicodeUtil::getCollVersion(const Firebird::string& icuVersion, +UnicodeUtil::ICU* UnicodeUtil::getCollVersion(const Firebird::string& icuVersion, const Firebird::string& configInfo, Firebird::string& collVersion) { ICU* icu = loadICU(icuVersion, configInfo); if (!icu) - return false; + return nullptr; char version[U_MAX_VERSION_STRING_LENGTH]; icu->uVersionToString(icu->collVersion, version); @@ -1427,7 +1440,7 @@ bool UnicodeUtil::getCollVersion(const Firebird::string& icuVersion, else collVersion = version; - return true; + return icu; } UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( @@ -1502,12 +1515,11 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( tt->texttype_pad_option = (attributes & TEXTTYPE_ATTR_PAD_SPACE) ? true : false; - ICU* icu = loadICU(collVersion, locale, configInfo); - if (!icu) - { - gds__log("loadICU failed"); - return NULL; - } + string icuVersion; + if (specificAttributes.get(IntlUtil::convertAsciiToUtf16("ICU-VERSION"), icuVersion)) + icuVersion = IntlUtil::convertUtf16ToAscii(icuVersion, &error); + + const auto icu = loadICU(icuVersion, collVersion, locale, configInfo); UErrorCode status = U_ZERO_ERROR; HalfStaticArray rulesBuffer; @@ -2000,8 +2012,8 @@ ULONG UnicodeUtil::Utf16Collation::canonical(ULONG srcLen, const USHORT* src, UL UnicodeUtil::ICU* UnicodeUtil::Utf16Collation::loadICU( - const Firebird::string& collVersion, const Firebird::string& locale, - const Firebird::string& configInfo) + const string& icuVersion, const string& collVersion, + const string& locale, const string& configInfo) { ObjectsArray versions; getVersions(configInfo, versions); @@ -2044,7 +2056,13 @@ UnicodeUtil::ICU* UnicodeUtil::Utf16Collation::loadICU( return icu; } - return NULL; + string errorMsg; + errorMsg.printf( + "An ICU library with collation version %s is required but was not found. " + "You may try to install ICU version %s, used to register the collation in this database " + "or look for 'gfix -icu' in Firebird documentation.", + collVersion.c_str(), icuVersion.c_str()); + (Arg::Gds(isc_random) << errorMsg).raise(); } diff --git a/src/common/unicode_util.h b/src/common/unicode_util.h index c7568a13e0..22c00ba2c1 100644 --- a/src/common/unicode_util.h +++ b/src/common/unicode_util.h @@ -58,6 +58,7 @@ public: const UChar *src, int32_t srcLength, UErrorCode *pErrorCode); + void (U_EXPORT2* u_getVersion) (UVersionInfo versionArray); UChar32 (U_EXPORT2* u_tolower) (UChar32 c); UChar32 (U_EXPORT2* u_toupper) (UChar32 c); int32_t (U_EXPORT2* u_strCompare) (const UChar* s1, int32_t length1, @@ -177,7 +178,8 @@ public: static ConversionICU& getConversionICU(); static ICU* loadICU(const Firebird::string& icuVersion, const Firebird::string& configInfo); - static bool getCollVersion(const Firebird::string& icuVersion, + static void getICUVersion(ICU* icu, int& majorVersion, int& minorVersion); + static ICU* getCollVersion(const Firebird::string& icuVersion, const Firebird::string& configInfo, Firebird::string& collVersion); class Utf16Collation @@ -241,8 +243,8 @@ public: ArrayComparator > ContractionsPrefixMap; - static ICU* loadICU(const Firebird::string& collVersion, const Firebird::string& locale, - const Firebird::string& configInfo); + static ICU* loadICU(const Firebird::string& icuVersion, const Firebird::string& collVersion, + const Firebird::string& locale, const Firebird::string& configInfo); void normalize(ULONG* strLen, const USHORT** str, bool forNumericSort, Firebird::HalfStaticArray& buffer) const; diff --git a/src/intl/lc_icu.cpp b/src/intl/lc_icu.cpp index e5eced3a14..f3f3d821a7 100644 --- a/src/intl/lc_icu.cpp +++ b/src/intl/lc_icu.cpp @@ -51,15 +51,11 @@ static bool texttype_default_init(texttype* tt, ULONG specificAttributesLength) //const ASCII* configInfo) { - charset cs; + AutoPtr cs(FB_NEW charset); memset(&cs, 0, sizeof(cs)); // test if that ICU charset exist - if (CSICU_charset_init(&cs, charSetName)) - { - IntlUtil::finiCharset(&cs); - } - else + if (!CSICU_charset_init(cs, charSetName)) return false; if ((attributes & ~TEXTTYPE_ATTR_PAD_SPACE) || @@ -72,7 +68,7 @@ static bool texttype_default_init(texttype* tt, } // name comes from stack. Copy it. - ASCII* p = FB_NEW_POOL(*getDefaultMemoryPool()) ASCII[strlen(name) + 1]; + ASCII* p = FB_NEW ASCII[strlen(name) + 1]; strcpy(p, name); tt->texttype_name = p; @@ -93,23 +89,24 @@ static bool texttype_unicode_init(texttype* tt, ULONG specificAttributesLength, const ASCII* configInfo) { - charset* cs = FB_NEW_POOL(*getDefaultMemoryPool()) charset; + AutoPtr cs(FB_NEW charset); memset(cs, 0, sizeof(*cs)); // test if that charset exist if (!LD_lookup_charset(cs, charSetName, configInfo)) - { - Firebird::SimpleDelete::clear(cs); return false; - } Firebird::UCharBuffer specificAttributesBuffer; memcpy(specificAttributesBuffer.getBuffer(specificAttributesLength), specificAttributes, specificAttributesLength); // ASF: Don't free "cs". It'will be used in the collation. - return Firebird::IntlUtil::initUnicodeCollation(tt, cs, name, + auto ret = Firebird::IntlUtil::initUnicodeCollation(tt, cs, name, attributes, specificAttributesBuffer, configInfo); + + cs.release(); + + return ret; } @@ -120,7 +117,7 @@ bool LCICU_setup_attributes(const ASCII* name, const ASCII* charSetName, const A if (len > 8 && strcmp(name + len - 8, "_UNICODE") == 0) { - AutoPtr cs(FB_NEW_POOL(*getDefaultMemoryPool()) charset); + AutoPtr cs(FB_NEW charset); memset(cs, 0, sizeof(*cs)); // test if that charset exist diff --git a/src/intl/ld.cpp b/src/intl/ld.cpp index 151cd3e812..eaf8f0f22e 100644 --- a/src/intl/ld.cpp +++ b/src/intl/ld.cpp @@ -28,6 +28,7 @@ #include "../intl/ld_proto.h" #include "../intl/cs_icu.h" #include "../intl/lc_icu.h" +#include "../common/utils_proto.h" #include "fb_exception.h" #ifdef HAVE_SYS_PARAM_H @@ -491,20 +492,12 @@ FB_DLL_EXPORT INTL_BOOL LD_lookup_charset(charset* cs, const ASCII* name, const } -FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info) +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype_with_status(char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info) { - const ASCII* configInfo; - - // ASF: We can't read config_info if version < INTL_VERSION_2, - // since it wasn't pushed in the stack by the engine. - if (version >= INTL_VERSION_2) - configInfo = config_info; - else - configInfo = ""; - if (ignore_attributes) { attributes = TEXTTYPE_ATTR_PAD_SPACE; @@ -512,6 +505,8 @@ FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_n specific_attributes_length = 0; } + string errorMsg; + try { for (int i = 0; collations[i].collationName; ++i) @@ -527,7 +522,7 @@ FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_n { if (strcmp(charSets[j].charSetName, charset_name) == 0) { - if (LD_lookup_charset(&cs, charset_name, configInfo)) + if (LD_lookup_charset(&cs, charset_name, config_info)) break; return false; @@ -547,13 +542,50 @@ FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_n return LCICU_texttype_init( tt, texttype_name, charset_name, attributes, specific_attributes, - specific_attributes_length, configInfo); + specific_attributes_length, config_info); } - catch (const Firebird::BadAlloc&) + catch (const Firebird::status_exception& ex) + { + auto status = ex.value(); + TEXT temp[BUFFER_LARGE]; + + while (fb_interpret(temp, sizeof(temp), &status)) + { + if (errorMsg.hasData()) + errorMsg += "\n-"; + + errorMsg += temp; + } + } + catch (...) { fb_assert(false); - return false; + errorMsg = "Uncaught exception"; } + + if (status_buffer_length) + fb_utils::copy_terminate(status_buffer, errorMsg.c_str(), status_buffer_length); + + return false; +} + + +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info) +{ + const ASCII* configInfo; + + // ASF: We can't read config_info if version < INTL_VERSION_2, + // since it wasn't pushed in the stack by the engine. + if (version >= INTL_VERSION_2) + configInfo = config_info; + else + configInfo = ""; + + return LD_lookup_texttype_with_status(nullptr, 0, tt, texttype_name, charset_name, attributes, specific_attributes, + specific_attributes_length, ignore_attributes, configInfo); } diff --git a/src/intl/ld_proto.h b/src/intl/ld_proto.h index f20fdf076a..20f50d193f 100644 --- a/src/intl/ld_proto.h +++ b/src/intl/ld_proto.h @@ -39,10 +39,18 @@ struct CsConvertImpl extern USHORT version; FB_DLL_EXPORT INTL_BOOL LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* config_info); + +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype_with_status(char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info); + FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info); + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info); + FB_DLL_EXPORT void LD_version(USHORT* version); FB_DLL_EXPORT ULONG LD_setup_attributes( const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, diff --git a/src/jrd/IntlManager.cpp b/src/jrd/IntlManager.cpp index ccc9857e1d..9e09e54936 100644 --- a/src/jrd/IntlManager.cpp +++ b/src/jrd/IntlManager.cpp @@ -625,7 +625,7 @@ bool IntlManager::lookupCharSet(const string& charSetName, charset* cs) } -bool IntlManager::lookupCollation(const string& collationName, +void IntlManager::lookupCollation(const string& collationName, const string& charSetName, USHORT attributes, const UCHAR* specificAttributes, ULONG specificAttributesLen, bool ignoreAttributes, @@ -633,32 +633,56 @@ bool IntlManager::lookupCollation(const string& collationName, { ExternalInfo charSetExternalInfo; ExternalInfo collationExternalInfo; + char statusBuffer[BUFFER_LARGE] = ""; if (charSetCollations->get(charSetName + ":" + charSetName, charSetExternalInfo) && charSetCollations->get(charSetName + ":" + collationName, collationExternalInfo)) { - pfn_INTL_lookup_texttype lookupFunction = NULL; + ModuleLoader::Module* module = nullptr; + + if (collationExternalInfo.moduleName.hasData()) + modules->get(collationExternalInfo.moduleName, module); + + pfn_INTL_lookup_texttype_with_status lookupStatusFunction = nullptr; if (collationExternalInfo.moduleName.isEmpty()) - lookupFunction = INTL_builtin_lookup_texttype; - else - { - ModuleLoader::Module* module; + lookupStatusFunction = INTL_builtin_lookup_texttype_status; + else if (module) + module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_WITH_STATUS_ENTRYPOINT), lookupStatusFunction); - if (modules->get(collationExternalInfo.moduleName, module) && module) - module->findSymbol(NULL, STRINGIZE(TEXTTYPE_ENTRYPOINT), lookupFunction); + if (lookupStatusFunction) + { + if ((*lookupStatusFunction)(statusBuffer, sizeof(statusBuffer), + tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), + attributes, specificAttributes, specificAttributesLen, ignoreAttributes, + collationExternalInfo.configInfo.c_str())) + { + return; + } } - - if (lookupFunction && - (*lookupFunction)(tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), - attributes, specificAttributes, specificAttributesLen, ignoreAttributes, - collationExternalInfo.configInfo.c_str())) + else if (module) { - return true; + pfn_INTL_lookup_texttype lookupFunction = nullptr; + module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_ENTRYPOINT), lookupFunction); + + if (lookupFunction && + (*lookupFunction)(tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), + attributes, specificAttributes, specificAttributesLen, ignoreAttributes, + collationExternalInfo.configInfo.c_str())) + { + return; + } } } - return false; + if (statusBuffer[0]) + { + (Arg::Gds(isc_collation_not_installed) << collationName << charSetName << + Arg::Gds(isc_random) << statusBuffer + ).raise(); + } + else + (Arg::Gds(isc_collation_not_installed) << collationName << charSetName).raise(); } diff --git a/src/jrd/IntlManager.h b/src/jrd/IntlManager.h index 26698497ac..c35ecf9705 100644 --- a/src/jrd/IntlManager.h +++ b/src/jrd/IntlManager.h @@ -47,7 +47,7 @@ public: static bool lookupCharSet(const Firebird::string& charSetName, charset* cs); - static bool lookupCollation(const Firebird::string& collationName, + static void lookupCollation(const Firebird::string& collationName, const Firebird::string& charSetName, USHORT attributes, const UCHAR* specificAttributes, ULONG specificAttributesLen, bool ignoreAttributes, diff --git a/src/jrd/intl.cpp b/src/jrd/intl.cpp index d6cdd9aafa..f03f3773e6 100644 --- a/src/jrd/intl.cpp +++ b/src/jrd/intl.cpp @@ -133,7 +133,7 @@ using namespace Firebird; static bool allSpaces(CharSet*, const BYTE*, ULONG, ULONG); static int blocking_ast_collation(void* ast_object); static void pad_spaces(thread_db*, CHARSET_ID, BYTE *, ULONG); -static INTL_BOOL lookup_texttype(texttype* tt, const SubtypeInfo* info); +static void lookup_texttype(texttype* tt, const SubtypeInfo* info); static GlobalPtr createCollationMtx; @@ -387,15 +387,10 @@ Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id) } Attachment* const att = tdbb->getAttachment(); - texttype* tt = FB_NEW_POOL(*att->att_pool) texttype; + AutoPtr tt(FB_NEW_POOL(*att->att_pool) texttype); memset(tt, 0, sizeof(texttype)); - if (!lookup_texttype(tt, &info)) - { - delete tt; - ERR_post(Arg::Gds(isc_collation_not_installed) << Arg::Str(info.collationName) << - Arg::Str(info.charsetName)); - } + lookup_texttype(tt, &info); if (charset_collations.getCount() <= id) charset_collations.grow(id + 1); @@ -415,9 +410,12 @@ Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id) } } - charset_collations[id] = Collation::createInstance(*att->att_pool, tt_id, tt, info.attributes, charset); + charset_collations[id] = Collation::createInstance(*att->att_pool, tt_id, + tt, info.attributes, charset); charset_collations[id]->name = info.collationName; + tt.release(); + // we don't need a lock in the charset if (id != 0) { @@ -489,9 +487,9 @@ void CharSetContainer::unloadCollation(thread_db* tdbb, USHORT tt_id) } -static INTL_BOOL lookup_texttype(texttype* tt, const SubtypeInfo* info) +static void lookup_texttype(texttype* tt, const SubtypeInfo* info) { - return IntlManager::lookupCollation(info->baseCollationName.c_str(), info->charsetName.c_str(), + IntlManager::lookupCollation(info->baseCollationName.c_str(), info->charsetName.c_str(), info->attributes, info->specificAttributes.begin(), info->specificAttributes.getCount(), info->ignoreAttributes, tt); } @@ -1170,12 +1168,19 @@ bool INTL_texttype_validate(Jrd::thread_db* tdbb, const SubtypeInfo* info) texttype tt; memset(&tt, 0, sizeof(tt)); - bool ret = lookup_texttype(&tt, info); + try + { + lookup_texttype(&tt, info); - if (ret && tt.texttype_fn_destroy) - tt.texttype_fn_destroy(&tt); + if (tt.texttype_fn_destroy) + tt.texttype_fn_destroy(&tt); - return ret; + return true; + } + catch (const Exception&) + { + return false; + } } diff --git a/src/jrd/intl_builtin.cpp b/src/jrd/intl_builtin.cpp index da94a9ac10..4eefd9943f 100644 --- a/src/jrd/intl_builtin.cpp +++ b/src/jrd/intl_builtin.cpp @@ -1658,10 +1658,12 @@ INTL_BOOL INTL_builtin_lookup_charset(charset* cs, const ASCII* charset_name, co } -INTL_BOOL INTL_builtin_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info) +INTL_BOOL INTL_builtin_lookup_texttype_status( + char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info) { if (ignore_attributes) { @@ -1700,8 +1702,29 @@ INTL_BOOL INTL_builtin_lookup_texttype(texttype* tt, const ASCII* texttype_name, if (func) { - return func(tt, texttype_name, charset_name, attributes, - specific_attributes, specific_attributes_length, ignore_attributes, config_info); + Firebird::string errorMsg; + + try + { + return func(tt, texttype_name, charset_name, attributes, + specific_attributes, specific_attributes_length, ignore_attributes, config_info); + } + catch (const Firebird::status_exception& ex) + { + auto status = ex.value(); + TEXT temp[BUFFER_LARGE]; + + while (fb_interpret(temp, sizeof(temp), &status)) + { + if (errorMsg.hasData()) + errorMsg += "\n-"; + + errorMsg += temp; + } + + if (status_buffer_length) + fb_utils::copy_terminate(status_buffer, errorMsg.c_str(), status_buffer_length); + } } return false; diff --git a/src/jrd/intl_proto.h b/src/jrd/intl_proto.h index 58c3b479f8..add4b4af78 100644 --- a/src/jrd/intl_proto.h +++ b/src/jrd/intl_proto.h @@ -56,10 +56,12 @@ USHORT INTL_string_to_key(Jrd::thread_db*, USHORT, const dsc*, dsc*, USHORT); // Built-in charsets/texttypes interface INTL_BOOL INTL_builtin_lookup_charset(charset* cs, const ASCII* charset_name, const ASCII* config_info); -INTL_BOOL INTL_builtin_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info); +INTL_BOOL INTL_builtin_lookup_texttype_status( + char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info); ULONG INTL_builtin_setup_attributes(const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst); From e01370ea77937468a11fb4ca91c295dbb17d4c1e Mon Sep 17 00:00:00 2001 From: firebirds <> Date: Fri, 22 Apr 2022 00:06:54 +0000 Subject: [PATCH 182/187] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 36da3eb263..401c1158a6 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:475 + FORMAL BUILD NUMBER:477 */ -#define PRODUCT_VER_STRING "5.0.0.475" -#define FILE_VER_STRING "WI-T5.0.0.475" -#define LICENSE_VER_STRING "WI-T5.0.0.475" -#define FILE_VER_NUMBER 5, 0, 0, 475 +#define PRODUCT_VER_STRING "5.0.0.477" +#define FILE_VER_STRING "WI-T5.0.0.477" +#define LICENSE_VER_STRING "WI-T5.0.0.477" +#define FILE_VER_NUMBER 5, 0, 0, 477 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "475" +#define FB_BUILD_NO "477" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 896fa56dd9..fb68430959 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=475 +BuildNum=477 NowAt=`pwd` cd `dirname $0` From cc77d95fb6d979430abdd69ecd66817672ee3de9 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 21 Apr 2022 22:22:28 -0300 Subject: [PATCH 183/187] Misc. --- src/intl/lc_icu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intl/lc_icu.cpp b/src/intl/lc_icu.cpp index f3f3d821a7..2b6e157136 100644 --- a/src/intl/lc_icu.cpp +++ b/src/intl/lc_icu.cpp @@ -100,10 +100,10 @@ static bool texttype_unicode_init(texttype* tt, memcpy(specificAttributesBuffer.getBuffer(specificAttributesLength), specificAttributes, specificAttributesLength); - // ASF: Don't free "cs". It'will be used in the collation. auto ret = Firebird::IntlUtil::initUnicodeCollation(tt, cs, name, attributes, specificAttributesBuffer, configInfo); + // ASF: Don't free "cs". It'will be used in the collation. cs.release(); return ret; From 674cd26580e80b0a7d563f52780912413976fb5e Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 21 Apr 2022 22:29:44 -0300 Subject: [PATCH 184/187] Improve error message in the case the of the collation attributes do not have ICU-VERSION - #7169. --- src/common/unicode_util.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 646be61068..a28529053b 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -2057,11 +2057,24 @@ UnicodeUtil::ICU* UnicodeUtil::Utf16Collation::loadICU( } string errorMsg; - errorMsg.printf( - "An ICU library with collation version %s is required but was not found. " - "You may try to install ICU version %s, used to register the collation in this database " - "or look for 'gfix -icu' in Firebird documentation.", - collVersion.c_str(), icuVersion.c_str()); + + if (icuVersion.isEmpty()) + { + errorMsg.printf( + "An ICU library with collation version %s is required but was not found. " + "You may try to install another ICU version which this collation version " + "or look for 'gfix -icu' in Firebird documentation.", + collVersion.c_str()); + } + else + { + errorMsg.printf( + "An ICU library with collation version %s is required but was not found. " + "You may try to install ICU version %s, used to register the collation in this database " + "or look for 'gfix -icu' in Firebird documentation.", + collVersion.c_str(), icuVersion.c_str()); + } + (Arg::Gds(isc_random) << errorMsg).raise(); } From 953f42bbdd306bb2704df5598f75ddc6a7ae8430 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Thu, 21 Apr 2022 22:34:10 -0300 Subject: [PATCH 185/187] Postfix for #7169 - Add LD_lookup_texttype_with_status in fbintl.vers. --- builds/posix/fbintl.vers | 11 ++++++----- src/common/intlobj_new.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/builds/posix/fbintl.vers b/builds/posix/fbintl.vers index a91d69b660..ffadaaff23 100644 --- a/builds/posix/fbintl.vers +++ b/builds/posix/fbintl.vers @@ -7,25 +7,26 @@ # you may not use this file except in compliance with the # License. You may obtain a copy of the License at # http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. -# +# # Software distributed under the License is distributed AS IS, # WITHOUT WARRANTY OF ANY KIND, either express or implied. # See the License for the specific language governing rights # and limitations under the License. -# +# # The Original Code was created by Nickolay Samofatov # for the Firebird Open Source RDBMS project. -# +# # Copyright (c) 2004 Nickolay Samofatov # and all contributors signed below. -# +# # All Rights Reserved. # Contributor(s): ______________________________________. # Adriano dos Santos Fernandes -# +# # LD_lookup_charset LD_lookup_texttype +LD_lookup_texttype_with_status LD_setup_attributes LD_version diff --git a/src/common/intlobj_new.h b/src/common/intlobj_new.h index b2dd4055fb..859f1e4d57 100644 --- a/src/common/intlobj_new.h +++ b/src/common/intlobj_new.h @@ -393,7 +393,7 @@ typedef ULONG (*pfn_INTL_setup_attributes) ( #define TEXTTYPE_ENTRYPOINT LD_lookup_texttype -#define TEXTTYPE_WITH_STATUS_ENTRYPOINT LD_lookup_texttype_with_status +#define TEXTTYPE_WITH_STATUS_ENTRYPOINT LD_lookup_texttype_with_status #define CHARSET_ENTRYPOINT LD_lookup_charset #define INTL_VERSION_ENTRYPOINT LD_version #define INTL_SETUP_ATTRIBUTES_ENTRYPOINT LD_setup_attributes From 0f13df3ce535b73d50175633f03b670286ac5016 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 22 Apr 2022 07:04:51 -0300 Subject: [PATCH 186/187] Fix typo - thanks to Karol B. --- src/common/unicode_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index a28529053b..83241ffa5d 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -2062,7 +2062,7 @@ UnicodeUtil::ICU* UnicodeUtil::Utf16Collation::loadICU( { errorMsg.printf( "An ICU library with collation version %s is required but was not found. " - "You may try to install another ICU version which this collation version " + "You may try to install another ICU version with this collation version " "or look for 'gfix -icu' in Firebird documentation.", collVersion.c_str()); } From 92c4170ebcea4b5ab99349979f63190007b27d9b Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Fri, 22 Apr 2022 10:58:37 -0300 Subject: [PATCH 187/187] Postfix for #7169 and Windows build. --- src/common/unicode_util.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 83241ffa5d..9d257c0615 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -517,7 +517,12 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, // ICU has several schemas for placing version into file name const char* const patterns[] = { - "%d_%d", "%d.%d", "%d%d" +#ifdef WIN_NT + "%d", +#endif + "%d_%d", + "%d.%d", + "%d%d" }; PathName s, filename; @@ -531,7 +536,9 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, break; } +#ifndef WIN_NT // There is no sence to try pattern "%d" for different minor versions + // ASF: In Windows ICU 63.1 libraries use 63.dll suffix. This is handled in 'patterns' above. if (!module && minorVersion == 0) { s.printf("%d", majorVersion); @@ -539,6 +546,7 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, module = ModuleLoader::fixAndLoadModule(NULL, filename); } +#endif } return module; @@ -1327,7 +1335,7 @@ UnicodeUtil::ConversionICU& UnicodeUtil::getConversionICU() // Try "favorite" (distributed on windows) version first const int favMaj = 63; - const int favMin = 0; + const int favMin = 1; try { if ((convIcu = ImplementConversionICU::create(favMaj, favMin)))