From 2528942a8c6f07e6bea2b9e38be1d58b634e0225 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 18 Dec 2024 10:49:00 +0300 Subject: [PATCH 01/49] Better handle null pointer checks in GPRE generated code --- src/gpre/obj_cxx.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/gpre/obj_cxx.cpp b/src/gpre/obj_cxx.cpp index 5486a902bc..8eda92d666 100644 --- a/src/gpre/obj_cxx.cpp +++ b/src/gpre/obj_cxx.cpp @@ -2293,7 +2293,10 @@ static void gen_for( const act* action, int column) const gpre_req* request = action->act_request; if (action->act_error || (action->act_flags & ACT_sql)) - success(column, true, global_status_name, " {"); + { + printa(column, "if (%s && !(%s->getState() & Firebird::IStatus::STATE_ERRORS)) {", + request->req_handle, global_status_name); + } printa(column, "while (1)"); column += INDENT; @@ -3075,11 +3078,10 @@ static void gen_s_start( const act* action, int column) if (action->act_error || (action->act_flags & ACT_sql)) { make_ok_test(action, request, column); + printa(column, "{"); column += INDENT; } gen_start(action, port, column, false); - if (action->act_error || (action->act_flags & ACT_sql)) - column -= INDENT; const TEXT* pattern1 = "if (%V1->getErrors()[1] == isc_bad_req_handle) { %RH->release(); %RH = NULL; }"; PAT args; @@ -3088,6 +3090,12 @@ static void gen_s_start( const act* action, int column) PATTERN_expand((USHORT) column, pattern1, &args); printa(column, "else break;"); + if (action->act_error || (action->act_flags & ACT_sql)) + { + column -= INDENT; + printa(column, "}"); + } + if (action->act_type == ACT_open) { endp(column); From 7ebb66b5d3dc9afa5b161c873fc332d3a6be5b33 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Thu, 19 Dec 2024 00:21:20 +0200 Subject: [PATCH 02/49] Improvement #8353 : Report unique usernames for isc_info_user_names --- src/jrd/inf.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index fd7b7eaa34..cdac0eeb5a 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -633,6 +633,8 @@ void INF_database_info(thread_db* tdbb, } { + StrArray names; + SyncLockGuard sync(&dbb->dbb_sync, SYNC_SHARED, "INF_database_info"); for (const Jrd::Attachment* att = dbb->dbb_attachments; att; att = att->att_next) @@ -643,6 +645,13 @@ void INF_database_info(thread_db* tdbb, { const char* userName = user->getUserName().hasData() ? user->getUserName().c_str() : "(Firebird Worker Thread)"; + + FB_SIZE_T pos; + if (names.find(userName, pos)) + continue; + + names.insert(pos, userName); + p = buffer; const ULONG len = MIN(strlen(userName), MAX_UCHAR); *p++ = static_cast(len); From 6990ab58a17e9efc42f34979b0ba1704aee58b27 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 19 Dec 2024 20:17:13 +0000 Subject: [PATCH 03/49] 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 9d8c1d1307..1c290d9d0a 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:553 + FORMAL BUILD NUMBER:555 */ -#define PRODUCT_VER_STRING "6.0.0.553" -#define FILE_VER_STRING "WI-T6.0.0.553" -#define LICENSE_VER_STRING "WI-T6.0.0.553" -#define FILE_VER_NUMBER 6, 0, 0, 553 +#define PRODUCT_VER_STRING "6.0.0.555" +#define FILE_VER_STRING "WI-T6.0.0.555" +#define LICENSE_VER_STRING "WI-T6.0.0.555" +#define FILE_VER_NUMBER 6, 0, 0, 555 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "553" +#define FB_BUILD_NO "555" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 55c23811eb..fff0d4a9cc 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=553 +BuildNum=555 NowAt=`pwd` cd `dirname $0` From 55b0501fa31b21409668b03f10b37f12c3d4d0f8 Mon Sep 17 00:00:00 2001 From: Mark Rotteveel Date: Sun, 22 Dec 2024 16:51:33 +0100 Subject: [PATCH 04/49] Update version number in firebird.conf --- builds/install/misc/firebird.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index 89c548187d..5221d6facb 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -1,6 +1,6 @@ ######################################### # -# Firebird version 5.0 configuration file +# Firebird version 6.0 configuration file # ######################################### From a0404d8888b2cf8974edd71bcfadc1f1ada70c3b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 22 Dec 2024 20:15:32 +0000 Subject: [PATCH 05/49] 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 1c290d9d0a..d2eb988b9f 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:555 + FORMAL BUILD NUMBER:556 */ -#define PRODUCT_VER_STRING "6.0.0.555" -#define FILE_VER_STRING "WI-T6.0.0.555" -#define LICENSE_VER_STRING "WI-T6.0.0.555" -#define FILE_VER_NUMBER 6, 0, 0, 555 +#define PRODUCT_VER_STRING "6.0.0.556" +#define FILE_VER_STRING "WI-T6.0.0.556" +#define LICENSE_VER_STRING "WI-T6.0.0.556" +#define FILE_VER_NUMBER 6, 0, 0, 556 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "555" +#define FB_BUILD_NO "556" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index fff0d4a9cc..caad83e877 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=555 +BuildNum=556 NowAt=`pwd` cd `dirname $0` From bc89f7e2e86575132bc3ec1bc38467de495df7a8 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 23 Dec 2024 09:44:02 -0300 Subject: [PATCH 06/49] Update CI tools. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 875960c790..cdcfacac23 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,7 +123,7 @@ jobs: build-alpine-x64: if: github.ref_type != 'tag' runs-on: ubuntu-22.04 - container: alpine:3.14 + container: alpine:3.21 steps: - name: Checkout @@ -394,9 +394,9 @@ jobs: run: | mkdir downloads pushd downloads - curl -OL https://dl.google.com/android/repository/android-ndk-r25b-linux.zip + curl -OL https://dl.google.com/android/repository/android-ndk-r27c-linux.zip mkdir -p $NDK - unzip android-ndk-r25b-linux.zip -d $NDK && f=("$NDK"/*) && mv "$NDK"/*/* "$NDK" && rmdir "${f[@]}" + unzip android-ndk-r27c-linux.zip -d $NDK && f=("$NDK"/*) && mv "$NDK"/*/* "$NDK" && rmdir "${f[@]}" - name: Build run: | From 0e0386afba7dbbb6cb2079e7b37333640c370fd0 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 23 Dec 2024 11:50:33 -0300 Subject: [PATCH 07/49] Revert "Update CI tools." This reverts commit bc89f7e2e86575132bc3ec1bc38467de495df7a8. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cdcfacac23..875960c790 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,7 +123,7 @@ jobs: build-alpine-x64: if: github.ref_type != 'tag' runs-on: ubuntu-22.04 - container: alpine:3.21 + container: alpine:3.14 steps: - name: Checkout @@ -394,9 +394,9 @@ jobs: run: | mkdir downloads pushd downloads - curl -OL https://dl.google.com/android/repository/android-ndk-r27c-linux.zip + curl -OL https://dl.google.com/android/repository/android-ndk-r25b-linux.zip mkdir -p $NDK - unzip android-ndk-r27c-linux.zip -d $NDK && f=("$NDK"/*) && mv "$NDK"/*/* "$NDK" && rmdir "${f[@]}" + unzip android-ndk-r25b-linux.zip -d $NDK && f=("$NDK"/*) && mv "$NDK"/*/* "$NDK" && rmdir "${f[@]}" - name: Build run: | From 387d8ad16a5789cb9a3fb25dc56ad27a634f0c39 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 23 Dec 2024 21:09:02 +0300 Subject: [PATCH 08/49] Fix statement ID output format in the trace --- src/utilities/ntrace/TracePluginImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities/ntrace/TracePluginImpl.cpp b/src/utilities/ntrace/TracePluginImpl.cpp index a71372804a..766613c1a5 100644 --- a/src/utilities/ntrace/TracePluginImpl.cpp +++ b/src/utilities/ntrace/TracePluginImpl.cpp @@ -1818,7 +1818,7 @@ void TracePluginImpl::register_sql_statement(ITraceSQLStatement* statement) stmt_data.description = FB_NEW_POOL(*getDefaultMemoryPool()) string(*getDefaultMemoryPool()); if (stmt_data.id) { - stmt_data.description->printf(NEWLINE "Statement %d:", stmt_data.id); + stmt_data.description->printf(NEWLINE "Statement %" SQUADFORMAT":", stmt_data.id); } string temp(*getDefaultMemoryPool()); From cc8009cc39ad76731eae6fb7f5035eb7aff0eee1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Dec 2024 20:16:35 +0000 Subject: [PATCH 09/49] 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 d2eb988b9f..c21f70afd0 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:556 + FORMAL BUILD NUMBER:559 */ -#define PRODUCT_VER_STRING "6.0.0.556" -#define FILE_VER_STRING "WI-T6.0.0.556" -#define LICENSE_VER_STRING "WI-T6.0.0.556" -#define FILE_VER_NUMBER 6, 0, 0, 556 +#define PRODUCT_VER_STRING "6.0.0.559" +#define FILE_VER_STRING "WI-T6.0.0.559" +#define LICENSE_VER_STRING "WI-T6.0.0.559" +#define FILE_VER_NUMBER 6, 0, 0, 559 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "556" +#define FB_BUILD_NO "559" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index caad83e877..a7210e9f4f 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=556 +BuildNum=559 NowAt=`pwd` cd `dirname $0` From c1707c4d6e400c9368178ac7265995192f03b8b9 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 25 Dec 2024 08:51:19 +0300 Subject: [PATCH 10/49] Change FB_NELEM return type to unsigned and resolve all FB_NELEM related Wsign-compare warnings (#8338) * Change `FB_NELEM` return type to unsigned and resolve all `FB_NELEM` related `Wsign-compare` warnings * Compare FB_NELEM only with FB_SIZE_T and FB_SSIZE_T * One more Wsign-compare fix --------- Co-authored-by: Artyom Abakumov --- src/common/CvtFormat.cpp | 4 ++-- src/common/SimilarToRegex.cpp | 4 ++-- src/common/TextType.cpp | 2 +- src/common/classes/NoThrowTimeStamp.cpp | 2 +- src/common/pretty.cpp | 2 +- src/gpre/hsh.cpp | 4 ++-- src/include/fb_types.h | 4 ++-- src/isql/isql.epp | 4 ++-- src/isql/show.epp | 4 ++-- src/jrd/Collation.cpp | 2 +- src/jrd/SysFunction.cpp | 3 ++- src/jrd/blob_filter.cpp | 2 +- src/jrd/extds/InternalDS.cpp | 2 +- src/jrd/filters.cpp | 2 +- src/jrd/ini.epp | 2 +- src/jrd/par.cpp | 2 +- src/jrd/sort.cpp | 2 +- src/yvalve/alt.cpp | 2 +- src/yvalve/gds.cpp | 4 ++-- 19 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/common/CvtFormat.cpp b/src/common/CvtFormat.cpp index 3ceb1c256e..92c303cd15 100644 --- a/src/common/CvtFormat.cpp +++ b/src/common/CvtFormat.cpp @@ -1331,7 +1331,7 @@ namespace bool isFound = false; std::string_view monthShortName = getSubstringFromString(str, strLength, strOffset, 3); - for (int i = 0; i < FB_NELEM(FB_SHORT_MONTHS) - 1; i++) + for (FB_SIZE_T i = 0; i < FB_NELEM(FB_SHORT_MONTHS) - 1; i++) { if (std::equal(monthShortName.begin(), monthShortName.end(), FB_SHORT_MONTHS[i], FB_SHORT_MONTHS[i] + strlen(FB_SHORT_MONTHS[i]), @@ -1352,7 +1352,7 @@ namespace bool isFound = false; std::string_view monthFullName = getSubstringFromString(str, strLength, strOffset); - for (int i = 0; i < FB_NELEM(FB_LONG_MONTHS_UPPER) - 1; i++) + for (FB_SIZE_T i = 0; i < FB_NELEM(FB_LONG_MONTHS_UPPER) - 1; i++) { if (std::equal(monthFullName.begin(), monthFullName.end(), FB_LONG_MONTHS_UPPER[i], FB_LONG_MONTHS_UPPER[i] + strlen(FB_LONG_MONTHS_UPPER[i]), diff --git a/src/common/SimilarToRegex.cpp b/src/common/SimilarToRegex.cpp index 42d0709133..b1b30ed0b4 100644 --- a/src/common/SimilarToRegex.cpp +++ b/src/common/SimilarToRegex.cpp @@ -410,7 +410,7 @@ namespace if (!hasPatternChar() || getPatternChar() != ']') status_exception::raise(Arg::Gds(isc_invalid_similar_pattern)); - for (item.clazz = 0; item.clazz < FB_NELEM(classes); ++item.clazz) + for (item.clazz = 0; static_cast(item.clazz) < FB_NELEM(classes); ++item.clazz) { if (fb_utils::strnicmp(patternStr + charSavePos, classes[item.clazz].similarClass, len) == 0) @@ -419,7 +419,7 @@ namespace } } - if (item.clazz >= FB_NELEM(classes)) + if (static_cast(item.clazz) >= FB_NELEM(classes)) status_exception::raise(Arg::Gds(isc_invalid_similar_pattern)); } else diff --git a/src/common/TextType.cpp b/src/common/TextType.cpp index ae095b4a2e..4d8353655e 100644 --- a/src/common/TextType.cpp +++ b/src/common/TextType.cpp @@ -153,7 +153,7 @@ TextType::TextType(TTYPE_ID _type, texttype *_tt, USHORT _attributes, CharSet* _ {'S', CHAR_UPPER_S} }; - for (int i = 0; i < FB_NELEM(conversions); i++) + for (FB_SIZE_T i = 0; i < FB_NELEM(conversions); i++) { try { diff --git a/src/common/classes/NoThrowTimeStamp.cpp b/src/common/classes/NoThrowTimeStamp.cpp index 0384cfada2..60a979c93a 100644 --- a/src/common/classes/NoThrowTimeStamp.cpp +++ b/src/common/classes/NoThrowTimeStamp.cpp @@ -330,7 +330,7 @@ void NoThrowTimeStamp::round_time(ISC_TIME &ntime, const int precision) if (scale <= 0) return; - fb_assert(scale < FB_NELEM(POW_10_TABLE)); + fb_assert(static_cast(scale) < FB_NELEM(POW_10_TABLE)); const ISC_TIME period = POW_10_TABLE[scale]; diff --git a/src/common/pretty.cpp b/src/common/pretty.cpp index b20c0c394a..d1a0e6956d 100644 --- a/src/common/pretty.cpp +++ b/src/common/pretty.cpp @@ -159,7 +159,7 @@ int PRETTY_print_cdb(const UCHAR* blr, FPTR_PRINT_CALLBACK routine, void* user_a while (parameter = BLR_BYTE) { const char* p; - if (parameter > FB_NELEM(cdb_table) || !(p = cdb_table[parameter])) + if (parameter > static_cast(FB_NELEM(cdb_table)) || !(p = cdb_table[parameter])) { return error(control, 0, "*** cdb parameter %d is undefined ***\n", parameter); } diff --git a/src/gpre/hsh.cpp b/src/gpre/hsh.cpp index 1d1b4b0360..3afd3d7c45 100644 --- a/src/gpre/hsh.cpp +++ b/src/gpre/hsh.cpp @@ -38,7 +38,7 @@ static int hash(const SCHAR*); static bool scompare(const SCHAR*, const SCHAR*); static bool scompare2(const SCHAR*, const SCHAR*); -const int HASH_SIZE = 211; +const FB_SIZE_T HASH_SIZE = 211; static gpre_sym* hash_table[HASH_SIZE]; static gpre_sym* key_symbols; @@ -80,7 +80,7 @@ void HSH_init() { //const char *string; - int i = 0; + FB_SIZE_T i = 0; for (gpre_sym** ptr = hash_table; i < HASH_SIZE; i++) *ptr++ = NULL; diff --git a/src/include/fb_types.h b/src/include/fb_types.h index 19ee4cf3e6..7c8a43b093 100644 --- a/src/include/fb_types.h +++ b/src/include/fb_types.h @@ -135,9 +135,9 @@ typedef int (*lock_ast_t)(void*); // Number of elements in an array template -constexpr int FB_NELEM(const T (&)[N]) +constexpr FB_SIZE_T FB_NELEM(const T (&)[N]) { - return N; + return static_cast(N); } // Intl types diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 68563554f2..8de2929f97 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -904,7 +904,7 @@ int ISQL_main(int argc, char* argv[]) TEXT helpstring[158]; IUTILS_msg_get(USAGE, sizeof(helpstring), helpstring); STDERROUT(helpstring); - for (int i = 0; i < FB_NELEM(isql_in_sw_table); i++) + for (FB_SIZE_T i = 0; i < FB_NELEM(isql_in_sw_table); i++) { if (isql_in_sw_table[i].in_sw_msg > 0) { @@ -5128,7 +5128,7 @@ static processing_state frontend(const TEXT* statement) // Shift parms to upper case, leaving original case in lparms typedef TEXT* isql_params_t[MAX_TERMS]; isql_params_t parms, lparms; - for (int iter = 0; iter < FB_NELEM(lparms); ++iter) + for (FB_SIZE_T iter = 0; iter < FB_NELEM(lparms); ++iter) { lparms[iter] = NULL; parms[iter] = NULL; diff --git a/src/isql/show.epp b/src/isql/show.epp index c15a22b422..83903e99a7 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -3752,7 +3752,7 @@ static processing_state show_dependencies(const char* object) bool missing = true; - for (int i = 0; i < FB_NELEM(Object_types); ++i) + for (FB_SIZE_T i = 0; i < FB_NELEM(Object_types); ++i) { if (show_dependencies(object, i) == SKIP) { @@ -6709,4 +6709,4 @@ static processing_state show_wireStats() return ps_ERR; return SKIP; -} \ No newline at end of file +} diff --git a/src/jrd/Collation.cpp b/src/jrd/Collation.cpp index a5bc9650f1..94fde12f1c 100644 --- a/src/jrd/Collation.cpp +++ b/src/jrd/Collation.cpp @@ -898,7 +898,7 @@ private: if (*control == *(CharType*) obj->getCanonicalChar(CHAR_GDML_SUBSTITUTE)) { // Note: don't allow substitution characters larger than vector - CharType** const end_vector = vector + (((int) c < FB_NELEM(vector)) ? c : 0); + CharType** const end_vector = vector + ((static_cast(c) < static_cast(FB_NELEM(vector))) ? c : 0); while (v <= end_vector) *v++ = 0; *end_vector = t; diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 0347d9b809..4bf7b03a27 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -62,6 +62,7 @@ #include "../common/classes/FpeControl.h" #include "../jrd/extds/ExtDS.h" #include "../jrd/align.h" +#include "firebird/impl/types_pub.h" #include #include @@ -2709,7 +2710,7 @@ const char* extractParts[] = const char* getPartName(int n) { - if (n < 0 || n >= FB_NELEM(extractParts) || !extractParts[n]) + if (n < 0 || static_cast(n) >= FB_NELEM(extractParts) || !extractParts[n]) return "Unknown"; return extractParts[n]; diff --git a/src/jrd/blob_filter.cpp b/src/jrd/blob_filter.cpp index 335875aefd..704574567a 100644 --- a/src/jrd/blob_filter.cpp +++ b/src/jrd/blob_filter.cpp @@ -239,7 +239,7 @@ BlobFilter* BLF_lookup_internal_filter(thread_db* tdbb, SSHORT from, SSHORT to) // Check for system defined filter - if (to == isc_blob_text && from >= 0 && from < FB_NELEM(filters)) + if (to == isc_blob_text && from >= 0 && static_cast(from) < FB_NELEM(filters)) { BlobFilter* result = FB_NEW_POOL(*dbb->dbb_permanent) BlobFilter(*dbb->dbb_permanent); result->blf_next = NULL; diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 65585493b6..a576b986a2 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -171,7 +171,7 @@ void InternalConnection::attach(thread_db* tdbb) memset(m_features, false, sizeof(m_features)); static const info_features features[] = ENGINE_FEATURES; - for (int i = 0; i < FB_NELEM(features); i++) + for (FB_SIZE_T i = 0; i < FB_NELEM(features); i++) setFeature(features[i]); } diff --git a/src/jrd/filters.cpp b/src/jrd/filters.cpp index c8fec42111..387b73f095 100644 --- a/src/jrd/filters.cpp +++ b/src/jrd/filters.cpp @@ -811,7 +811,7 @@ ISC_STATUS filter_transliterate_text(USHORT action, BlobControl* control) { case isc_blob_filter_open: case isc_blob_filter_create: - for (SSHORT i = 0; i < FB_NELEM(control->ctl_data); i++) + for (FB_SIZE_T i = 0; i < FB_NELEM(control->ctl_data); i++) control->ctl_data[i] = 0; aux = NULL; diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index d0d6527971..03413fc8d0 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -1619,7 +1619,7 @@ static void store_indices(thread_db* tdbb, USHORT odsVersion) AutoRequest handle1, handle2, handle3; - for (int n = 0; n < SYSTEM_INDEX_COUNT; n++) + for (FB_SIZE_T n = 0; n < SYSTEM_INDEX_COUNT; n++) { const ini_idx_t* index = &indices[n]; const auto relation = MET_relation(tdbb, index->ini_idx_relid); diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 97b9111202..3d7442d49d 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -1519,7 +1519,7 @@ DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb) const ULONG blrOffset = csb->csb_blr_reader.getOffset(); const SSHORT blrOperator = csb->csb_blr_reader.getByte(); - if (blrOperator < 0 || blrOperator >= FB_NELEM(blr_parsers)) + if (blrOperator < 0 || static_cast(blrOperator) >= FB_NELEM(blr_parsers)) { // NS: This error string is correct, please do not mangle it again and again. // The whole error message is "BLR syntax error: expected %s at offset %d, encountered %d" diff --git a/src/jrd/sort.cpp b/src/jrd/sort.cpp index 113ee6cbcf..4a264a4280 100644 --- a/src/jrd/sort.cpp +++ b/src/jrd/sort.cpp @@ -1525,7 +1525,7 @@ void Sort::mergeRuns(USHORT n) // and there n < RUN_GROUP * MAX_MERGE_LEVEL merge_control blks[RUN_GROUP * MAX_MERGE_LEVEL]; - fb_assert((n - 1) <= FB_NELEM(blks)); // stack var big enough? + fb_assert(static_cast(n - 1) <= FB_NELEM(blks)); // stack var big enough? m_longs -= SIZEOF_SR_BCKPTR_IN_LONGS; diff --git a/src/yvalve/alt.cpp b/src/yvalve/alt.cpp index 2199c7dc11..4f2e2d2ffb 100644 --- a/src/yvalve/alt.cpp +++ b/src/yvalve/alt.cpp @@ -250,7 +250,7 @@ ISC_STATUS API_ROUTINE_VARARG gds__start_transaction(ISC_STATUS* status_vector, teb_t tebs[16]; teb_t* teb = tebs; - if (count > FB_NELEM(tebs)) + if (static_cast(count) > FB_NELEM(tebs)) teb = (teb_t*) gds__alloc(((SLONG) sizeof(teb_t) * count)); // FREE: later in this module diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 765b36d585..a1f1370818 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -4025,7 +4025,7 @@ static void blr_print_verb(gds_ctl* control, SSHORT level) case blr_invoke_function_type: n = control->ctl_blr_reader.getByte(); - if (n == 0 || n >= FB_NELEM(typeSubCodes)) + if (n == 0 || n >= static_cast(FB_NELEM(typeSubCodes))) blr_error(control, "*** invalid blr_invoke_function_type sub code ***"); blr_format(control, "blr_invoke_function_type_%s,", typeSubCodes[n]); @@ -4123,7 +4123,7 @@ static void blr_print_verb(gds_ctl* control, SSHORT level) case blr_invsel_procedure_type: n = control->ctl_blr_reader.getByte(); - if (n == 0 || n >= FB_NELEM(typeSubCodes)) + if (n == 0 || n >= static_cast(FB_NELEM(typeSubCodes))) blr_error(control, "*** invalid blr_invsel_procedure_type sub code ***"); blr_format(control, "blr_invsel_procedure_type_%s,", typeSubCodes[n]); From fa83e793d1bd0c67e41c4d0d73475840443b4d20 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 25 Dec 2024 20:16:22 +0000 Subject: [PATCH 11/49] 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 c21f70afd0..4d8bb99db4 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:559 + FORMAL BUILD NUMBER:560 */ -#define PRODUCT_VER_STRING "6.0.0.559" -#define FILE_VER_STRING "WI-T6.0.0.559" -#define LICENSE_VER_STRING "WI-T6.0.0.559" -#define FILE_VER_NUMBER 6, 0, 0, 559 +#define PRODUCT_VER_STRING "6.0.0.560" +#define FILE_VER_STRING "WI-T6.0.0.560" +#define LICENSE_VER_STRING "WI-T6.0.0.560" +#define FILE_VER_NUMBER 6, 0, 0, 560 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "559" +#define FB_BUILD_NO "560" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index a7210e9f4f..0debdd3468 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=559 +BuildNum=560 NowAt=`pwd` cd `dirname $0` From 566bebf2d53f447c541d4b4ca5616b34d7d37616 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Thu, 26 Dec 2024 14:38:50 +0300 Subject: [PATCH 12/49] Make return-type a compilation error (#8362) This protects from undefined behavior cases that can be added to the code by mistake. --- builds/posix/make.defaults | 2 +- builds/win32/msvc15/FirebirdCommon.props | 1 + src/common/CvtFormat.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/builds/posix/make.defaults b/builds/posix/make.defaults index 3c28683f6f..e41232b18b 100755 --- a/builds/posix/make.defaults +++ b/builds/posix/make.defaults @@ -111,7 +111,7 @@ GLOB_OPTIONS:= #____________________________________________________________________________ # Global c++ flags: firebird needs no RTTI, choose build standard and c++ specific warnings level -PLUSPLUS_FLAGS:= -fno-rtti -std=c++17 -Werror=delete-incomplete +PLUSPLUS_FLAGS:= -fno-rtti -std=c++17 -Werror=delete-incomplete -Werror=return-type # If this is defined then we use special rules useful for developers only IsDeveloper = @DEVEL_FLG@ diff --git a/builds/win32/msvc15/FirebirdCommon.props b/builds/win32/msvc15/FirebirdCommon.props index 617e13a39b..18bf022691 100644 --- a/builds/win32/msvc15/FirebirdCommon.props +++ b/builds/win32/msvc15/FirebirdCommon.props @@ -28,6 +28,7 @@ false true _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4715 true diff --git a/src/common/CvtFormat.cpp b/src/common/CvtFormat.cpp index 92c303cd15..1e89c09c8c 100644 --- a/src/common/CvtFormat.cpp +++ b/src/common/CvtFormat.cpp @@ -1124,6 +1124,7 @@ namespace return twelveHours == 12 ? twelveHours : 12 + twelveHours; cb->err(Arg::Gds(isc_incorrect_hours_period) << string(period.data(), period.length())); + return 0; // suppress compiler warning/error } constexpr int roundYearPatternImplementation(int parsedRRValue, int currentYear) From 3bdaddfb5c89ac2ecd2c563f7b2492ae0fad4e55 Mon Sep 17 00:00:00 2001 From: Dmitry <47507037+DmEye@users.noreply.github.com> Date: Thu, 26 Dec 2024 14:41:16 +0300 Subject: [PATCH 13/49] Fix linux warnings Wformat (#8361) * Fix linux warnings Wformat * Remove duplication defines --- src/yvalve/gds.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index a1f1370818..7071c8cec2 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -975,7 +975,7 @@ static SLONG safe_interpret(char* const s, const FB_SIZE_T bufsize, } if (!found) { - sprintf(s, "unknown ISC error %ld", (SLONG) code); // TXNN + sprintf(s, "unknown ISC error %" SLONGFORMAT, (SLONG) code); // TXNN } } } @@ -1000,11 +1000,11 @@ static SLONG safe_interpret(char* const s, const FB_SIZE_T bufsize, break; case isc_arg_dos: - sprintf(s, "unknown dos error %ld", (SLONG) code); // TXNN + sprintf(s, "unknown dos error %" SLONGFORMAT, (SLONG) code); // TXNN break; case isc_arg_next_mach: - sprintf(s, "next/mach error %ld", (SLONG) code); // AP + sprintf(s, "next/mach error %" SLONGFORMAT, (SLONG) code); // AP break; case isc_arg_win32: @@ -1016,7 +1016,7 @@ static SLONG safe_interpret(char* const s, const FB_SIZE_T bufsize, s, bufsize, NULL)) #endif { - sprintf(s, "unknown Win32 error %ld", (SLONG) code); // TXNN + sprintf(s, "unknown Win32 error %" SLONGFORMAT, (SLONG) code); // TXNN } break; From 2423a1487b0b9751f0dd97ce79bcba1d92551a7a Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 26 Dec 2024 17:24:37 +0300 Subject: [PATCH 14/49] Compare signed/unsigned warnings --- src/common/CvtFormat.cpp | 10 +++++----- src/common/dsc.cpp | 10 +++++----- src/common/dsc.h | 2 +- src/common/dsc_proto.h | 2 +- src/jrd/SysFunction.cpp | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/common/CvtFormat.cpp b/src/common/CvtFormat.cpp index 1e89c09c8c..c0485e2087 100644 --- a/src/common/CvtFormat.cpp +++ b/src/common/CvtFormat.cpp @@ -70,7 +70,7 @@ namespace } } - bool contains(const char* value, USHORT& outTimezoneId, int& outParsedTimezoneLength) + bool contains(const char* value, USHORT& outTimezoneId, unsigned int& outParsedTimezoneLength) { const TrieNode* currentNode = m_root; FB_SIZE_T valueLength = fb_strlen(value); @@ -97,7 +97,7 @@ namespace TrieNode* currentNode = m_root; FB_SIZE_T valueLength = fb_strlen(value); - for (int i = 0; i < valueLength; i++) + for (unsigned int i = 0; i < valueLength; i++) { int index = calculateIndex(value[i]); @@ -460,7 +460,7 @@ namespace patternStr = std::string_view(format + formatStart, offset - formatStart + 1); bool isFound = false; - for (int j = 0; j < PatternsSize; j++) + for (unsigned int j = 0; j < PatternsSize; j++) { if (!strncmp(patterns[j], patternStr.data(), patternStr.length())) { @@ -1542,7 +1542,7 @@ namespace } case Format::TZR: { - int parsedTimezoneNameLength = 0; + unsigned int parsedTimezoneNameLength = 0; const bool timezoneNameIsCorrect = timeZoneTrie().contains(str + strOffset, outTimezoneId, parsedTimezoneNameLength); if (!timezoneNameIsCorrect) status_exception::raise(Arg::Gds(isc_invalid_timezone_region) << string(str + strOffset, parsedTimezoneNameLength)); @@ -1669,7 +1669,7 @@ ISC_TIMESTAMP_TZ CVT_format_string_to_datetime(const dsc* desc, const Firebird:: stringUpper[i] = toupper(sourceString[i]); string formatUpper(format.length(), '\0'); - for (int i = 0; i < format.length(); i++) + for (unsigned int i = 0; i < format.length(); i++) formatUpper[i] = toupper(format[i]); StringToDateTimeData cvtData; diff --git a/src/common/dsc.cpp b/src/common/dsc.cpp index 44f3249506..ee867be24b 100644 --- a/src/common/dsc.cpp +++ b/src/common/dsc.cpp @@ -1356,7 +1356,7 @@ static bool validate_dsc_tables(); -int dsc::getStringLength() const +USHORT dsc::getStringLength() const { return DSC_string_length(this); } @@ -1533,7 +1533,7 @@ bool DSC_make_descriptor(DSC* desc, } -int DSC_string_length(const dsc* desc) +USHORT DSC_string_length(const dsc* desc) { /************************************** * @@ -1561,10 +1561,10 @@ int DSC_string_length(const dsc* desc) return desc->dsc_length - sizeof(USHORT); default: if (!DTYPE_IS_EXACT(desc->dsc_dtype) || desc->dsc_scale == 0) - return (int) _DSC_convert_to_text_length[desc->dsc_dtype]; + return _DSC_convert_to_text_length[desc->dsc_dtype]; if (desc->dsc_scale < 0) - return (int) _DSC_convert_to_text_length[desc->dsc_dtype] + 1; - return (int) _DSC_convert_to_text_length[desc->dsc_dtype] + desc->dsc_scale; + return _DSC_convert_to_text_length[desc->dsc_dtype] + 1; + return _DSC_convert_to_text_length[desc->dsc_dtype] + desc->dsc_scale; } } diff --git a/src/common/dsc.h b/src/common/dsc.h index d536102443..b39779b7a3 100644 --- a/src/common/dsc.h +++ b/src/common/dsc.h @@ -499,7 +499,7 @@ typedef struct dsc dsc_address = address; } - int getStringLength() const; + USHORT getStringLength() const; operator Ods::Descriptor() const { diff --git a/src/common/dsc_proto.h b/src/common/dsc_proto.h index 1664edabed..b8372d1abf 100644 --- a/src/common/dsc_proto.h +++ b/src/common/dsc_proto.h @@ -26,7 +26,7 @@ #include "../common/dsc.h" -int DSC_string_length(const struct dsc*); +USHORT DSC_string_length(const struct dsc*); const TEXT* DSC_dtype_tostring(UCHAR); void DSC_get_dtype_name(const dsc*, TEXT*, USHORT); bool DSC_make_descriptor(dsc*, USHORT, SSHORT, diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 4bf7b03a27..027fa1a1ae 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -2626,7 +2626,7 @@ dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValue Arg::Str(function->name)); } - for (int i = 0; i < Uuid::STR_LEN; ++i) + for (unsigned int i = 0; i < Uuid::STR_LEN; ++i) { if (i == 8 || i == 13 || i == 18 || i == 23) { From b1ef0e3c52b0ea58badfe2038e17d8c6de0b7c5d Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Thu, 26 Dec 2024 16:43:28 +0300 Subject: [PATCH 15/49] Fix mismatched-new-delete warnings --- src/common/call_service.cpp | 2 +- src/common/security.h | 27 +-------------------------- src/dsql/DdlNodes.epp | 4 ++-- src/jrd/UserManagement.cpp | 6 +++--- src/jrd/UserManagement.h | 4 ++-- src/utilities/gsec/gsec.cpp | 8 ++++---- src/yvalve/alt.cpp | 6 +++--- 7 files changed, 16 insertions(+), 41 deletions(-) diff --git a/src/common/call_service.cpp b/src/common/call_service.cpp index 72034949c7..e701b7d121 100644 --- a/src/common/call_service.cpp +++ b/src/common/call_service.cpp @@ -465,7 +465,7 @@ void callRemoteServiceManager(ISC_STATUS* status, { const char request[] = {isc_info_svc_get_users}; int startQuery = 0; - Auth::StackUserData uData; + Auth::UserData uData; for (;;) { diff --git a/src/common/security.h b/src/common/security.h index 8495efcee2..2851816d2e 100644 --- a/src/common/security.h +++ b/src/common/security.h @@ -155,7 +155,7 @@ private: typedef Firebird::Array AuthenticationBlock; -class UserData : +class UserData final : public Firebird::VersionedIface > { public: @@ -233,31 +233,6 @@ public: IntField u, g; }; -class StackUserData final : public UserData -{ -public: - void* operator new(size_t, void* memory) noexcept - { - return memory; - } -}; - -class DynamicUserData final : public UserData -{ -public: -#ifdef DEBUG_GDS_ALLOC - void* operator new(size_t size, Firebird::MemoryPool& pool, const char* fileName, int line) - { - return pool.allocate(size, fileName, line); - } -#else // DEBUG_GDS_ALLOC - void* operator new(size_t size, Firebird::MemoryPool& pool) - { - return pool.allocate(size); - } -#endif // DEBUG_GDS_ALLOC -}; - class Get : public Firebird::GetPlugins { public: diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 0fa5cce0bb..e355ded4c0 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -11352,7 +11352,7 @@ void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); - Auth::DynamicUserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::DynamicUserData; + Auth::UserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::UserData; MetaName text(name); if (text.isEmpty() && mode == USER_MOD) @@ -11469,7 +11469,7 @@ void DropUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jr // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); - Auth::DynamicUserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::DynamicUserData; + Auth::UserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::UserData; string text = name.c_str(); diff --git a/src/jrd/UserManagement.cpp b/src/jrd/UserManagement.cpp index d918fde3de..d09b8a45eb 100644 --- a/src/jrd/UserManagement.cpp +++ b/src/jrd/UserManagement.cpp @@ -314,7 +314,7 @@ void UserManagement::commit() } } -USHORT UserManagement::put(Auth::DynamicUserData* userData) +USHORT UserManagement::put(Auth::UserData* userData) { const FB_SIZE_T ret = commands.getCount(); if (ret > MAX_USHORT) @@ -379,7 +379,7 @@ void UserManagement::execute(USHORT id) if (command->attr.entered() || command->op == Auth::ADDMOD_OPER) { - Auth::StackUserData cmd; + Auth::UserData cmd; cmd.op = Auth::DIS_OPER; cmd.user.set(&statusWrapper, command->userName()->get()); check(&statusWrapper); @@ -614,7 +614,7 @@ RecordBuffer* UserManagement::getList(thread_db* tdbb, jrd_rel* relation) for (FillSnapshot fillSnapshot(this); fillSnapshot.pos < managers.getCount(); ++fillSnapshot.pos) { - Auth::StackUserData u; + Auth::UserData u; u.op = Auth::DIS_OPER; *ec = managers[fillSnapshot.pos].second->execute(currentWrapper, &u, &fillSnapshot); diff --git a/src/jrd/UserManagement.h b/src/jrd/UserManagement.h index 7d7895e4b8..3334d19155 100644 --- a/src/jrd/UserManagement.h +++ b/src/jrd/UserManagement.h @@ -59,7 +59,7 @@ public: ~UserManagement(); // store userData for DFW-time processing - USHORT put(Auth::DynamicUserData* userData); + USHORT put(Auth::UserData* userData); // execute command with ID void execute(USHORT id); // commit transaction in security database @@ -71,7 +71,7 @@ public: private: thread_db* threadDbb; - Firebird::HalfStaticArray commands; + Firebird::HalfStaticArray commands; typedef Firebird::Pair > Manager; Firebird::ObjectsArray managers; Firebird::NoCaseString plugins; diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index e29f3c9ef0..f61efade02 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -308,7 +308,7 @@ namespace { public Firebird::AutoIface > { public: - explicit Callback(StackUserData* pu) + explicit Callback(UserData* pu) : u(pu) { } @@ -329,7 +329,7 @@ namespace { } private: - StackUserData* u; + UserData* u; }; } // anonymous namespace @@ -356,7 +356,7 @@ int gsec(Firebird::UtilSvc* uSvc) tsec* tdsec = &tsecInstance; tsec::putSpecific(tdsec); - StackUserData u; + UserData u; tdsec->tsec_user_data = &u; Firebird::LocalStatus lsManager; @@ -549,7 +549,7 @@ int gsec(Firebird::UtilSvc* uSvc) if (user_data->operation() == MOD_OPER && user_data->userName()->entered() && (fieldSet(&user_data->u) || fieldSet(&user_data->g) || fieldSet(&user_data->group))) { - StackUserData u; + UserData u; u.op = DIS_OPER; u.user.set(&statusWrapper, user_data->userName()->get()); check(&statusWrapper); diff --git a/src/yvalve/alt.cpp b/src/yvalve/alt.cpp index 4f2e2d2ffb..2b6e233228 100644 --- a/src/yvalve/alt.cpp +++ b/src/yvalve/alt.cpp @@ -862,7 +862,7 @@ ISC_STATUS API_ROUTINE isc_add_user(ISC_STATUS* status, const USER_SEC_DATA* inp * Return > 0 if any error occurs. * **************************************/ - Auth::StackUserData userInfo; + Auth::UserData userInfo; userInfo.op = Auth::ADD_OPER; Firebird::LocalStatus s; Firebird::CheckStatusWrapper statusWrapper(&s); @@ -925,7 +925,7 @@ ISC_STATUS API_ROUTINE isc_delete_user(ISC_STATUS* status, const USER_SEC_DATA* * Return > 0 if any error occurs. * **************************************/ - Auth::StackUserData userInfo; + Auth::UserData userInfo; userInfo.op = Auth::DEL_OPER; Firebird::LocalStatus s; Firebird::CheckStatusWrapper statusWrapper(&s); @@ -970,7 +970,7 @@ ISC_STATUS API_ROUTINE isc_modify_user(ISC_STATUS* status, const USER_SEC_DATA* * Return > 0 if any error occurs. * **************************************/ - Auth::StackUserData userInfo; + Auth::UserData userInfo; userInfo.op = Auth::MOD_OPER; Firebird::LocalStatus s; Firebird::CheckStatusWrapper statusWrapper(&s); From 125140103dfc296b23f79b3917353578d870c26c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 26 Dec 2024 20:16:32 +0000 Subject: [PATCH 16/49] 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 4d8bb99db4..7f93b16377 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:560 + FORMAL BUILD NUMBER:564 */ -#define PRODUCT_VER_STRING "6.0.0.560" -#define FILE_VER_STRING "WI-T6.0.0.560" -#define LICENSE_VER_STRING "WI-T6.0.0.560" -#define FILE_VER_NUMBER 6, 0, 0, 560 +#define PRODUCT_VER_STRING "6.0.0.564" +#define FILE_VER_STRING "WI-T6.0.0.564" +#define LICENSE_VER_STRING "WI-T6.0.0.564" +#define FILE_VER_NUMBER 6, 0, 0, 564 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "560" +#define FB_BUILD_NO "564" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 0debdd3468..79762cc41d 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=560 +BuildNum=564 NowAt=`pwd` cd `dirname $0` From f0740d2a3282ed92a87b8e0547139ba8efe61173 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sat, 28 Dec 2024 09:27:34 +0300 Subject: [PATCH 17/49] Wipe out multi-file database support (#8047) * Wipe out multi-file database support * Fix (fingers crossed) Windows build, some more cleanup and code simplification * Fix gstat build on Windows and also Android/MacOS builds --- src/burp/restore.epp | 108 +++++++---- src/dsql/DdlNodes.epp | 104 +++-------- src/dsql/DdlNodes.h | 57 +----- src/dsql/parse.y | 84 +-------- src/jrd/cch.cpp | 9 - src/jrd/dfw.epp | 219 ++-------------------- src/jrd/jrd.cpp | 33 ++-- src/jrd/met.epp | 1 - src/jrd/ods.h | 4 +- src/jrd/os/pio.h | 10 - src/jrd/os/pio_proto.h | 1 - src/jrd/os/posix/unix.cpp | 178 ++++++------------ src/jrd/os/win32/winnt.cpp | 225 ++++++++--------------- src/jrd/pag.cpp | 294 +++--------------------------- src/jrd/pag_proto.h | 3 +- src/jrd/sdw.cpp | 204 +-------------------- src/jrd/sdw_proto.h | 1 - src/jrd/tra.h | 1 - src/jrd/vio.cpp | 97 +++++----- src/utilities/gstat/dba.epp | 200 +++----------------- src/utilities/gstat/ppg.cpp | 11 -- src/utilities/rebuild/rebuild.cpp | 44 +---- 22 files changed, 384 insertions(+), 1504 deletions(-) diff --git a/src/burp/restore.epp b/src/burp/restore.epp index f992afd2b2..bdd59a7a19 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -4204,53 +4204,85 @@ bool get_files(BurpGlobals* tdgbl) att_type attribute; scan_attr_t scan_next_attr; - STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1) - X IN RDB$FILES - X.RDB$FILE_FLAGS = 0; + BASED_ON RDB$FILES.RDB$FILE_NAME filename = ""; + SSHORT flags = 0, sequence = 0, number = 0; + SLONG start = 0, length = 0; - skip_init(&scan_next_attr); - while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + { + switch (attribute) { - switch (attribute) - { - case att_file_filename: - GET_TEXT(X.RDB$FILE_NAME); - BURP_verbose (116, X.RDB$FILE_NAME); - // msg 116 restoring file %s - break; + case att_file_filename: + GET_TEXT(filename); + break; - case att_file_sequence: - X.RDB$FILE_SEQUENCE = (USHORT) get_int32(tdgbl); - break; + case att_file_sequence: + sequence = (SSHORT) get_int32(tdgbl); + break; - case att_file_start: - X.RDB$FILE_START = get_int32(tdgbl); - break; + case att_file_start: + start = get_int32(tdgbl); + break; - case att_file_length: - X.RDB$FILE_LENGTH = get_int32(tdgbl); - break; + case att_file_length: + length = get_int32(tdgbl); + break; - case att_file_flags: - X.RDB$FILE_FLAGS |= get_int32(tdgbl); - break; + case att_file_flags: + flags |= (SSHORT) get_int32(tdgbl); + break; - case att_shadow_number: - X.RDB$SHADOW_NUMBER = (USHORT) get_int32(tdgbl); - if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER) - X.RDB$FILE_FLAGS |= FILE_inactive; - break; + case att_shadow_number: + number = (SSHORT) get_int32(tdgbl); + if (tdgbl->gbl_sw_kill && number) + flags |= FILE_inactive; + break; - default: - bad_attribute(scan_next_attr, attribute, 85); - // msg 85 file - break; - } + default: + bad_attribute(scan_next_attr, attribute, 85); + // msg 85 file + break; } - END_STORE; - ON_ERROR - general_on_error (); - END_ERROR; + } + + const bool multiFileSupport = (tdgbl->runtimeODS <= DB_VERSION_DDL13_1); + + if ((multiFileSupport || !sequence) && filename[0]) + { + BURP_verbose (116, filename); + // msg 116 restoring file %s + + STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1) + X IN RDB$FILES + + strncpy(X.RDB$FILE_NAME, filename, sizeof(X.RDB$FILE_NAME)); + X.RDB$FILE_FLAGS = flags; + X.RDB$SHADOW_NUMBER = number; + + if (multiFileSupport) + { + X.RDB$FILE_SEQUENCE.NULL = FALSE; + X.RDB$FILE_SEQUENCE = sequence; + + X.RDB$FILE_START.NULL = FALSE; + X.RDB$FILE_START = start; + + X.RDB$FILE_LENGTH.NULL = FALSE; + X.RDB$FILE_LENGTH = length; + } + else + { + X.RDB$FILE_SEQUENCE.NULL = TRUE; + X.RDB$FILE_START.NULL = TRUE; + X.RDB$FILE_LENGTH.NULL = TRUE; + } + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + } return true; } diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index e355ded4c0..5a3cd5eed3 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -80,9 +80,6 @@ static void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* dsql_fld* field, ValueSourceClause* clause, string& source, BlrDebugWriter::BlrData& value); static void deleteKeyConstraint(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName, const MetaName& constraintName, const MetaName& indexName); -static void defineFile(thread_db* tdbb, jrd_tra* transaction, SLONG shadowNumber, bool manualShadow, - bool conditionalShadow, SLONG& dbAlloc, - const PathName& name, SLONG start, SLONG length); static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName, const MetaName& fieldName); static bool isItSqlRole(thread_db* tdbb, jrd_tra* transaction, const MetaName& inputName, @@ -490,46 +487,6 @@ static void deleteKeyConstraint(thread_db* tdbb, jrd_tra* transaction, } } -// Define a database or shadow file. -static void defineFile(thread_db* tdbb, jrd_tra* transaction, SLONG shadowNumber, bool manualShadow, - bool conditionalShadow, SLONG& dbAlloc, const PathName& name, SLONG start, SLONG length) -{ - PathName expandedName = name; - - if (!ISC_expand_filename(expandedName, false)) - status_exception::raise(Arg::PrivateDyn(231)); // File name is invalid. - - if (tdbb->getDatabase()->dbb_filename == expandedName) - status_exception::raise(Arg::PrivateDyn(166)); - - AutoCacheRequest request(tdbb, drq_l_files, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - FIRST 1 X IN RDB$FILES - WITH X.RDB$FILE_NAME EQ expandedName.c_str() - { - status_exception::raise(Arg::PrivateDyn(166)); - } - END_FOR - - request.reset(tdbb, drq_s_files, DYN_REQUESTS); - - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - X IN RDB$FILES - { - expandedName.copyTo(X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME)); - X.RDB$SHADOW_NUMBER = shadowNumber; - X.RDB$FILE_FLAGS = (manualShadow ? FILE_manual : 0) | - (conditionalShadow ? FILE_conditional : 0); - - dbAlloc = MAX(dbAlloc, start); - X.RDB$FILE_START = dbAlloc; - X.RDB$FILE_LENGTH = length; - dbAlloc += length; - } - END_STORE -} - // Checks to see if the given field already exists in a relation. static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName, const MetaName& fieldName) @@ -10462,7 +10419,7 @@ string CreateShadowNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, number); NODE_PRINT(printer, manual); NODE_PRINT(printer, conditional); - NODE_PRINT(printer, files); + NODE_PRINT(printer, fileName); return "CreateShadowNode"; } @@ -10486,7 +10443,7 @@ void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScrat // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); - // If a shadow set identified by the shadow number already exists return error. + // If a shadow set identified by the shadow number already exists return error AutoCacheRequest request(tdbb, drq_l_shadow, DYN_REQUESTS); @@ -10502,25 +10459,34 @@ void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScrat } END_FOR - SLONG start = 0; + PathName expandedName = fileName.ToPathName(); - for (NestConst* i = files.begin(); i != files.end(); ++i) + if (!ISC_expand_filename(expandedName, false)) + status_exception::raise(Arg::PrivateDyn(231)); // File name is invalid + + if (tdbb->getDatabase()->dbb_filename == expandedName) + status_exception::raise(Arg::PrivateDyn(166)); + + request.reset(tdbb, drq_l_files, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FIRST 1 X IN RDB$FILES + WITH X.RDB$FILE_NAME EQ expandedName.c_str() { - bool first = i == files.begin(); - DbFileClause* file = *i; - - if (!first && i[-1]->length == 0 && file->start == 0) - { - // Preceding file did not specify length, so %s must include starting page number - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_file_length_err) << file->name); - } - - defineFile(tdbb, transaction, number, manual && first, conditional && first, - start, file->name.c_str(), file->start, file->length); + status_exception::raise(Arg::PrivateDyn(166)); } + END_FOR + + request.reset(tdbb, drq_s_files, DYN_REQUESTS); + + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + X IN RDB$FILES + { + expandedName.copyTo(X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME)); + X.RDB$SHADOW_NUMBER = number; + X.RDB$FILE_FLAGS = (manual ? FILE_manual : 0) | (conditional ? FILE_conditional : 0); + } + END_STORE savePoint.release(); // everything is ok } @@ -12742,13 +12708,11 @@ string AlterDatabaseNode::internalPrint(NodePrinter& printer) const DdlNode::internalPrint(printer); NODE_PRINT(printer, create); - NODE_PRINT(printer, createLength); NODE_PRINT(printer, linger); NODE_PRINT(printer, clauses); NODE_PRINT(printer, differenceFile); NODE_PRINT(printer, setDefaultCharSet); NODE_PRINT(printer, setDefaultCollation); - NODE_PRINT(printer, files); NODE_PRINT(printer, cryptPlugin); NODE_PRINT(printer, keyName); @@ -12924,18 +12888,6 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc } } - SLONG dbAlloc = PageSpace::maxAlloc(tdbb->getDatabase()); - SLONG start = create ? createLength + 1 : 0; - for (NestConst* i = files.begin(); i != files.end(); ++i) - { - DbFileClause* file = *i; - - start = MAX(start, file->start); - defineFile(tdbb, transaction, 0, false, false, dbAlloc, - file->name.c_str(), start, file->length); - start += file->length; - } - if (differenceFile.hasData()) defineDifference(tdbb, transaction, differenceFile.c_str()); @@ -13064,7 +13016,6 @@ void AlterDatabaseNode::changeBackupMode(thread_db* tdbb, jrd_tra* transaction, X IN RDB$FILES { X.RDB$FILE_FLAGS = FILE_difference | FILE_backing_up; - X.RDB$FILE_START = 0; } END_STORE @@ -13116,7 +13067,6 @@ void AlterDatabaseNode::defineDifference(thread_db* tdbb, jrd_tra* transaction, strcpy(FIL.RDB$FILE_NAME, file.c_str()); FIL.RDB$FILE_FLAGS = FILE_difference; - FIL.RDB$FILE_START = 0; } END_STORE } diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index ffa46aa959..7f3f1b15df 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -101,40 +101,6 @@ public: }; -class DbFileClause : public Printable -{ -public: - DbFileClause(MemoryPool& p, const DbFileClause& o) - : name(p, o.name), - start(o.start), - length(o.length) - { - } - - explicit DbFileClause(MemoryPool& p, const Firebird::string& aName) - : name(p, aName), - start(0), - length(0) - { - } - -public: - virtual Firebird::string internalPrint(NodePrinter& printer) const - { - NODE_PRINT(printer, name); - NODE_PRINT(printer, start); - NODE_PRINT(printer, length); - - return "DbFileClause"; - } - -public: - Firebird::string name; // File name - SLONG start; // Starting page - SLONG length; // File length in pages -}; - - class ExternalClause : public Printable { public: @@ -1967,12 +1933,13 @@ public: class CreateShadowNode : public DdlNode { public: - CreateShadowNode(MemoryPool& p, const SSHORT aNumber) + CreateShadowNode(MemoryPool& p, SSHORT aNumber, bool aManual, bool aConditional, + const Firebird::string& aFileName) : DdlNode(p), number(aNumber), - manual(false), - conditional(false), - files(p) + manual(aManual), + conditional(aConditional), + fileName(p, aFileName) { } @@ -1996,7 +1963,7 @@ public: SSHORT number; bool manual; bool conditional; - Firebird::Array > files; + Firebird::string fileName; bool createIfNotExistsOnly = false; }; @@ -2425,14 +2392,9 @@ public: public: AlterDatabaseNode(MemoryPool& p) : DdlNode(p), - create(false), - createLength(0), - linger(-1), - clauses(0), differenceFile(p), setDefaultCharSet(p), setDefaultCollation(p), - files(p), cryptPlugin(p), keyName(p), pubTables(p) @@ -2468,13 +2430,12 @@ private: void checkClauses(thread_db* tdbb); public: - bool create; // Is the node created with a CREATE DATABASE command? - SLONG createLength, linger; - unsigned clauses; + bool create = false; // Is the node created with a CREATE DATABASE command? + SLONG linger = -1; + unsigned clauses = 0; Firebird::string differenceFile; MetaName setDefaultCharSet; MetaName setDefaultCollation; - Firebird::Array > files; MetaName cryptPlugin; MetaName keyName; Firebird::TriState ssDefiner; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index bcfbd50833..c44adee3e8 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -767,8 +767,6 @@ using namespace Firebird; Firebird::string* stringPtr; Jrd::IntlString* intlStringPtr; Jrd::Lim64String* lim64ptr; - Jrd::DbFileClause* dbFileClause; - Firebird::Array >* dbFilesClause; Jrd::ExternalClause* externalClause; Firebird::NonPooledPair* namedArgument; Firebird::NonPooledPair*, Jrd::ValueListNode*>* namedArguments; @@ -1807,16 +1805,8 @@ index_condition_opt // CREATE SHADOW %type shadow_clause shadow_clause - : pos_short_integer manual_auto conditional utf_string first_file_length - { - $$ = newNode($1); - $$->manual = $2; - $$->conditional = $3; - $$->files.add(newNode(*$4)); - $$->files.front()->length = $5; - } - sec_shadow_files(NOTRIAL(&$6->files)) - { $$ = $6; } + : pos_short_integer manual_auto conditional utf_string + { $$ = newNode($1, $2, $3, *$4); } ; %type manual_auto @@ -1832,24 +1822,6 @@ conditional | CONDITIONAL { $$ = true; } ; -%type first_file_length -first_file_length - : /* nothing */ { $$ = 0; } - | LENGTH equals long_integer page_noise { $$ = $3; } - ; - -%type sec_shadow_files() -sec_shadow_files($dbFilesClause) - : // nothing - | db_file_list($dbFilesClause) - ; - -%type db_file_list() -db_file_list($dbFilesClause) - : db_file { $dbFilesClause->add($1); } - | db_file_list db_file { $dbFilesClause->add($2); } - ; - // CREATE DOMAIN @@ -2279,8 +2251,6 @@ db_initial_option($alterDatabaseNode) | ROLE utf_string | PASSWORD utf_string | SET NAMES utf_string - | LENGTH equals long_integer page_noise - { $alterDatabaseNode->createLength = $3; } ; %type db_rem_desc1() @@ -2297,9 +2267,7 @@ db_rem_desc($alterDatabaseNode) %type db_rem_option() db_rem_option($alterDatabaseNode) - : db_file - { $alterDatabaseNode->files.add($1); } - | DEFAULT CHARACTER SET symbol_character_set_name + : DEFAULT CHARACTER SET symbol_character_set_name { $alterDatabaseNode->setDefaultCharSet = *$4; } | DEFAULT CHARACTER SET symbol_character_set_name COLLATION symbol_collation_name { @@ -2310,49 +2278,6 @@ db_rem_option($alterDatabaseNode) { $alterDatabaseNode->differenceFile = *$3; } ; -%type db_file -db_file - : FILE utf_string - { - DbFileClause* clause = newNode(*$2); - $$ = clause; - } - file_desc1($3) - { $$ = $3; } - ; - -%type file_desc1() -file_desc1($dbFileClause) - : // nothing - | file_desc($dbFileClause) - ; - -%type file_desc() -file_desc($dbFileClause) - : file_clause($dbFileClause) - | file_desc file_clause($dbFileClause) - ; - -%type file_clause() -file_clause($dbFileClause) - : STARTING file_clause_noise long_integer - { $dbFileClause->start = $3; } - | LENGTH equals long_integer page_noise - { $dbFileClause->length = $3; } - ; - -file_clause_noise - : // nothing - | AT - | AT PAGE - ; - -page_noise - : // nothing - | PAGE - | PAGES - ; - // CREATE TABLE @@ -4770,8 +4695,7 @@ alter_db($alterDatabaseNode) %type db_alter_clause() db_alter_clause($alterDatabaseNode) - : ADD db_file_list(NOTRIAL(&$alterDatabaseNode->files)) - | ADD DIFFERENCE FILE utf_string + : ADD DIFFERENCE FILE utf_string { $alterDatabaseNode->differenceFile = *$4; } | DROP DIFFERENCE FILE { $alterDatabaseNode->clauses |= AlterDatabaseNode::CLAUSE_DROP_DIFFERENCE; } diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index c966e4ea7e..6f097e8e54 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -2489,15 +2489,6 @@ bool CCH_write_all_shadows(thread_db* tdbb, Shadow* shadow, BufferDesc* bdb, Ods PAG_add_header_entry(tdbb, header, HDR_root_file_name, (USHORT) strlen((const char*) q), q); - jrd_file* next_file = shadow_file->fil_next; - if (next_file) - { - q = (UCHAR *) next_file->fil_string; - const SLONG last = next_file->fil_min_page - 1; - PAG_add_header_entry(tdbb, header, HDR_file, (USHORT) strlen((const char*) q), q); - PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(last), (const UCHAR*) &last); - } - header->hdr_flags |= hdr_active_shadow; header->hdr_header.pag_pageno = bdb->bdb_page.getPageNum(); } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 2609eef97e..1edc4ff4ee 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -56,7 +56,7 @@ * BUGCHECK(291) took place. To avoid that issue, it was decided not to modify data * in system transaction. An exception is RDB$FORMATS relation, which is always modified * by transaction zero. Also an aspect of 'dirty' access from system transaction was - * taken into an account in add_file(), make_version() and create_index(). + * taken into an account in make_version() and create_index(). * */ @@ -449,7 +449,6 @@ private: * *================================================================== */ -static bool add_file(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool add_shadow(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_shadow(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool compute_security(thread_db*, SSHORT, DeferredWork*, jrd_tra*); @@ -1272,7 +1271,6 @@ namespace static const deferred_task task_table[] = { - { dfw_add_file, add_file }, { dfw_add_shadow, add_shadow }, { dfw_delete_index, modify_index }, { dfw_delete_rfr, delete_rfr }, @@ -1876,133 +1874,6 @@ void DFW_update_index(const TEXT* name, USHORT id, const SelectivityList& select } -static bool add_file(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) -{ -/************************************** - * - * a d d _ f i l e - * - ************************************** - * - * Functional description - * Add a file to a database. - * This file could be a regular database - * file or a shadow file. Either way we - * require exclusive access to the database. - * - **************************************/ - USHORT section, shadow_number; - SLONG start, min_start; - - SET_TDBB(tdbb); - Database* const dbb = tdbb->getDatabase(); - - switch (phase) - { - case 0: - CCH_release_exclusive(tdbb); - return false; - - case 1: - case 2: - return true; - - case 3: - if (!CCH_exclusive(tdbb, LCK_EX, WAIT_PERIOD, NULL)) - raiseDatabaseInUseError(true); - return true; - - case 4: - CCH_flush(tdbb, FLUSH_FINI, 0); - start = PageSpace::maxAlloc(dbb) + 1; - AutoRequest handle; - AutoRequest handle2; - - // Check the file name for node name. This has already - // been done for shadows in add_shadow() - - if (work->dfw_type != dfw_add_shadow) { - check_filename(work->dfw_name, true); - } - - // User transaction may be safely used instead of system, cause - // we requested and got exclusive database access. AP-2008. - - // get any files to extend into - - FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) X IN RDB$FILES - WITH X.RDB$FILE_NAME EQ work->dfw_name.c_str() - // First expand the file name This has already been done - // for shadows in add_shadow ()) - if (work->dfw_type != dfw_add_shadow) - { - MODIFY X USING - ISC_expand_filename(X.RDB$FILE_NAME, 0, - X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME), false); - END_MODIFY - } - - // Check the previous file length - FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) - FIRST 1 Y IN RDB$FILES - WITH Y.RDB$SHADOW_NUMBER EQ X.RDB$SHADOW_NUMBER - AND Y.RDB$FILE_SEQUENCE NOT MISSING - SORTED BY DESCENDING Y.RDB$FILE_SEQUENCE - { - if (!Y.RDB$FILE_START.NULL && !Y.RDB$FILE_LENGTH.NULL) - { - min_start = Y.RDB$FILE_START + (Y.RDB$FILE_LENGTH ? Y.RDB$FILE_LENGTH : 1); - start = MAX(min_start, start); - } - } - END_FOR - - // If there is no starting position specified, or if it is - // too low a value, raise the error. - if (X.RDB$FILE_START < start) - { - ERR_post(Arg::Gds(isc_file_starting_page_err) << - Arg::Str(X.RDB$FILE_NAME) << Arg::Num(start)); - } - - start = X.RDB$FILE_START; - - shadow_number = X.RDB$SHADOW_NUMBER; - if ((shadow_number && - (section = SDW_add_file(tdbb, X.RDB$FILE_NAME, start, shadow_number))) || - (section = PAG_add_file(tdbb, X.RDB$FILE_NAME, start))) - { - MODIFY X USING - X.RDB$FILE_SEQUENCE = section; - X.RDB$FILE_START = start; - END_MODIFY - } - END_FOR - - if (section) - { - handle.reset(); - section--; - FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) X IN RDB$FILES - WITH X.RDB$FILE_SEQUENCE EQ section - AND X.RDB$SHADOW_NUMBER EQ shadow_number - { - MODIFY X USING - X.RDB$FILE_LENGTH = start - X.RDB$FILE_START; - END_MODIFY - } - END_FOR - } - - CCH_release_exclusive(tdbb); - break; - } - - return false; -} - - - static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** @@ -2053,11 +1924,11 @@ static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr finished = false; handle.reset(); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) - F IN RDB$FILES - WITH F.RDB$FILE_NAME EQ work->dfw_name.c_str() - + F IN RDB$FILES WITH F.RDB$FILE_NAME EQ work->dfw_name.c_str() + { expanded_fname = F.RDB$FILE_NAME; ISC_expand_filename(expanded_fname, false); + MODIFY F USING expanded_fname.copyTo(F.RDB$FILE_NAME, sizeof(F.RDB$FILE_NAME)); END_MODIFY @@ -2066,94 +1937,34 @@ static bool add_shadow(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr { if ((F.RDB$SHADOW_NUMBER == shadow->sdw_number) && !(shadow->sdw_flags & SDW_IGNORE)) { - if (F.RDB$FILE_FLAGS & FILE_shadow) - { - // This is the case of a bogus duplicate posted - // work when we added a multi-file shadow - finished = true; - } - else if (shadow->sdw_flags & (SDW_dumped)) - { - /* Case of adding a file to a currently active - * shadow set. - * Note: as of 1995-January-31 there is - * no SQL syntax that supports this, but there - * may be GDML - */ - add_file(tdbb, 3, work, transaction); - add_file(tdbb, 4, work, transaction); - finished = true; - } - else + if (!(F.RDB$FILE_FLAGS & FILE_shadow)) { // We cannot add a file to a shadow that is still // in the process of being created. raiseDatabaseInUseError(false); } + + // This is the case of a bogus duplicate posted + // work when we added a multi-file shadow + finished = true; break; } } - + } END_FOR if (finished) return false; - // this file is part of a new shadow, so get all files for the shadow - // in order of the starting page for the file - - // Note that for a multi-file shadow, we have several pieces of - // work posted (one dfw_add_shadow for each file). Rather than - // trying to cancel the other pieces of work we ignore them - // when they arrive in this routine. - - sequence = 0; - min_page = 0; - shadow = NULL; handle.reset(); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) - X IN RDB$FILES CROSS - Y IN RDB$FILES - OVER RDB$SHADOW_NUMBER - WITH X.RDB$FILE_NAME EQ expanded_fname.c_str() - SORTED BY Y.RDB$FILE_START + F IN RDB$FILES WITH F.RDB$FILE_NAME EQ expanded_fname.c_str() { - // for the first file, create a brand new shadow; for secondary - // files that have a starting page specified, add a file - if (!sequence) - SDW_add(tdbb, Y.RDB$FILE_NAME, Y.RDB$SHADOW_NUMBER, Y.RDB$FILE_FLAGS); - else if (Y.RDB$FILE_START) - { - if (!shadow) - { - for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) - { - if ((Y.RDB$SHADOW_NUMBER == shadow->sdw_number) && - !(shadow->sdw_flags & SDW_IGNORE)) - { - break; - } - } - } + SDW_add(tdbb, F.RDB$FILE_NAME, F.RDB$SHADOW_NUMBER, F.RDB$FILE_FLAGS); - if (!shadow) - BUGCHECK(203); // msg 203 shadow block not found for extend file - - min_page = MAX((min_page + 1), (ULONG) Y.RDB$FILE_START); - add_sequence = SDW_add_file(tdbb, Y.RDB$FILE_NAME, min_page, Y.RDB$SHADOW_NUMBER); - } - - // update the sequence number and bless the file entry as being good - - if (!sequence || (Y.RDB$FILE_START && add_sequence)) - { - MODIFY Y - Y.RDB$FILE_FLAGS |= FILE_shadow; - Y.RDB$FILE_SEQUENCE = sequence; - Y.RDB$FILE_START = min_page; - END_MODIFY - sequence++; - } + MODIFY F + F.RDB$FILE_FLAGS |= FILE_shadow; + END_MODIFY } END_FOR diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 0706bbeeb6..2d0fea9563 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1301,7 +1301,7 @@ private: static void check_database(thread_db* tdbb, bool async = false); static void commit(thread_db*, jrd_tra*, const bool); -static bool drop_files(const jrd_file*); +static bool drop_file(Database*, 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); @@ -1831,7 +1831,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch dbb->dbb_crypto_manager = FB_NEW_POOL(*dbb->dbb_permanent) CryptoManager(tdbb); dbb->dbb_monitoring_data = FB_NEW_POOL(*dbb->dbb_permanent) MonitoringData(dbb); - PAG_init2(tdbb, 0); + PAG_init2(tdbb); PAG_header(tdbb, false, newForceWrite); dbb->dbb_page_manager.initTempPageSpace(tdbb); dbb->dbb_crypto_manager->attach(tdbb, attachment); @@ -3552,11 +3552,9 @@ void JAttachment::internalDropDatabase(CheckStatusWrapper* user_status) // This point on database is useless // drop the files here - bool err = drop_files(file); + bool err = drop_file(dbb, file); for (; shadow; shadow = shadow->sdw_next) - { - err = drop_files(shadow->sdw_file) || err; - } + err = drop_file(dbb, shadow->sdw_file) || err; tdbb->setDatabase(NULL); Database::destroy(dbb); @@ -6797,31 +6795,28 @@ static void commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_f } -static bool drop_files(const jrd_file* file) +static bool drop_file(Database* dbb, const jrd_file* file) { /************************************** * - * d r o p _ f i l e s + * d r o p _ f i l e * ************************************** * * Functional description - * drop a linked list of files + * Drop a file. * **************************************/ FbLocalStatus status; - for (; file; file = file->fil_next) + if (unlink(file->fil_string)) { - if (unlink(file->fil_string)) - { - ERR_build_status(&status, Arg::Gds(isc_io_error) << Arg::Str("unlink") << - Arg::Str(file->fil_string) << - Arg::Gds(isc_io_delete_err) << SYS_ERR(errno)); - Database* dbb = GET_DBB(); - PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); - iscDbLogStatus(pageSpace->file->fil_string, &status); - } + ERR_build_status(&status, Arg::Gds(isc_io_error) << Arg::Str("unlink") << + Arg::Str(file->fil_string) << + Arg::Gds(isc_io_delete_err) << SYS_ERR(errno)); + + PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); + iscDbLogStatus(pageSpace->file->fil_string, &status); } return status->getState() & IStatus::STATE_ERRORS ? true : false; diff --git a/src/jrd/met.epp b/src/jrd/met.epp index f16f2190f4..8d5efc9f1b 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -1782,7 +1782,6 @@ void MET_get_shadow_files(thread_db* tdbb, bool delete_files) FOR(REQUEST_HANDLE handle) X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER NOT MISSING AND X.RDB$SHADOW_NUMBER NE 0 - AND X.RDB$FILE_SEQUENCE EQ 0 { if ((X.RDB$FILE_FLAGS & FILE_shadow) && !(X.RDB$FILE_FLAGS & FILE_inactive)) { diff --git a/src/jrd/ods.h b/src/jrd/ods.h index 12d4071710..18ad4824e6 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -531,8 +531,8 @@ static_assert(offsetof(struct header_page, hdr_data) == 128, "hdr_data offset mi inline constexpr UCHAR HDR_end = 0; inline constexpr UCHAR HDR_root_file_name = 1; // Original name of root file -inline constexpr UCHAR HDR_file = 2; // Secondary file -inline constexpr UCHAR HDR_last_page = 3; // Last logical page number of file +//inline constexpr UCHAR HDR_file = 2; // Secondary file +//inline constexpr UCHAR HDR_last_page = 3; // Last logical page number of file inline constexpr UCHAR HDR_sweep_interval = 4; // Transactions between sweeps inline constexpr UCHAR HDR_crypt_checksum = 5; // Checksum of critical crypt parameters inline constexpr UCHAR HDR_difference_file = 6; // Delta file that is used during backup lock diff --git a/src/jrd/os/pio.h b/src/jrd/os/pio.h index f5ff6f4a17..fa594f7864 100644 --- a/src/jrd/os/pio.h +++ b/src/jrd/os/pio.h @@ -41,11 +41,6 @@ namespace Jrd { class jrd_file : public pool_alloc_rpt { public: - jrd_file* fil_next; // Next file in database - ULONG fil_min_page; // Minimum page number in file - ULONG fil_max_page; // Maximum page number in file - USHORT fil_sequence; // Sequence number of file - USHORT fil_fudge; // Fudge factor for page relocation int fil_desc; Firebird::Mutex fil_mutex; USHORT fil_flags; @@ -69,11 +64,6 @@ public: delete fil_ext_lock; } - jrd_file* fil_next; // Next file in database - ULONG fil_min_page; // Minimum page number in file - ULONG fil_max_page; // Maximum page number in file - USHORT fil_sequence; // Sequence number of file - USHORT fil_fudge; // Fudge factor for page relocation HANDLE fil_desc; // File descriptor Firebird::RWLock* fil_ext_lock; // file extend lock USHORT fil_flags; diff --git a/src/jrd/os/pio_proto.h b/src/jrd/os/pio_proto.h index d936c8aea2..8ec0ec3cde 100644 --- a/src/jrd/os/pio_proto.h +++ b/src/jrd/os/pio_proto.h @@ -36,7 +36,6 @@ namespace Ods { struct pag; } -int PIO_add_file(Jrd::thread_db*, Jrd::jrd_file*, const Firebird::PathName&, SLONG); void PIO_close(Jrd::jrd_file*); Jrd::jrd_file* PIO_create(Jrd::thread_db*, const Firebird::PathName&, const bool, const bool); diff --git a/src/jrd/os/posix/unix.cpp b/src/jrd/os/posix/unix.cpp index ba1a3220dc..76071238de 100644 --- a/src/jrd/os/posix/unix.cpp +++ b/src/jrd/os/posix/unix.cpp @@ -132,7 +132,7 @@ using namespace Firebird; static const mode_t MASK = 0660; -static jrd_file* seek_file(jrd_file*, BufferDesc*, FB_UINT64*, FbStatusVector*); +static bool seek_file(jrd_file*, BufferDesc*, FB_UINT64*, FbStatusVector*); static jrd_file* setup_file(Database*, const PathName&, int, USHORT); static void lockDatabaseFile(int& desc, const bool shareMode, const bool temporary, const char* fileName, ISC_STATUS operation); @@ -149,42 +149,8 @@ static int raw_devices_unlink_database (const PathName&); static int openFile(const Firebird::PathName&, const bool, const bool, const bool); static void maybeCloseFile(int&); -int PIO_add_file(thread_db* tdbb, jrd_file* main_file, const PathName& file_name, SLONG start) -{ -/************************************** - * - * P I O _ a d d _ f i l e - * - ************************************** - * - * Functional description - * Add a file to an existing database. Return the sequence - * number of the new file. If anything goes wrong, return a - * sequence of 0. - * NOTE: This routine does not lock any mutexes on - * its own behalf. It is assumed that mutexes will - * have been locked before entry. - * - **************************************/ - jrd_file* new_file = PIO_create(tdbb, file_name, false, false); - if (!new_file) - return 0; - new_file->fil_min_page = start; - USHORT sequence = 1; - - jrd_file* file; - for (file = main_file; file->fil_next; file = file->fil_next) - ++sequence; - - file->fil_max_page = start - 1; - file->fil_next = new_file; - - return sequence; -} - - -void PIO_close(jrd_file* main_file) +void PIO_close(jrd_file* file) { /************************************** * @@ -199,13 +165,10 @@ void PIO_close(jrd_file* main_file) * **************************************/ - for (jrd_file* file = main_file; file; file = file->fil_next) + if (file->fil_desc && file->fil_desc != -1) { - if (file->fil_desc && file->fil_desc != -1) - { - close(file->fil_desc); - file->fil_desc = -1; - } + close(file->fil_desc); + file->fil_desc = -1; } } @@ -336,7 +299,7 @@ bool PIO_expand(const TEXT* file_name, USHORT file_length, TEXT* expanded_name, } -void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, const USHORT pageSize) +void PIO_extend(thread_db* tdbb, jrd_file* file, const ULONG extPages, const USHORT pageSize) { /************************************** * @@ -348,56 +311,46 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons * Extend file by extPages pages of pageSize size. * **************************************/ + fb_assert(extPages); #if defined(HAVE_LINUX_FALLOC_H) && defined(HAVE_FALLOCATE) EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); - ULONG leftPages = extPages; - for (jrd_file* file = main_file; file && leftPages; file = file->fil_next) + if (file->fil_flags & FIL_no_fast_extend) + return; + + const ULONG filePages = PIO_get_number_of_pages(file, pageSize); + const ULONG extendBy = MIN(MAX_ULONG - filePages, extPages); + + int r; + for (r = 0; r < IO_RETRY; r++) { - const ULONG filePages = PIO_get_number_of_pages(file, pageSize); - const ULONG fileMaxPages = (file->fil_max_page == MAX_ULONG) ? - MAX_ULONG : file->fil_max_page - file->fil_min_page + 1; - if (filePages < fileMaxPages) - { - if (file->fil_flags & FIL_no_fast_extend) - return; + int err = fallocate(file->fil_desc, 0, filePages * pageSize, extendBy * pageSize); + if (err == 0) + break; - const ULONG extendBy = MIN(fileMaxPages - filePages + file->fil_fudge, leftPages); + err = errno; + if (SYSCALL_INTERRUPTED(err)) + continue; - int r; - for (r = 0; r < IO_RETRY; r++) - { - int err = fallocate(file->fil_desc, 0, filePages * pageSize, extendBy * pageSize); - if (err == 0) - break; + if (err != EOPNOTSUPP && err != ENOSYS) + unix_error("fallocate", file, isc_io_write_err); - err = errno; - if (SYSCALL_INTERRUPTED(err)) - continue; + file->fil_flags |= FIL_no_fast_extend; + return; + } - if (err != EOPNOTSUPP && err != ENOSYS) - unix_error("fallocate", file, isc_io_write_err); - - file->fil_flags |= FIL_no_fast_extend; - return; - } - - if (r == IO_RETRY) - { + if (r == IO_RETRY) + { #ifdef DEV_BUILD - fprintf(stderr, "PIO_extend: retry count exceeded\n"); - fflush(stderr); + fprintf(stderr, "PIO_extend: retry count exceeded\n"); + fflush(stderr); #endif - unix_error("fallocate_retry", file, isc_io_write_err); - } - - leftPages -= extendBy; - } + unix_error("fallocate_retry", file, isc_io_write_err); } #else - main_file->fil_flags |= FIL_no_fast_extend; + file->fil_flags |= FIL_no_fast_extend; #endif // fallocate present // not implemented @@ -405,7 +358,7 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons } -void PIO_flush(thread_db* tdbb, jrd_file* main_file) +void PIO_flush(thread_db* tdbb, jrd_file* file) { /************************************** * @@ -422,15 +375,12 @@ void PIO_flush(thread_db* tdbb, jrd_file* main_file) #ifndef SUPERSERVER_V2 EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); - MutexLockGuard guard(main_file->fil_mutex, FB_FUNCTION); + MutexLockGuard guard(file->fil_mutex, FB_FUNCTION); - for (jrd_file* file = main_file; file; file = file->fil_next) + if (file->fil_desc != -1) { - if (file->fil_desc != -1) - { - // This really should be an error - fsync(file->fil_desc); - } + // This really should be an error + fsync(file->fil_desc); } #endif } @@ -615,7 +565,7 @@ void PIO_header(thread_db* tdbb, UCHAR* address, int length) static Firebird::InitInstance zeros; -USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* status_vector, +USHORT PIO_init_data(thread_db* tdbb, jrd_file* file, FbStatusVector* status_vector, ULONG startPage, USHORT initPages) { /************************************** @@ -642,16 +592,14 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); - jrd_file* file = seek_file(main_file, &bdb, &offset, status_vector); - - if (!file) + if (!seek_file(file, &bdb, &offset, status_vector)) return 0; - if (file->fil_min_page + 8 > startPage) + if (startPage < 8) return 0; USHORT leftPages = initPages; - const ULONG initBy = MIN(file->fil_max_page - startPage, leftPages); + const ULONG initBy = MIN(MAX_ULONG - startPage, leftPages); if (initBy < leftPages) leftPages = initBy; @@ -667,15 +615,16 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu for (int r = 0; r < IO_RETRY; r++) { - if (!(file = seek_file(file, &bdb, &offset, status_vector))) - return false; + if (!seek_file(file, &bdb, &offset, status_vector)) + return 0; + if ((written = os_utils::pwrite(file->fil_desc, zero_buff, to_write, LSEEK_OFFSET_CAST offset)) == to_write) break; + if (written < 0 && !SYSCALL_INTERRUPTED(errno)) return unix_error("write", file, isc_io_write_err, status_vector); } - leftPages -= write_pages; i += write_pages; } @@ -812,7 +761,7 @@ bool PIO_read(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page, for (i = 0; i < IO_RETRY; i++) { - if (!(file = seek_file(file, bdb, &offset, status_vector))) + if (!seek_file(file, bdb, &offset, status_vector)) return false; if ((bytes = os_utils::pread(file->fil_desc, page, size, LSEEK_OFFSET_CAST offset)) == size) @@ -864,7 +813,7 @@ bool PIO_write(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page, for (i = 0; i < IO_RETRY; i++) { - if (!(file = seek_file(file, bdb, &offset, status_vector))) + if (!seek_file(file, bdb, &offset, status_vector)) return false; if ((bytes = os_utils::pwrite(file->fil_desc, page, size, LSEEK_OFFSET_CAST offset)) == size) @@ -881,8 +830,8 @@ bool PIO_write(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page, } -static jrd_file* seek_file(jrd_file* file, BufferDesc* bdb, FB_UINT64* offset, - FbStatusVector* status_vector) +static bool seek_file(jrd_file* file, BufferDesc* bdb, FB_UINT64* offset, + FbStatusVector* status_vector) { /************************************** * @@ -891,43 +840,29 @@ static jrd_file* seek_file(jrd_file* file, BufferDesc* bdb, FB_UINT64* offset, ************************************** * * Functional description - * Given a buffer descriptor block, find the appropriate - * file block and seek to the proper page in that file. + * Given a buffer descriptor block, seek to the proper page in that file. * **************************************/ - BufferControl* bcb = bdb->bdb_bcb; - Database* dbb = bcb->bcb_database; - ULONG page = bdb->bdb_page.getPageNum(); - - for (;; file = file->fil_next) - { - if (!file) { - CORRUPT(158); // msg 158 database file not available - } - else if (page >= file->fil_min_page && page <= file->fil_max_page) - break; - } + BufferControl* const bcb = bdb->bdb_bcb; + const ULONG page = bdb->bdb_page.getPageNum(); if (file->fil_desc == -1) { unix_error("lseek", file, isc_io_access_err, status_vector); - return 0; + return false; } - page -= file->fil_min_page - file->fil_fudge; - FB_UINT64 lseek_offset = page; - lseek_offset *= dbb->dbb_page_size; + lseek_offset *= bcb->bcb_page_size; if (lseek_offset != (FB_UINT64) LSEEK_OFFSET_CAST lseek_offset) { unix_error("lseek", file, isc_io_32bit_exceeded_err, status_vector); - return 0; + return false; } *offset = lseek_offset; - - return file; + return true; } @@ -999,7 +934,6 @@ static jrd_file* setup_file(Database* dbb, const PathName& file_name, int desc, { file = FB_NEW_RPT(*dbb->dbb_permanent, file_name.length() + 1) jrd_file(); file->fil_desc = desc; - file->fil_max_page = MAX_ULONG; file->fil_flags = flags; strcpy(file->fil_string, file_name.c_str()); } diff --git a/src/jrd/os/win32/winnt.cpp b/src/jrd/os/win32/winnt.cpp index 71b0a3b50b..fa9ea5bb1e 100644 --- a/src/jrd/os/win32/winnt.cpp +++ b/src/jrd/os/win32/winnt.cpp @@ -106,7 +106,7 @@ using namespace Firebird; #define TEXT SCHAR static bool maybeCloseFile(HANDLE&); -static jrd_file* seek_file(jrd_file*, BufferDesc*, OVERLAPPED*); +static bool seek_file(jrd_file*, BufferDesc*, OVERLAPPED*); static jrd_file* setup_file(Database*, const Firebird::PathName&, HANDLE, USHORT); static bool nt_error(const TEXT*, const jrd_file*, ISC_STATUS, FbStatusVector* const); @@ -121,39 +121,7 @@ static const DWORD g_dwExtraTempFlags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE; -int PIO_add_file(thread_db* tdbb, jrd_file* main_file, const Firebird::PathName& file_name, SLONG start) -{ -/************************************** - * - * P I O _ a d d _ f i l e - * - ************************************** - * - * Functional description - * Add a file to an existing database. Return the sequence - * number of the new file. If anything goes wrong, return a - * sequence of 0. - * - **************************************/ - jrd_file* const new_file = PIO_create(tdbb, file_name, false, false); - if (!new_file) - return 0; - - new_file->fil_min_page = start; - USHORT sequence = 1; - - jrd_file* file; - for (file = main_file; file->fil_next; file = file->fil_next) - ++sequence; - - file->fil_max_page = start - 1; - file->fil_next = new_file; - - return sequence; -} - - -void PIO_close(jrd_file* main_file) +void PIO_close(jrd_file* file) { /************************************** * @@ -164,10 +132,7 @@ void PIO_close(jrd_file* main_file) * Functional description * **************************************/ - for (jrd_file* file = main_file; file; file = file->fil_next) - { - maybeCloseFile(file->fil_desc); - } + maybeCloseFile(file->fil_desc); } @@ -251,7 +216,7 @@ bool PIO_expand(const TEXT* file_name, USHORT file_length, TEXT* expanded_name, } -void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, const USHORT pageSize) +void PIO_extend(thread_db* tdbb, jrd_file* file, const ULONG extPages, const USHORT pageSize) { /************************************** * @@ -263,6 +228,8 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons * Extend file by extPages pages of pageSize size. * **************************************/ + fb_assert(extPages); + // hvlad: prevent other reading\writing threads from changing file pointer. // As we open file without FILE_FLAG_OVERLAPPED, ReadFile\WriteFile calls // will change file pointer we set here and file truncation instead of file @@ -271,42 +238,30 @@ void PIO_extend(thread_db* tdbb, jrd_file* main_file, const ULONG extPages, cons // and read\write activity performed simultaneously) // if file have no extend lock it is better to not extend file than corrupt it - if (!main_file->fil_ext_lock) + if (!file->fil_ext_lock) return; EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); - FileExtendLockGuard extLock(main_file->fil_ext_lock, true); + FileExtendLockGuard extLock(file->fil_ext_lock, true); - ULONG leftPages = extPages; - for (jrd_file* file = main_file; file && leftPages; file = file->fil_next) - { - const ULONG filePages = PIO_get_number_of_pages(file, pageSize); - const ULONG fileMaxPages = (file->fil_max_page == MAX_ULONG) ? - MAX_ULONG : file->fil_max_page - file->fil_min_page + 1; - if (filePages < fileMaxPages) - { - const ULONG extendBy = MIN(fileMaxPages - filePages + file->fil_fudge, leftPages); + const ULONG filePages = PIO_get_number_of_pages(file, pageSize); + const ULONG extendBy = MIN(MAX_ULONG - filePages, extPages); - HANDLE hFile = file->fil_desc; + const HANDLE hFile = file->fil_desc; - LARGE_INTEGER newSize; - newSize.QuadPart = ((ULONGLONG) filePages + extendBy) * pageSize; + LARGE_INTEGER newSize; + newSize.QuadPart = ((ULONGLONG) filePages + extendBy) * pageSize; - const DWORD ret = SetFilePointer(hFile, newSize.LowPart, &newSize.HighPart, FILE_BEGIN); - if (ret == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - nt_error("SetFilePointer", file, isc_io_write_err, NULL); - } - if (!SetEndOfFile(hFile)) { - nt_error("SetEndOfFile", file, isc_io_write_err, NULL); - } + const DWORD ret = SetFilePointer(hFile, newSize.LowPart, &newSize.HighPart, FILE_BEGIN); + if (ret == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + nt_error("SetFilePointer", file, isc_io_write_err, NULL); - leftPages -= extendBy; - } - } + if (!SetEndOfFile(hFile)) + nt_error("SetEndOfFile", file, isc_io_write_err, NULL); } -void PIO_flush(thread_db* tdbb, jrd_file* main_file) +void PIO_flush(thread_db* tdbb, jrd_file* file) { /************************************** * @@ -320,7 +275,7 @@ void PIO_flush(thread_db* tdbb, jrd_file* main_file) **************************************/ EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); - for (jrd_file* file = main_file; file; file = file->fil_next) + if (file->fil_desc != INVALID_HANDLE_VALUE) FlushFileBuffers(file->fil_desc); } @@ -417,7 +372,7 @@ void PIO_header(thread_db* tdbb, UCHAR* address, int length) static Firebird::InitInstance zeros; -USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* status_vector, +USHORT PIO_init_data(thread_db* tdbb, jrd_file* file, FbStatusVector* status_vector, ULONG startPage, USHORT initPages) { /************************************** @@ -436,7 +391,7 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu Database* const dbb = tdbb->getDatabase(); EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); - FileExtendLockGuard extLock(main_file->fil_ext_lock, false); + FileExtendLockGuard extLock(file->fil_ext_lock, false); // Fake buffer, used in seek_file. Page space ID doesn't matter there // as we already know file to work with @@ -444,16 +399,14 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu bdb.bdb_page = PageNumber(0, startPage); OVERLAPPED overlapped; - jrd_file* file = seek_file(main_file, &bdb, &overlapped); - - if (!file) + if (!seek_file(file, &bdb, &overlapped)) return 0; - if (file->fil_min_page + 8 > startPage) + if (startPage < 8) return 0; USHORT leftPages = initPages; - const ULONG initBy = MIN(file->fil_max_page - startPage, leftPages); + const ULONG initBy = MIN(MAX_ULONG - startPage, leftPages); if (initBy < leftPages) leftPages = initBy; @@ -464,8 +417,8 @@ USHORT PIO_init_data(thread_db* tdbb, jrd_file* main_file, FbStatusVector* statu if (write_pages > leftPages) write_pages = leftPages; - jrd_file* file1 = seek_file(main_file, &bdb, &overlapped); - fb_assert(file1 == file); + if (!seek_file(file, &bdb, &overlapped)) + return 0; const DWORD to_write = (DWORD) write_pages * dbb->dbb_page_size; DWORD written; @@ -590,7 +543,7 @@ bool PIO_read(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page, FileExtendLockGuard extLock(file->fil_ext_lock, false); OVERLAPPED overlapped; - if (!(file = seek_file(file, bdb, &overlapped))) + if (!seek_file(file, bdb, &overlapped)) return false; HANDLE desc = file->fil_desc; @@ -625,14 +578,12 @@ bool PIO_read_ahead(thread_db* tdbb, ************************************** * * Functional description - * Read a contiguous set of pages. The only - * tricky part is to segment the I/O when crossing - * file boundaries. + * Read a contiguous set of pages. * **************************************/ OVERLAPPED overlapped, *overlapped_ptr; - Database* const dbb = tdbb->getDatabase(); + const auto dbb = tdbb->getDatabase(); EngineCheckout cout(tdbb, FB_FUNCTION, EngineCheckout::UNNECESSARY); @@ -647,60 +598,45 @@ bool PIO_read_ahead(thread_db* tdbb, piob->piob_flags = 0; } + // Setup up a dummy buffer descriptor block for seeking file BufferDesc bdb; - while (pages) + bdb.bdb_dbb = dbb; + bdb.bdb_page = start_page; + + const jrd_file* const file = dbb->dbb_file; + + if (!seek_file(file, &bdb, status_vector, overlapped_ptr, &overlapped_ptr)) + return false; + + const HANDLE desc = file->fil_desc; + const DWORD length = pages * dbb->dbb_page_size; + + DWORD actual_length; + if (ReadFile(desc, buffer, length, &actual_length, overlapped_ptr) && + actual_length == length) { - // Setup up a dummy buffer descriptor block for seeking file. - - bdb.bdb_dbb = dbb; - bdb.bdb_page = start_page; - - jrd_file* file = seek_file(dbb->dbb_file, &bdb, status_vector, overlapped_ptr, &overlapped_ptr); - if (!file) - return false; - - // Check that every page within the set resides in the same database - // file. If not read what you can and loop back for the rest. - - DWORD segmented_length = 0; - while (pages && start_page >= file->fil_min_page && start_page <= file->fil_max_page) - { - segmented_length += dbb->dbb_page_size; - ++start_page; - --pages; - } - - HANDLE desc = file->fil_desc; - - DWORD actual_length; - if (ReadFile(desc, buffer, segmented_length, &actual_length, overlapped_ptr) && - actual_length == segmented_length) - { - if (piob && !pages) - piob->piob_flags = PIOB_success; - } - else if (piob && !pages) - { - piob->piob_flags = PIOB_pending; - piob->piob_desc = reinterpret_cast(desc); - piob->piob_file = file; - piob->piob_io_length = segmented_length; - } - else if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) || - actual_length != segmented_length) - { - if (piob) - piob->piob_flags = PIOB_error; - - release_io_event(file, overlapped_ptr); - return nt_error("GetOverlappedResult", file, isc_io_read_err, status_vector); - } - - if (!piob || (piob->piob_flags & (PIOB_success | PIOB_error))) - release_io_event(file, overlapped_ptr); - - buffer += segmented_length; + if (piob) + piob->piob_flags = PIOB_success; } + else if (piob) + { + piob->piob_flags = PIOB_pending; + piob->piob_desc = reinterpret_cast(desc); + piob->piob_file = file; + piob->piob_io_length = segmented_length; + } + else if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) || + actual_length != length) + { + if (piob) + piob->piob_flags = PIOB_error; + + release_io_event(file, overlapped_ptr); + return nt_error("GetOverlappedResult", file, isc_io_read_err, status_vector); + } + + if (!piob || (piob->piob_flags & (PIOB_success | PIOB_error))) + release_io_event(file, overlapped_ptr); return true; } @@ -767,8 +703,7 @@ bool PIO_write(thread_db* tdbb, jrd_file* file, BufferDesc* bdb, Ods::pag* page, FileExtendLockGuard extLock(file->fil_ext_lock, false); OVERLAPPED overlapped; - file = seek_file(file, bdb, &overlapped); - if (!file) + if (!seek_file(file, bdb, &overlapped)) return false; HANDLE desc = file->fil_desc; @@ -813,9 +748,7 @@ ULONG PIO_get_number_of_pages(const jrd_file* file, const USHORT pagesize) } -static jrd_file* seek_file(jrd_file* file, - BufferDesc* bdb, - OVERLAPPED* overlapped) +static bool seek_file(jrd_file* file, BufferDesc* bdb, OVERLAPPED* overlapped) { /************************************** * @@ -824,24 +757,11 @@ static jrd_file* seek_file(jrd_file* file, ************************************** * * Functional description - * Given a buffer descriptor block, find the appropriate - * file block and seek to the proper page in that file. + * Given a buffer descriptor block, seek to the proper page in that file. * **************************************/ - BufferControl *bcb = bdb->bdb_bcb; - ULONG page = bdb->bdb_page.getPageNum(); - - for (;; file = file->fil_next) - { - if (!file) { - CORRUPT(158); // msg 158 database file not available - } - else if (page >= file->fil_min_page && page <= file->fil_max_page) { - break; - } - } - - page -= file->fil_min_page - file->fil_fudge; + BufferControl* const bcb = bdb->bdb_bcb; + const ULONG page = bdb->bdb_page.getPageNum(); LARGE_INTEGER liOffset; liOffset.QuadPart = UInt32x32To64((DWORD) page, (DWORD) bcb->bcb_page_size); @@ -854,7 +774,7 @@ static jrd_file* seek_file(jrd_file* file, ThreadSync* thd = ThreadSync::getThread(FB_FUNCTION); overlapped->hEvent = thd->getIOEvent(); - return file; + return true; } @@ -876,7 +796,6 @@ static jrd_file* setup_file(Database* dbb, const Firebird::PathName& file_name, { file = FB_NEW_RPT(*dbb->dbb_permanent, file_name.length() + 1) jrd_file(); file->fil_desc = desc; - file->fil_max_page = MAX_ULONG; file->fil_flags = flags; strcpy(file->fil_string, file_name.c_str()); diff --git a/src/jrd/pag.cpp b/src/jrd/pag.cpp index 73df5a3826..3c7c683517 100644 --- a/src/jrd/pag.cpp +++ b/src/jrd/pag.cpp @@ -364,115 +364,6 @@ namespace } // namespace -USHORT PAG_add_file(thread_db* tdbb, const TEXT* file_name, SLONG start) -{ -/************************************** - * - * P A G _ a d d _ f i l e - * - ************************************** - * - * Functional description - * Add a file to the current database. Return the sequence number for the new file. - * - **************************************/ - SET_TDBB(tdbb); - ensureDbWritable(tdbb); - - const auto dbb = tdbb->getDatabase(); - - // Find current last file - - PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); - jrd_file* file = pageSpace->file; - while (file->fil_next) { - file = file->fil_next; - } - - // Verify database file path against DatabaseAccess entry of firebird.conf - if (!JRD_verify_database_access(file_name)) - { - string fileName(file_name); - ISC_systemToUtf8(fileName); - ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") << - Arg::Str(fileName)); - } - - // Create the file. If the sequence number comes back zero, it didn't work, so punt - - const USHORT sequence = PIO_add_file(tdbb, pageSpace->file, file_name, start); - if (!sequence) - return 0; - - // Create header page for new file - - jrd_file* next = file->fil_next; - - WIN window(DB_PAGE_SPACE, next->fil_min_page); - header_page* header = (header_page*) CCH_fake(tdbb, &window, 1); - header->hdr_header.pag_type = pag_header; - header->hdr_sequence = sequence; - header->hdr_page_size = dbb->dbb_page_size; - header->hdr_data[0] = HDR_end; - header->hdr_end = HDR_SIZE; - next->fil_sequence = sequence; - -#ifdef SUPPORT_RAW_DEVICES - // The following lines (taken from PAG_format_header) are needed to identify - // this file in raw_devices_validate_database as a valid database attachment. - *(ISC_TIMESTAMP*) header->hdr_creation_date = TimeZoneUtil::getCurrentGmtTimeStamp().utc_timestamp; - // should we include milliseconds or not? - //TimeStamp::round_time(header->hdr_creation_date->timestamp_time, 0); - - header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG; - DbImplementation::current.store(header); - header->hdr_ods_minor = ODS_CURRENT; - if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) - header->hdr_flags |= hdr_SQL_dialect_3; -#endif - - header->hdr_header.pag_pageno = window.win_page.getPageNum(); - // It's header, never encrypted - PIO_write(tdbb, pageSpace->file, window.win_bdb, window.win_buffer, tdbb->tdbb_status_vector); - CCH_RELEASE(tdbb, &window); - next->fil_fudge = 1; - - // Update the previous header page to point to new file - - file->fil_fudge = 0; - window.win_page = file->fil_min_page; - header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header); - if (!file->fil_min_page) - CCH_MARK_MUST_WRITE(tdbb, &window); - else - CCH_MARK(tdbb, &window); - - --start; - - if (file->fil_min_page) - { - PAG_add_header_entry(tdbb, header, HDR_file, static_cast(strlen(file_name)), - reinterpret_cast(file_name)); - PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(SLONG), (UCHAR*) &start); - } - else - { - storeClump(tdbb, HDR_file, static_cast(strlen(file_name)), - reinterpret_cast(file_name)); - storeClump(tdbb, HDR_last_page, sizeof(SLONG), (UCHAR*) &start); - } - - header->hdr_header.pag_pageno = window.win_page.getPageNum(); - // It's header, never encrypted - PIO_write(tdbb, pageSpace->file, window.win_bdb, window.win_buffer, tdbb->tdbb_status_vector); - CCH_RELEASE(tdbb, &window); - if (file->fil_min_page) - file->fil_fudge = 1; - - return sequence; -} - - void PAG_add_header_entry(thread_db* tdbb, header_page* header, USHORT type, USHORT len, const UCHAR* entry) { @@ -1136,8 +1027,7 @@ void PAG_header(thread_db* tdbb, bool info, const TriState newForceWrite) // Ensure the file-level FW mode matches the actual FW mode in the database const auto pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); - for (jrd_file* file = pageSpace->file; file; file = file->fil_next) - PIO_force_write(file, forceWrite && !readOnly); + PIO_force_write(pageSpace->file, forceWrite && !readOnly); if (dbb->dbb_backup_manager->getState() != Ods::hdr_nbak_normal) dbb->dbb_backup_manager->setForcedWrites(forceWrite); @@ -1327,7 +1217,7 @@ void PAG_init(thread_db* tdbb) } -void PAG_init2(thread_db* tdbb, USHORT shadow_number) +void PAG_init2(thread_db* tdbb) { /************************************** * @@ -1336,128 +1226,40 @@ void PAG_init2(thread_db* tdbb, USHORT shadow_number) ************************************** * * Functional description - * Perform second phase of page initialization -- the eternal - * search for additional files. + * Read and apply the database header options. * **************************************/ SET_TDBB(tdbb); const auto dbb = tdbb->getDatabase(); - FbStatusVector* status = tdbb->tdbb_status_vector; + WIN window(HEADER_PAGE_NUMBER); + const auto header = (header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header); - // allocate a spare buffer which is large enough, - // and set up to release it in case of error. Align - // the temporary page buffer for raw disk access. - - Array temp; - UCHAR* const temp_page = temp.getAlignedBuffer(dbb->dbb_page_size, dbb->getIOBlockSize()); - - PageSpace* const pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); - jrd_file* file = pageSpace->file; - if (shadow_number) + for (const UCHAR* p = header->hdr_data; *p != HDR_end; p += 2 + p[1]) { - Shadow* shadow = dbb->dbb_shadow; - for (; shadow; shadow = shadow->sdw_next) + switch (*p) { - if (shadow->sdw_number == shadow_number) - { - file = shadow->sdw_file; - break; - } - } - if (!shadow) - BUGCHECK(161); // msg 161 shadow block not found - } - - USHORT sequence = 1; - WIN window(DB_PAGE_SPACE, -1); - - // Loop thru files and header pages until everything is open - - while (true) - { - window.win_page = file->fil_min_page; - ULONG last_page = 0; - BufferDesc temp_bdb(dbb->dbb_bcb); - - // note that we do not have to get a read lock on - // the header page (except for header page 0) because - // the only time it will be modified is when adding a file, - // which must be done with an exclusive lock on the database -- - // if this changes, this policy will have to be reevaluated; - // at any rate there is a problem with getting a read lock - // because the corresponding page in the main database file may not exist - - if (!file->fil_min_page) - CCH_FETCH(tdbb, &window, LCK_read, pag_header); - - header_page* header = (header_page*) temp_page; - temp_bdb.bdb_buffer = (pag*) header; - temp_bdb.bdb_page = window.win_page; - - // Read the required page into the local buffer - // It's header, never encrypted - PIO_read(tdbb, file, &temp_bdb, (PAG) header, status); - - if (shadow_number && !file->fil_min_page) - CCH_RELEASE(tdbb, &window); - - PathName nextFileName; - - for (const UCHAR* p = header->hdr_data; *p != HDR_end; p += 2 + p[1]) - { - switch (*p) - { - case HDR_file: - nextFileName.assign(p + 2, p[1]); - break; - - case HDR_last_page: - fb_assert(p[1] == sizeof(last_page)); - memcpy(&last_page, p + 2, sizeof(last_page)); - break; - - case HDR_sweep_interval: - fb_assert(p[1] == sizeof(SLONG)); - memcpy(&dbb->dbb_sweep_interval, p + 2, sizeof(SLONG)); - break; - - case HDR_db_guid: - fb_assert(p[1] == Guid::SIZE); - dbb->dbb_guid = Guid(p + 2); - break; - - case HDR_repl_seq: - fb_assert(p[1] == sizeof(FB_UINT64)); - memcpy(&dbb->dbb_repl_sequence, p + 2, sizeof(FB_UINT64)); - break; - } - } - - if (!shadow_number && !file->fil_min_page) - CCH_RELEASE(tdbb, &window); - - if (file->fil_min_page) - file->fil_fudge = 1; - - if (nextFileName.isEmpty()) + case HDR_sweep_interval: + fb_assert(p[1] == sizeof(SLONG)); + memcpy(&dbb->dbb_sweep_interval, p + 2, sizeof(SLONG)); break; - // Verify database file path against DatabaseAccess entry of firebird.conf - if (!JRD_verify_database_access(nextFileName)) - { - ISC_systemToUtf8(nextFileName); - ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") << - Arg::Str(nextFileName)); + case HDR_db_guid: + fb_assert(p[1] == Guid::SIZE); + dbb->dbb_guid = Guid(p + 2); + break; + + case HDR_repl_seq: + fb_assert(p[1] == sizeof(FB_UINT64)); + memcpy(&dbb->dbb_repl_sequence, p + 2, sizeof(FB_UINT64)); + break; + + default: + break; } - - file->fil_next = PIO_open(tdbb, nextFileName, nextFileName); - file->fil_max_page = last_page; - file = file->fil_next; - - file->fil_min_page = last_page + 1; - file->fil_sequence = sequence++; } + + CCH_RELEASE(tdbb, &window); } @@ -1644,14 +1446,10 @@ void PAG_set_force_write(thread_db* tdbb, bool flag) CCH_RELEASE(tdbb, &window); PageSpace* const pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); - for (jrd_file* file = pageSpace->file; file; file = file->fil_next) - PIO_force_write(file, flag); + PIO_force_write(pageSpace->file, flag); for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) - { - for (jrd_file* file = shadow->sdw_file; file; file = file->fil_next) - PIO_force_write(file, flag); - } + PIO_force_write(shadow->sdw_file, flag); if (dbb->dbb_backup_manager->getState() != Ods::hdr_nbak_normal) dbb->dbb_backup_manager->setForcedWrites(flag); @@ -1917,13 +1715,7 @@ PageSpace::~PageSpace() if (file) { PIO_close(file); - - while (file) - { - jrd_file* next = file->fil_next; - delete file; - file = next; - } + delete file; } } @@ -1936,15 +1728,7 @@ ULONG PageSpace::actAlloc() * **************************************/ - // Traverse the linked list of files and add up the - // number of pages in each file - const USHORT pageSize = dbb->dbb_page_size; - ULONG tot_pages = 0; - for (const jrd_file* f = file; f != NULL; f = f->fil_next) { - tot_pages += PIO_get_number_of_pages(f, pageSize); - } - - return tot_pages; + return PIO_get_number_of_pages(file, dbb->dbb_page_size); } ULONG PageSpace::actAlloc(const Database* dbb) @@ -1961,17 +1745,7 @@ ULONG PageSpace::maxAlloc() * Compute last physically allocated page of database. * **************************************/ - const USHORT pageSize = dbb->dbb_page_size; - const jrd_file* f = file; - ULONG nPages = PIO_get_number_of_pages(f, pageSize); - - while (f->fil_next && nPages == f->fil_max_page - f->fil_min_page + 1 + f->fil_fudge) - { - f = f->fil_next; - nPages = PIO_get_number_of_pages(f, pageSize); - } - - nPages += f->fil_min_page - f->fil_fudge; + const ULONG nPages = PIO_get_number_of_pages(file, dbb->dbb_page_size); if (maxPageNumber < nPages) maxPageNumber = nPages; @@ -1987,15 +1761,7 @@ ULONG PageSpace::maxAlloc(const Database* dbb) bool PageSpace::onRawDevice() const { -#ifdef SUPPORT_RAW_DEVICES - for (const jrd_file* f = file; f != NULL; f = f->fil_next) - { - if (f->fil_flags & FIL_raw_device) - return true; - } -#endif - - return false; + return (file->fil_flags & FIL_raw_device) != 0; } ULONG PageSpace::lastUsedPage() diff --git a/src/jrd/pag_proto.h b/src/jrd/pag_proto.h index dc9a4ae20d..084b55a2a3 100644 --- a/src/jrd/pag_proto.h +++ b/src/jrd/pag_proto.h @@ -37,7 +37,6 @@ namespace Ods { struct header_page; } -USHORT PAG_add_file(Jrd::thread_db* tdbb, const TEXT*, SLONG); void PAG_add_header_entry(Jrd::thread_db* tdbb, Ods::header_page*, USHORT, USHORT, const UCHAR*); bool PAG_replace_entry_first(Jrd::thread_db* tdbb, Ods::header_page*, USHORT, USHORT, const UCHAR*); Ods::pag* PAG_allocate_pages(Jrd::thread_db* tdbb, Jrd::win* window, unsigned cntAlloc, bool aligned); @@ -49,7 +48,7 @@ bool PAG_get_clump(Jrd::thread_db*, USHORT, USHORT*, UCHAR*); void PAG_header(Jrd::thread_db*, bool, const Firebird::TriState newForceWrite = Firebird::TriState()); void PAG_header_init(Jrd::thread_db*); void PAG_init(Jrd::thread_db*); -void PAG_init2(Jrd::thread_db*, USHORT); +void PAG_init2(Jrd::thread_db*); SLONG PAG_last_page(Jrd::thread_db* tdbb); void PAG_release_page(Jrd::thread_db* tdbb, const Jrd::PageNumber&, const Jrd::PageNumber&); void PAG_release_pages(Jrd::thread_db* tdbb, USHORT pageSpaceID, int cntRelease, diff --git a/src/jrd/sdw.cpp b/src/jrd/sdw.cpp index c7a14f7a39..4e78682f33 100644 --- a/src/jrd/sdw.cpp +++ b/src/jrd/sdw.cpp @@ -64,7 +64,6 @@ static bool check_for_file(thread_db* tdbb, const SCHAR*, USHORT); #ifdef NOT_USED_OR_REPLACED static void check_if_got_ast(thread_db* tdbb, jrd_file*); #endif -static void copy_header(thread_db* tdbb); static void update_dbb_to_sdw(Database*); @@ -112,147 +111,6 @@ void SDW_add(thread_db* tdbb, const TEXT* file_name, USHORT shadow_number, USHOR } -int SDW_add_file(thread_db* tdbb, const TEXT* file_name, SLONG start, USHORT shadow_number) -{ -/************************************** - * - * S D W _ a d d _ f i l e - * - ************************************** - * - * Functional description - * Add a file to a shadow set. - * Return the sequence number for the new file. - * - **************************************/ - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - - SyncLockGuard guard(&dbb->dbb_shadow_sync, SYNC_EXCLUSIVE, "SDW_add_file"); - - // Find the file to be extended - - jrd_file* shadow_file = 0; - Shadow* shadow; - for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) - { - if ((shadow->sdw_number == shadow_number) && - !(shadow->sdw_flags & (SDW_IGNORE | SDW_rollover))) - { - shadow_file = shadow->sdw_file; - break; - } - } - - if (!shadow) { - return 0; - } - - // find the last file in the list, open the new file - - jrd_file* file = shadow_file; - while (file->fil_next) { - file = file->fil_next; - } - - // Verify shadow file path against DatabaseAccess entry of firebird.conf - if (!JRD_verify_database_access(file_name)) - { - ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("database shadow") << - Arg::Str(file_name)); - } - - const SLONG sequence = PIO_add_file(tdbb, shadow_file, file_name, start); - if (!sequence) - return 0; - - jrd_file* next = file->fil_next; - - // Always write the header page, even for a conditional - // shadow that hasn't been activated. - - // allocate a spare buffer which is large enough, - // and set up to release it in case of error. Align - // the spare page buffer for raw disk access. - - Array temp; - UCHAR* const spare_page = temp.getAlignedBuffer(dbb->dbb_page_size, dbb->getIOBlockSize()); - - // create the header using the spare_buffer - - header_page* header = (header_page*) spare_page; - header->hdr_header.pag_type = pag_header; - header->hdr_sequence = sequence; - header->hdr_page_size = dbb->dbb_page_size; - header->hdr_data[0] = HDR_end; - header->hdr_end = HDR_SIZE; - - // fool PIO_write into writing the scratch page into the correct place - BufferDesc temp_bdb(dbb->dbb_bcb); - temp_bdb.bdb_page = next->fil_min_page; - temp_bdb.bdb_buffer = (PAG) header; - header->hdr_header.pag_pageno = temp_bdb.bdb_page.getPageNum(); - // It's header, never encrypted - if (!PIO_write(tdbb, shadow_file, &temp_bdb, reinterpret_cast(header), 0)) - return 0; - - next->fil_fudge = 1; - - // Update the previous header page to point to new file -- - // we can use the same header page, suitably modified, - // because they all look pretty much the same at this point - - /******************* - Fix for bug 7925. drop_gdb wan not dropping secondary file in - multi-shadow files. The structure was not being filled with the - info. Commented some code so that the structure will always be filled. - - -Sudesh 07/06/95 - - The original code : - === - if (shadow_file == file) - copy_header(tdbb); - else - === - ************************/ - - // Temporarly reverting the change ------- Sudesh 07/07/95 ******* - - if (shadow_file == file) - { - copy_header(tdbb); - } - else - { - --start; - header->hdr_data[0] = HDR_end; - header->hdr_end = HDR_SIZE; - - PAG_add_header_entry(tdbb, header, HDR_file, static_cast(strlen(file_name)), - reinterpret_cast(file_name)); - PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(start), - reinterpret_cast(&start)); - file->fil_fudge = 0; - temp_bdb.bdb_page = file->fil_min_page; - header->hdr_header.pag_pageno = temp_bdb.bdb_page.getPageNum(); - // It's header, never encrypted - if (!PIO_write(tdbb, shadow_file, &temp_bdb, reinterpret_cast(header), 0)) - return 0; - - if (file->fil_min_page) { - file->fil_fudge = 1; - } - } - - if (file->fil_min_page) { - file->fil_fudge = 1; - } - - return sequence; -} - - void SDW_check(thread_db* tdbb) { /************************************** @@ -814,12 +672,7 @@ bool SDW_rollover_to_shadow(thread_db* tdbb, jrd_file* file, const bool inAst) // close the main database file if possible and release all file blocks PIO_close(pageSpace->file); - - while ( (file = pageSpace->file) ) - { - pageSpace->file = file->fil_next; - delete file; - } + delete pageSpace->file; /* point the main database file at the file of the first shadow in the list and mark that shadow as rolled over to @@ -894,13 +747,7 @@ static void shutdown_shadow(Shadow* shadow) // close the shadow files and free up the associated memory PIO_close(shadow->sdw_file); - jrd_file* file; - jrd_file* free = shadow->sdw_file; - - for (; (file = free->fil_next); free = file) - delete free; - - delete free; + delete shadow->sdw_file; delete shadow; } @@ -1003,12 +850,6 @@ void SDW_start(thread_db* tdbb, const TEXT* file_name, const header_page* shadow_header = (header_page*) spare_page; - // NOTE ! NOTE! NOTE! - // Starting V4.0, header pages can have overflow pages. For the shadow, - // we are making an assumption that the shadow header page will not - // overflow, as the only things written on a shadow header is the - // HDR_root_file_name, HDR_file, and HDR_last_page - const UCHAR* p = shadow_header->hdr_data; while (*p != HDR_end && *p != HDR_root_file_name) { p += 2 + p[1]; @@ -1046,10 +887,6 @@ void SDW_start(thread_db* tdbb, const TEXT* file_name, shadow->sdw_flags |= SDW_dumped; } - // get the ancillary files and reset the error environment - - PAG_init2(tdbb, shadow_number); - } // try catch (const Firebird::Exception& ex) { @@ -1257,35 +1094,6 @@ static void check_if_got_ast(thread_db* tdbb, jrd_file* file) } #endif -static void copy_header(thread_db* tdbb) -{ -/************************************** - * - * c o p y _ h e a d e r - * - ************************************** - * - * Functional description - * Fetch the header page from the database - * and write it to the shadow file. This is - * done so that if this shadow is extended, - * the header page will be there for writing - * the name of the extend file. - * - **************************************/ - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - CHECK_DBB(dbb); - - // get the database header page and write it out -- - // CCH will take care of modifying it - - WIN window(HEADER_PAGE_NUMBER); - CCH_FETCH(tdbb, &window, LCK_write, pag_header); - CCH_MARK_MUST_WRITE(tdbb, &window); - CCH_RELEASE(tdbb, &window); -} - static void update_dbb_to_sdw(Database* dbb) { @@ -1319,13 +1127,7 @@ static void update_dbb_to_sdw(Database* dbb) // hvlad: need sync for this code PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); PIO_close(pageSpace->file); - - jrd_file* file; - while ( (file = pageSpace->file) ) - { - pageSpace->file = file->fil_next; - delete file; - } + delete pageSpace->file; pageSpace->file = shadow->sdw_file; shadow->sdw_flags |= SDW_rollover; diff --git a/src/jrd/sdw_proto.h b/src/jrd/sdw_proto.h index db9f8cf23f..325bdfef7d 100644 --- a/src/jrd/sdw_proto.h +++ b/src/jrd/sdw_proto.h @@ -29,7 +29,6 @@ namespace Jrd { } void SDW_add(Jrd::thread_db* tdbb, const TEXT*, USHORT, USHORT); -int SDW_add_file(Jrd::thread_db* tdbb, const TEXT*, SLONG, USHORT); void SDW_check(Jrd::thread_db* tdbb); bool SDW_check_conditional(Jrd::thread_db* tdbb); void SDW_close(); diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 35a1797b27..d42711bcca 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -478,7 +478,6 @@ enum dfw_t { dfw_create_index, dfw_delete_index, dfw_compute_security, - dfw_add_file, dfw_add_shadow, dfw_delete_shadow, dfw_delete_shadow_nodelete, diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index cdb851df85..3d23566bbc 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2240,27 +2240,32 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_files: protect_system_table_delupd(tdbb, relation, "DELETE"); { - const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); - const USHORT file_flags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ? + const bool nameDefined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); + + const auto shadowNumber = EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) ? MOV_get_long(tdbb, &desc2, 0) : 0; - if (file_flags & FILE_difference) + + const auto fileFlags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ? + MOV_get_long(tdbb, &desc2, 0) : 0; + + if (shadowNumber) { - if (file_flags & FILE_backing_up) - DFW_post_work(transaction, dfw_end_backup, &desc, 0); - if (name_defined) - DFW_post_work(transaction, dfw_delete_difference, &desc, 0); - } - else if (EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) && - (id = MOV_get_long(tdbb, &desc2, 0))) - { - if (!(file_flags & FILE_inactive)) + if (!(fileFlags & FILE_inactive)) { - if (file_flags & FILE_nodelete) - DFW_post_work(transaction, dfw_delete_shadow_nodelete, &desc, id); - else - DFW_post_work(transaction, dfw_delete_shadow, &desc, id); + const auto work = (fileFlags & FILE_nodelete) ? + dfw_delete_shadow_nodelete : dfw_delete_shadow; + + DFW_post_work(transaction, work, &desc, shadowNumber); } } + else if (fileFlags & FILE_difference) + { + if (fileFlags & FILE_backing_up) + DFW_post_work(transaction, dfw_end_backup, &desc, 0); + + if (nameDefined) + DFW_post_work(transaction, dfw_delete_difference, &desc, 0); + } } break; @@ -3617,18 +3622,24 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_files: protect_system_table_delupd(tdbb, relation, "UPDATE"); { - SSHORT new_rel_flags, old_rel_flags; EVL_field(0, new_rpb->rpb_record, f_file_name, &desc1); - if (EVL_field(0, new_rpb->rpb_record, f_file_flags, &desc2) && - ((new_rel_flags = MOV_get_long(tdbb, &desc2, 0)) & FILE_difference) && - EVL_field(0, org_rpb->rpb_record, f_file_flags, &desc2) && - ((old_rel_flags = MOV_get_long(tdbb, &desc2, 0)) != new_rel_flags)) + + const auto orgFileFlags = EVL_field(0, org_rpb->rpb_record, f_file_flags, &desc2) ? + MOV_get_long(tdbb, &desc2, 0) : 0; + const auto newFileFlags = EVL_field(0, new_rpb->rpb_record, f_file_flags, &desc2) ? + MOV_get_long(tdbb, &desc2, 0) : 0; + + if ((newFileFlags & FILE_difference) && orgFileFlags != newFileFlags) { DFW_post_work(transaction, - (new_rel_flags & FILE_backing_up ? dfw_begin_backup : dfw_end_backup), + (newFileFlags & FILE_backing_up) ? dfw_begin_backup : dfw_end_backup, &desc1, 0); } } + // Nullify the unsupported fields + new_rpb->rpb_record->setNull(f_file_seq); + new_rpb->rpb_record->setNull(f_file_start); + new_rpb->rpb_record->setNull(f_file_length); break; case rel_charsets: @@ -4175,34 +4186,32 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_files: protect_system_table_insert(tdbb, request, relation); { - const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); - if (EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) && - MOV_get_long(tdbb, &desc2, 0)) + const bool nameDefined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); + + const auto shadowNumber = EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) ? + MOV_get_long(tdbb, &desc2, 0) : 0; + + const auto fileFlags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ? + MOV_get_long(tdbb, &desc2, 0) : 0; + + if (shadowNumber) { - EVL_field(0, rpb->rpb_record, f_file_flags, &desc2); - if (!(MOV_get_long(tdbb, &desc2, 0) & FILE_inactive)) { + if (!(fileFlags & FILE_inactive)) DFW_post_work(transaction, dfw_add_shadow, &desc, 0); - } } - else + else if (fileFlags & FILE_difference) { - USHORT rel_flags; - if (EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) && - ((rel_flags = MOV_get_long(tdbb, &desc2, 0)) & FILE_difference)) - { - if (name_defined) { - DFW_post_work(transaction, dfw_add_difference, &desc, 0); - } - if (rel_flags & FILE_backing_up) - { - DFW_post_work(transaction, dfw_begin_backup, &desc, 0); - } - } - else { - DFW_post_work(transaction, dfw_add_file, &desc, 0); - } + if (nameDefined) + DFW_post_work(transaction, dfw_add_difference, &desc, 0); + + if (fileFlags & FILE_backing_up) + DFW_post_work(transaction, dfw_begin_backup, &desc, 0); } } + // Nullify the unsupported fields + rpb->rpb_record->setNull(f_file_seq); + rpb->rpb_record->setNull(f_file_start); + rpb->rpb_record->setNull(f_file_length); break; case rel_triggers: diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index 44c0ac4277..5bf89a5d4b 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -164,10 +164,6 @@ struct dba_rel struct dba_fil { - dba_fil* fil_next; // Next file in database - ULONG fil_min_page; // Minimum page number in file - ULONG fil_max_page; // Maximum page number in file - USHORT fil_fudge; // Fudge factor for page relocation #ifdef WIN_NT void *fil_desc; #else @@ -216,16 +212,6 @@ static void print_help(); #include "../utilities/gstat/dba_proto.h" -struct open_files -{ -#ifdef WIN_NT - void* desc; -#else - int desc; -#endif - open_files* open_files_next; -}; - struct dba_mem { char* memory; @@ -241,7 +227,7 @@ public: : ThreadData(tddDBA), uSvc(us) { //dba_throw = false; - files = 0; + file = 0; relations = 0; page_size = 0; dp_per_pp = 0; @@ -252,14 +238,13 @@ public: global_buffer = 0; exit_code = 0; head_of_mem_list = 0; - head_of_files_list = 0; memset(dba_status_vector, 0, sizeof (dba_status_vector)); dba_status = dba_status_vector; } //bool dba_throw; Firebird::UtilSvc* uSvc; - dba_fil* files; + dba_fil* file; dba_rel* relations; USHORT page_size; USHORT dp_per_pp; @@ -270,7 +255,6 @@ public: pag* global_buffer; int exit_code; dba_mem *head_of_mem_list; - open_files *head_of_files_list; ISC_STATUS *dba_status; ISC_STATUS_ARRAY dba_status_vector; @@ -613,7 +597,7 @@ int gstat(Firebird::UtilSvc* uSvc) expandDatabaseName(fileName, tempStr, NULL); fileName = tempStr; - dba_fil* current = db_open(fileName.c_str(), fileName.length()); + dba_fil* file = db_open(fileName.c_str(), fileName.length()); alignas(DIRECT_IO_BLOCK_SIZE) SCHAR temp[MAX(RAW_HEADER_SIZE, DIRECT_IO_BLOCK_SIZE)]; tddba->page_size = MAX(RAW_HEADER_SIZE, DIRECT_IO_BLOCK_SIZE); @@ -661,38 +645,6 @@ int gstat(Firebird::UtilSvc* uSvc) if (sw_header) dba_exit(FINI_OK, tddba); - // gather continuation files - - while (true) - { - if (page != HEADER_PAGE) - { - current = db_open(file_name, static_cast(strlen(file_name))); - header = (const header_page*) db_read(page); - } - - if (current != tddba->files) - current->fil_fudge = 1; // ignore header page once read it - - PathName nextFileName; - - const UCHAR* vp = header->hdr_data; - for (const auto vend = reinterpret_cast(header) + header->hdr_page_size; - vp < vend && *vp != HDR_end; vp += 2 + vp[1]) - { - if (*vp == HDR_file) - nextFileName.assign(vp + 2, vp[1]); - - if (*vp == HDR_last_page) - memcpy(¤t->fil_max_page, vp + 2, sizeof(current->fil_max_page)); - } - - if (nextFileName.isEmpty()) - break; - - page = current->fil_max_page + 1; // first page of next file - } - if (sw_enc) { class Statist @@ -724,6 +676,7 @@ int gstat(Firebird::UtilSvc* uSvc) private: ULONG enc, non; }; + Statist data, index, blob, generator, other; ULONG last = lastUsedPage(header->hdr_page_size); @@ -766,18 +719,6 @@ int gstat(Firebird::UtilSvc* uSvc) dba_exit(FINI_OK, tddba); } - // print continuation file sequence - - dba_print(false, 7); - // msg 7: \n\nDatabase file sequence: - for (current = tddba->files; current->fil_next; current = current->fil_next) - { - dba_print(false, 8, SafeArg() << current->fil_string << current->fil_next->fil_string); - // msg 8: File %s continues as file %s - } - dba_print(false, 9, SafeArg() << current->fil_string << ((current == tddba->files) ? "only" : "last")); - // msg 9: File %s is the %s file\n - // Check to make sure that the user accessing the database is either // SYSDBA or owner of the database @@ -1192,22 +1133,11 @@ int gstat(Firebird::UtilSvc* uSvc) alloced = alloced->mem_next; } - // close files - open_files* open_file = tddba->head_of_files_list; - while (open_file) - { - db_close(open_file->desc); - open_file = open_file->open_files_next; - } + // close file + if (tddba->file) + db_close(tddba->file->fil_desc); // free linked lists - while (tddba->head_of_files_list != 0) - { - open_files* tmp1 = tddba->head_of_files_list; - tddba->head_of_files_list = tddba->head_of_files_list->open_files_next; - delete tmp1; - } - while (tddba->head_of_mem_list != 0) { dba_mem* tmp2 = tddba->head_of_mem_list; @@ -1865,29 +1795,12 @@ static dba_fil* db_open(const char* file_name, USHORT file_length) **************************************/ tdba* tddba = tdba::getSpecific(); - dba_fil* fil; + const auto file = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1); - if (tddba->files) - { - for (fil = tddba->files; fil->fil_next; fil = fil->fil_next); - fil->fil_next = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1); - fil->fil_next->fil_min_page = fil->fil_max_page + 1; - fil = fil->fil_next; - } - else - { - // empty list - fil = tddba->files = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1); - fil->fil_min_page = 0L; - } + strcpy(file->fil_string, file_name); + file->fil_length = file_length; - fil->fil_next = NULL; - strcpy(fil->fil_string, file_name); - fil->fil_length = file_length; - fil->fil_fudge = 0; - fil->fil_max_page = 0L; - - fil->fil_desc = CreateFile( file_name, + file->fil_desc = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, @@ -1895,31 +1808,15 @@ static dba_fil* db_open(const char* file_name, USHORT file_length) FILE_ATTRIBUTE_NORMAL, 0); - if (fil->fil_desc == INVALID_HANDLE_VALUE) + if (file->fil_desc == INVALID_HANDLE_VALUE) { tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 29, SafeArg() << file_name); // msg 29: Can't open database file %s db_error(GetLastError()); } - open_files* file_list = FB_NEW_POOL(*getDefaultMemoryPool()) open_files; - if (!file_list) - { - // NOMEM: return error - dba_error(31); - } - file_list->desc = fil->fil_desc; - file_list->open_files_next = 0; - - if (tddba->head_of_files_list == 0) - tddba->head_of_files_list = file_list; - else - { - file_list->open_files_next = tddba->head_of_files_list; - tddba->head_of_files_list = file_list; - } - - return fil; + tddba->file = file; + return file; } @@ -1945,17 +1842,9 @@ static const pag* db_read( SLONG page_number, bool ok_enc) tddba->page_number = page_number; - dba_fil* fil; - for (fil = tddba->files; page_number > (SLONG) fil->fil_max_page && fil->fil_next;) - { - fil = fil->fil_next; - } - - page_number -= fil->fil_min_page - fil->fil_fudge; - LARGE_INTEGER liOffset; liOffset.QuadPart = UInt32x32To64((DWORD) page_number, (DWORD) tddba->page_size); - if (SetFilePointer(fil->fil_desc, (LONG) liOffset.LowPart, &liOffset.HighPart, FILE_BEGIN) == + if (SetFilePointer(tddba->file->fil_desc, (LONG) liOffset.LowPart, &liOffset.HighPart, FILE_BEGIN) == (DWORD) -1) { int lastError = GetLastError(); @@ -1968,7 +1857,7 @@ static const pag* db_read( SLONG page_number, bool ok_enc) } SLONG actual_length; - if (!ReadFile( fil->fil_desc, + if (!ReadFile( tddba->file->fil_desc, tddba->global_buffer, tddba->page_size, reinterpret_cast(&actual_length), @@ -2057,54 +1946,20 @@ static dba_fil* db_open(const char* file_name, USHORT file_length) **************************************/ tdba* tddba = tdba::getSpecific(); - dba_fil* fil; - if (tddba->files) - { - for (fil = tddba->files; fil->fil_next; fil = fil->fil_next) - ; - fil->fil_next = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1); - fil->fil_next->fil_min_page = fil->fil_max_page + 1; - fil = fil->fil_next; - } - else - { - // empty list + const auto file = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1); - fil = tddba->files = (dba_fil*) alloc(sizeof(dba_fil) + file_length + 1); - fil->fil_min_page = 0L; - } + strcpy(file->fil_string, file_name); + file->fil_length = file_length; - fil->fil_next = NULL; - strcpy(fil->fil_string, file_name); - fil->fil_length = file_length; - fil->fil_fudge = 0; - fil->fil_max_page = 0L; - - if ((fil->fil_desc = os_utils::open(file_name, O_RDONLY)) == -1) + if ((file->fil_desc = os_utils::open(file_name, O_RDONLY)) == -1) { tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 29, SafeArg() << file_name); // msg 29: Can't open database file %s db_error(errno); } - open_files* file_list = FB_NEW_POOL(*getDefaultMemoryPool()) open_files; - if (!file_list) - { - // NOMEM: return error - dba_error(31); - } - file_list->desc = fil->fil_desc; - file_list->open_files_next = 0; - - if (tddba->head_of_files_list == 0) - tddba->head_of_files_list = file_list; - else - { - file_list->open_files_next = tddba->head_of_files_list; - tddba->head_of_files_list = file_list; - } - - return fil; + tddba->file = file; + return file; } @@ -2127,15 +1982,8 @@ static const pag* db_read( SLONG page_number, bool ok_enc) tddba->page_number = page_number; - dba_fil* fil; - for (fil = tddba->files; page_number > (SLONG) fil->fil_max_page && fil->fil_next;) - { - fil = fil->fil_next; - } - - page_number -= fil->fil_min_page - fil->fil_fudge; const FB_UINT64 offset = ((FB_UINT64) page_number) * ((FB_UINT64) tddba->page_size); - if (os_utils::lseek (fil->fil_desc, offset, 0) == -1) + if (os_utils::lseek (tddba->file->fil_desc, offset, 0) == -1) { tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 30, SafeArg()); // msg 30: Can't read a database page @@ -2145,7 +1993,7 @@ static const pag* db_read( SLONG page_number, bool ok_enc) USHORT length = tddba->page_size; for (SCHAR* p = (SCHAR *) tddba->global_buffer; length > 0;) { - const int l = read(fil->fil_desc, p, length); + const int l = read(tddba->file->fil_desc, p, length); if (l < 0) { tddba->uSvc->getStatusAccessor().setServiceStatus(GSTAT_MSG_FAC, 30, SafeArg()); diff --git a/src/utilities/gstat/ppg.cpp b/src/utilities/gstat/ppg.cpp index 88380c898b..40b773e03e 100644 --- a/src/utilities/gstat/ppg.cpp +++ b/src/utilities/gstat/ppg.cpp @@ -231,17 +231,6 @@ void PPG_print_header(const header_page* header, bool nocreation, Firebird::Util uSvc->printf(false, "\tRoot file name:\t\t%s\n", temp); break; - case HDR_file: - memcpy(temp, p + 2, p[1]); - temp[p[1]] = '\0'; - uSvc->printf(false, "\tContinuation file:\t\t%s\n", temp); - break; - - case HDR_last_page: - memcpy(&number, p + 2, sizeof(number)); - uSvc->printf(false, "\tLast logical page:\t\t%ld\n", number); - break; - case HDR_sweep_interval: memcpy(&number, p + 2, sizeof(number)); uSvc->printf(false, "\tSweep interval:\t\t%ld\n", number); diff --git a/src/utilities/rebuild/rebuild.cpp b/src/utilities/rebuild/rebuild.cpp index d15caa95d7..8d8d1cadb2 100644 --- a/src/utilities/rebuild/rebuild.cpp +++ b/src/utilities/rebuild/rebuild.cpp @@ -63,7 +63,6 @@ static void format_index_root(index_root_page*, int, SSHORT, SSHORT); static void format_pointer(pointer_page*, int, SSHORT, SSHORT, bool, SSHORT, const SLONG*); static void format_pip(page_inv_page*, int, int); static void format_tip(tx_inv_page*, int, SLONG); -static void get_next_file(rbdb*, header_page*); static void get_range(TEXT***, const TEXT* const* const, ULONG*, ULONG*); static void get_switch(TEXT**, swc*); static header_page* open_database(rbdb*, ULONG); @@ -197,8 +196,7 @@ int main( int argc, char *argv[]) rbdb = (rbdb*) RBDB_alloc((SLONG) (sizeof(struct rbdb) + strlen(db_in))); strcpy(rbdb->rbdb_file.fil_name, db_in); rbdb->rbdb_file.fil_length = strlen(db_in); - if (header = open_database(rbdb, pg_size)) - get_next_file(rbdb, header); + header = open_database(rbdb, pg_size); // some systems don't care for this write sharing stuff... if (rbdb && (sw_dump_tips || sw_dump_pages)) @@ -210,7 +208,6 @@ int main( int argc, char *argv[]) } } - gdbb = &tdbb_struct; gdbb->tdbb_database = &dbb_struct; gdbb->tdbb_transaction = &dull; @@ -252,17 +249,15 @@ int main( int argc, char *argv[]) fclose(dbg_file); if (rbdb) + { RBDB_close(rbdb); - while (rbdb) - { - rbdb* const next_db = rbdb->rbdb_next; if (rbdb->rbdb_buffer1) gds__free(rbdb->rbdb_buffer1); if (rbdb->rbdb_buffer2) gds__free(rbdb->rbdb_buffer2); + gds__free(rbdb); - rbdb = next_db; } return 0; @@ -332,8 +327,7 @@ void RBDB_close( rbdb* rbdb) * Functional description * **************************************/ - for (; rbdb; rbdb = rbdb->rbdb_next) - close(rbdb->rbdb_file.fil_file); + close(rbdb->rbdb_file.fil_file); } @@ -732,36 +726,6 @@ static void format_tip( tx_inv_page* page, int page_size, SLONG next_page) } -static void get_next_file( rbdb* rbdb, header_page* header) -{ -/************************************** - * - * g e t _ n e x t _ f i l e - * - ************************************** - * - * Functional description - * If there's another file as part of - * this database, get it now. - * - **************************************/ - rbdb** next = &rbdb->rbdb_next; - const UCHAR* p = header->hdr_data; - for (const UCHAR* const end = p + header->hdr_page_size; p < end && *p != HDR_end; p += 2 + p[1]) - { - if (*p == HDR_file) - { - rbdb* next_rbdb = (rbdb*) RBDB_alloc(sizeof(struct rbdb) + (SSHORT) p[1]); - next_rbdb->rbdb_file.fil_length = (SSHORT) p[1]; - strncpy(next_rbdb->rbdb_file.fil_name, p + 2, (SSHORT) p[1]); - *next = next_rbdb; - next = &next_rbdb->rbdb_next; - break; - } - } -} - - static void get_range(TEXT*** argv, const TEXT* const* const end, ULONG* lower, ULONG* upper) { /************************************** From 5fc596904c7173d0eed1b3d701a0170df04ae0d1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 28 Dec 2024 20:15:34 +0000 Subject: [PATCH 18/49] 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 7f93b16377..a53b7891c3 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:564 + FORMAL BUILD NUMBER:565 */ -#define PRODUCT_VER_STRING "6.0.0.564" -#define FILE_VER_STRING "WI-T6.0.0.564" -#define LICENSE_VER_STRING "WI-T6.0.0.564" -#define FILE_VER_NUMBER 6, 0, 0, 564 +#define PRODUCT_VER_STRING "6.0.0.565" +#define FILE_VER_STRING "WI-T6.0.0.565" +#define LICENSE_VER_STRING "WI-T6.0.0.565" +#define FILE_VER_NUMBER 6, 0, 0, 565 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "564" +#define FB_BUILD_NO "565" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 79762cc41d..96fb86b0b3 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=564 +BuildNum=565 NowAt=`pwd` cd `dirname $0` From d6ad19aa07deeaac8107a25a9243c5699a3c4ea1 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 22 Dec 2024 23:03:27 -0300 Subject: [PATCH 19/49] Refactor ISQL creating FrontendParser class. --- builds/win32/msvc15/isql_static.vcxproj | 2 + .../win32/msvc15/isql_static.vcxproj.filters | 6 + builds/win32/msvc15/isql_test.vcxproj | 1 + builds/win32/msvc15/isql_test.vcxproj.filters | 3 + src/common/StdHelper.h | 42 + src/common/classes/MetaString.h | 24 + src/isql/FrontendLexer.cpp | 53 +- src/isql/FrontendLexer.h | 14 + src/isql/FrontendParser.cpp | 732 +++++++++ src/isql/FrontendParser.h | 285 ++++ src/isql/isql.epp | 1356 ++++++---------- src/isql/iutils.cpp | 11 + src/isql/iutils_proto.h | 2 + src/isql/show.epp | 1421 ++++++++--------- src/isql/show_proto.h | 3 +- src/isql/tests/FrontendParserTest.cpp | 621 +++++++ src/isql/tests/ISqlTest.cpp | 13 - src/jrd/obj.h | 5 +- 18 files changed, 2916 insertions(+), 1678 deletions(-) create mode 100644 src/common/StdHelper.h create mode 100644 src/isql/FrontendParser.cpp create mode 100644 src/isql/FrontendParser.h create mode 100644 src/isql/tests/FrontendParserTest.cpp diff --git a/builds/win32/msvc15/isql_static.vcxproj b/builds/win32/msvc15/isql_static.vcxproj index 2fe7388b96..647eff13af 100644 --- a/builds/win32/msvc15/isql_static.vcxproj +++ b/builds/win32/msvc15/isql_static.vcxproj @@ -24,6 +24,7 @@ + @@ -40,6 +41,7 @@ + diff --git a/builds/win32/msvc15/isql_static.vcxproj.filters b/builds/win32/msvc15/isql_static.vcxproj.filters index 5605a4551f..285226069d 100644 --- a/builds/win32/msvc15/isql_static.vcxproj.filters +++ b/builds/win32/msvc15/isql_static.vcxproj.filters @@ -30,6 +30,9 @@ ISQL files + + ISQL files + ISQL files @@ -73,6 +76,9 @@ Header files + + Header files + Header files diff --git a/builds/win32/msvc15/isql_test.vcxproj b/builds/win32/msvc15/isql_test.vcxproj index dabd1312a7..d8912dbe15 100644 --- a/builds/win32/msvc15/isql_test.vcxproj +++ b/builds/win32/msvc15/isql_test.vcxproj @@ -177,6 +177,7 @@ + diff --git a/builds/win32/msvc15/isql_test.vcxproj.filters b/builds/win32/msvc15/isql_test.vcxproj.filters index 331157ba2b..26868fd4e6 100644 --- a/builds/win32/msvc15/isql_test.vcxproj.filters +++ b/builds/win32/msvc15/isql_test.vcxproj.filters @@ -10,6 +10,9 @@ source + + source + source diff --git a/src/common/StdHelper.h b/src/common/StdHelper.h new file mode 100644 index 0000000000..c055a1257e --- /dev/null +++ b/src/common/StdHelper.h @@ -0,0 +1,42 @@ +/* + * 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) 2024 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + */ + +#ifndef FB_COMMON_STD_HELPER_H +#define FB_COMMON_STD_HELPER_H + +namespace Firebird { + +// To be used with std::visit + +template +struct StdVisitOverloads : Ts... +{ + using Ts::operator()...; +}; + +template +StdVisitOverloads(Ts...) -> StdVisitOverloads; + +} // namespace Firebird + +#endif // FB_COMMON_STD_HELPER_H diff --git a/src/common/classes/MetaString.h b/src/common/classes/MetaString.h index 251aedd360..6c954d3c07 100644 --- a/src/common/classes/MetaString.h +++ b/src/common/classes/MetaString.h @@ -93,6 +93,30 @@ public: int compare(const AbstractString& s) const { return compare(s.c_str(), s.length()); } int compare(const MetaString& m) const { return memcmp(data, m.data, MAX_SQL_IDENTIFIER_SIZE); } + string toQuotedString() const + { + string s; + + if (hasData()) + { + s.reserve(count + 2); + + s.append("\""); + + for (const auto c : *this) + { + if (c == '"') + s.append("\""); + + s.append(&c, 1); + } + + s.append("\""); + } + + return s; + } + bool operator==(const char* s) const { return compare(s) == 0; } bool operator!=(const char* s) const { return compare(s) != 0; } bool operator==(const AbstractString& s) const { return compare(s) == 0; } diff --git a/src/isql/FrontendLexer.cpp b/src/isql/FrontendLexer.cpp index 5c5a63d911..2a3c3602a0 100644 --- a/src/isql/FrontendLexer.cpp +++ b/src/isql/FrontendLexer.cpp @@ -28,9 +28,7 @@ #include -static std::string trim(std::string_view str); - -static std::string trim(std::string_view str) +std::string FrontendLexer::trim(std::string_view str) { auto finish = str.end(); auto start = str.begin(); @@ -142,7 +140,7 @@ std::variant= term.length() && std::equal(term.begin(), term.end(), pos)) + if (std::size_t(end - pos) >= term.length() && std::equal(term.begin(), term.end(), pos)) { const auto initialStatement = std::string(buffer.cbegin(), pos); pos += term.length(); @@ -224,13 +222,58 @@ FrontendLexer::Token FrontendLexer::getToken() return token; } -std::optional FrontendLexer::getStringToken() +FrontendLexer::Token FrontendLexer::getNameToken() { + skipSpacesAndComments(); + Token token; + if (pos >= end) + { + token.type = Token::TYPE_EOF; + return token; + } + + if (const auto optStringToken = getStringToken(); optStringToken.has_value()) + return optStringToken.value(); + + const auto start = pos; + bool first = true; + + while (pos < end) + { + const auto c = *pos++; + + if (!((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + c == '{' || + c == '}' || + (!first && c >= '0' && c <= '9') || + (!first && c == '$') || + (!first && c == '_'))) + { + if (!first) + --pos; + + break; + } + + first = false; + } + + token.processedText = token.rawText = std::string(start, pos); + std::transform(token.processedText.begin(), token.processedText.end(), + token.processedText.begin(), toupper); + + return token; +} + +std::optional FrontendLexer::getStringToken() +{ if (pos >= end) return std::nullopt; + Token token; const auto start = pos; switch (toupper(*pos)) diff --git a/src/isql/FrontendLexer.h b/src/isql/FrontendLexer.h index d2ac1f88f2..1357da7a14 100644 --- a/src/isql/FrontendLexer.h +++ b/src/isql/FrontendLexer.h @@ -48,6 +48,12 @@ public: Type type = TYPE_OTHER; std::string rawText; std::string processedText; + + std::string getProcessedString() const + { + return type == FrontendLexer::Token::TYPE_STRING || type == FrontendLexer::Token::TYPE_META_STRING ? + processedText : rawText; + } }; struct SingleStatement @@ -74,6 +80,7 @@ public: FrontendLexer& operator=(const FrontendLexer&) = delete; public: + static std::string trim(std::string_view str); static std::string stripComments(std::string_view statement); public: @@ -87,6 +94,11 @@ public: return pos; } + void setPos(std::string::const_iterator newPos) + { + pos = newPos; + } + void rewind() { deletePos = buffer.begin(); @@ -97,7 +109,9 @@ public: void appendBuffer(std::string_view newBuffer); void reset(); std::variant getSingleStatement(std::string_view term); + Token getToken(); + Token getNameToken(); private: std::optional getStringToken(); diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp new file mode 100644 index 0000000000..46735e7350 --- /dev/null +++ b/src/isql/FrontendParser.cpp @@ -0,0 +1,732 @@ +/* + * 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) 2024 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + */ + +#include "firebird.h" +#include "../isql/FrontendParser.h" +#include + + +FrontendParser::AnyNode FrontendParser::internalParse() +{ + static constexpr std::string_view TOKEN_ADD("ADD"); + static constexpr std::string_view TOKEN_BLOBDUMP("BLOBDUMP"); + static constexpr std::string_view TOKEN_BLOBVIEW("BLOBVIEW"); + static constexpr std::string_view TOKEN_CONNECT("CONNECT"); + static constexpr std::string_view TOKEN_COPY("COPY"); + static constexpr std::string_view TOKEN_CREATE("CREATE"); + static constexpr std::string_view TOKEN_DROP("DROP"); + static constexpr std::string_view TOKEN_EDIT("EDIT"); + static constexpr std::string_view TOKEN_EXIT("EXIT"); + static constexpr std::string_view TOKEN_EXPLAIN("EXPLAIN"); + static constexpr std::string_view TOKEN_HELP("HELP"); + static constexpr std::string_view TOKEN_INPUT("INPUT"); + static constexpr std::string_view TOKEN_OUTPUT("OUTPUT"); + static constexpr std::string_view TOKEN_QUIT("QUIT"); + static constexpr std::string_view TOKEN_SET("SET"); + static constexpr std::string_view TOKEN_SHELL("SHELL"); + static constexpr std::string_view TOKEN_SHOW("SHOW"); + + const auto commandToken = lexer.getToken(); + + if (commandToken.type == Token::TYPE_OTHER) + { + const auto& command = commandToken.processedText; + + if (command == TOKEN_ADD) + { + if (const auto tableName = parseName()) + { + AddNode node; + node.tableName = std::move(tableName.value()); + + if (parseEof()) + return node; + } + } + else if (command == TOKEN_BLOBDUMP || command == TOKEN_BLOBVIEW) + { + if (const auto blobId = lexer.getToken(); blobId.type != Token::TYPE_EOF) + { + BlobDumpViewNode node; + + // Find the high and low values of the blob id + if (blobId.processedText.empty()) + return InvalidNode(); + + sscanf(blobId.processedText.c_str(), "%" xLONGFORMAT":%" xLONGFORMAT, + &node.blobId.gds_quad_high, &node.blobId.gds_quad_low); + + if (command == TOKEN_BLOBDUMP) + { + if (const auto file = parseFileName()) + { + node.file = std::move(file.value()); + + if (parseEof()) + return node; + } + } + else + { + if (parseEof()) + return node; + } + } + } + else if (command == TOKEN_CONNECT) + { + ConnectNode node; + + do + { + const auto token = lexer.getToken(); + + if (token.type == Token::TYPE_EOF) + { + if (node.args.empty()) + break; + else + return node; + } + else if (token.type != Token::TYPE_OTHER && + token.type != Token::TYPE_STRING && + token.type != Token::TYPE_META_STRING) + { + return InvalidNode(); + } + + node.args.push_back(std::move(token)); + } while(true); + } + else if (command == TOKEN_COPY) + { + CopyNode node; + + if (const auto source = parseName()) + node.source = std::move(source.value()); + else + return InvalidNode(); + + if (const auto destination = parseName()) + node.destination = std::move(destination.value()); + else + return InvalidNode(); + + if (const auto database = parseFileName()) + node.database = std::move(database.value()); + else + return InvalidNode(); + + if (parseEof()) + return node; + } + else if (command == TOKEN_CREATE) + { + if (const auto createWhat = lexer.getToken(); + createWhat.type == Token::TYPE_OTHER && + (createWhat.processedText == "DATABASE" || + (options.schemaAsDatabase && createWhat.processedText == "SCHEMA"))) + { + CreateDatabaseNode node; + + do + { + const auto token = lexer.getToken(); + + if (token.type == Token::TYPE_EOF) + { + if (node.args.empty()) + break; + else + return node; + } + else if (token.type != Token::TYPE_OTHER && + token.type != Token::TYPE_STRING && + token.type != Token::TYPE_META_STRING) + { + return InvalidNode(); + } + + node.args.push_back(std::move(token)); + } while(true); + } + } + else if (command == TOKEN_DROP) + { + if (const auto dropWhat = lexer.getToken(); + dropWhat.type == Token::TYPE_OTHER && + (dropWhat.processedText == "DATABASE" || + (options.schemaAsDatabase && dropWhat.processedText == "SCHEMA"))) + { + if (parseEof()) + return DropDatabaseNode(); + } + } + else if (command == TOKEN_EDIT) + { + EditNode node; + node.file = parseFileName(); + + if (parseEof()) + return node; + } + else if (command == TOKEN_EXIT) + { + if (parseEof()) + return ExitNode(); + } + else if (command == TOKEN_EXPLAIN) + { + ExplainNode node; + + if (const auto query = parseUtilEof()) + { + node.query = std::move(query.value()); + return node; + } + } + else if (command == TOKEN_HELP || command == "?") + { + HelpNode node; + + if (const auto token = lexer.getToken(); token.type == Token::TYPE_EOF) + return node; + else if (token.type == Token::TYPE_OTHER) + { + node.command = token.processedText; + + if (parseEof()) + return node; + } + } + else if (command.length() >= 2 && TOKEN_INPUT.find(command) == 0) + { + if (const auto file = parseFileName()) + { + InputNode node; + node.file = std::move(file.value()); + + if (parseEof()) + return node; + } + } + else if (command.length() >= 3 && TOKEN_OUTPUT.find(command) == 0) + { + OutputNode node; + node.file = parseFileName(); + + if (parseEof()) + return node; + } + else if (command == TOKEN_QUIT) + { + if (parseEof()) + return QuitNode(); + } + else if (command == TOKEN_SET) + { + if (const auto setNode = parseSet(); !std::holds_alternative(setNode)) + return setNode; + } + else if (command == TOKEN_SHELL) + { + ShellNode node; + node.command = parseUtilEof(); + return node; + } + else if (command == TOKEN_SHOW) + { + if (const auto showNode = parseShow(); !std::holds_alternative(showNode)) + return showNode; + } + } + + return InvalidNode(); +} + +FrontendParser::AnySetNode FrontendParser::parseSet() +{ + static constexpr std::string_view TOKEN_AUTODDL("AUTODDL"); + static constexpr std::string_view TOKEN_AUTOTERM("AUTOTERM"); + static constexpr std::string_view TOKEN_BAIL("BAIL"); + static constexpr std::string_view TOKEN_BLOBDISPLAY("BLOBDISPLAY"); + static constexpr std::string_view TOKEN_BULK_INSERT("BULK_INSERT"); + static constexpr std::string_view TOKEN_COUNT("COUNT"); + static constexpr std::string_view TOKEN_ECHO("ECHO"); + static constexpr std::string_view TOKEN_EXEC_PATH_DISPLAY("EXEC_PATH_DISPLAY"); + static constexpr std::string_view TOKEN_EXPLAIN("EXPLAIN"); + static constexpr std::string_view TOKEN_HEADING("HEADING"); + static constexpr std::string_view TOKEN_KEEP_TRAN_PARAMS("KEEP_TRAN_PARAMS"); + static constexpr std::string_view TOKEN_LIST("LIST"); + static constexpr std::string_view TOKEN_LOCAL_TIMEOUT("LOCAL_TIMEOUT"); + static constexpr std::string_view TOKEN_MAXROWS("MAXROWS"); + static constexpr std::string_view TOKEN_NAMES("NAMES"); + static constexpr std::string_view TOKEN_PER_TABLE_STATS("PER_TABLE_STATS"); + static constexpr std::string_view TOKEN_PLAN("PLAN"); + static constexpr std::string_view TOKEN_PLANONLY("PLANONLY"); + static constexpr std::string_view TOKEN_ROWCOUNT("ROWCOUNT"); + static constexpr std::string_view TOKEN_SQL("SQL"); + static constexpr std::string_view TOKEN_SQLDA_DISPLAY("SQLDA_DISPLAY"); + static constexpr std::string_view TOKEN_STATS("STATS"); + static constexpr std::string_view TOKEN_TERMINATOR("TERMINATOR"); + static constexpr std::string_view TOKEN_TIME("TIME"); + static constexpr std::string_view TOKEN_TRANSACTION("TRANSACTION"); + static constexpr std::string_view TOKEN_WARNINGS("WARNINGS"); + static constexpr std::string_view TOKEN_WIDTH("WIDTH"); + static constexpr std::string_view TOKEN_WNG("WNG"); + static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS"); + + switch (const auto setCommandToken = lexer.getToken(); setCommandToken.type) + { + case Token::TYPE_EOF: + return SetNode(); + + case Token::TYPE_OTHER: + { + const auto& text = setCommandToken.processedText; + + if (const auto parsed = parseSet(text, TOKEN_AUTODDL, 4)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_AUTOTERM)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_BAIL)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_BLOBDISPLAY, 4)) + return parsed.value(); + else if (text == TOKEN_BULK_INSERT) + { + SetBulkInsertNode node; + + if (const auto statement = parseUtilEof()) + { + node.statement = statement.value(); + return node; + } + } + else if (const auto parsed = parseSet(text, TOKEN_COUNT)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_ECHO)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_EXEC_PATH_DISPLAY)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_EXPLAIN)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_HEADING)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_KEEP_TRAN_PARAMS, 9)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_LIST)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_LOCAL_TIMEOUT)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_MAXROWS)) + return parsed.value(); + else if (text == TOKEN_NAMES) + { + SetNamesNode node; + node.name = parseName(); + + if (parseEof()) + return node; + } + else if (const auto parsed = parseSet(text, TOKEN_PER_TABLE_STATS, 7)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_PLAN)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_PLANONLY)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_ROWCOUNT)) + return parsed.value(); + else if (text == TOKEN_SQL) + { + SetSqlDialectNode node; + + if (const auto dialectToken = lexer.getToken(); + dialectToken.type == Token::TYPE_OTHER && dialectToken.processedText == "DIALECT") + { + if (const auto arg = lexer.getToken(); arg.type != Token::TYPE_EOF) + { + node.arg = arg.processedText; + + if (parseEof()) + return node; + } + } + } + else if (const auto parsed = parseSet(text, TOKEN_SQLDA_DISPLAY)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_STATS, 4)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_TERMINATOR, 4, false)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_TIME)) + { + if (const auto setTimeNode = std::get_if(&parsed.value()); + setTimeNode && setTimeNode->arg == "ZONE") + { + return InvalidNode(); + } + + return parsed.value(); + } + else if (text.length() >= 5 && std::string(TOKEN_TRANSACTION).find(text) == 0) + { + SetTransactionNode node; + node.statement = lexer.getBuffer(); + return node; + } + else if (const auto parsed = parseSet(text, TOKEN_WARNINGS, 7)) + return parsed.value(); + else if (const auto parsed = parseSet(text, TOKEN_WNG)) + return parsed.value(); + else if (text == TOKEN_WIDTH) + { + SetWidthNode node; + + if (const auto column = lexer.getToken(); column.type != Token::TYPE_EOF) + { + node.column = column.processedText; + + if (const auto width = lexer.getToken(); width.type != Token::TYPE_EOF) + { + node.width = width.processedText; + + if (!parseEof()) + return InvalidNode(); + } + + return node; + } + } + else if (const auto parsed = parseSet(text, TOKEN_WIRE_STATS, 4)) + return parsed.value(); + + break; + } + } + + return InvalidNode(); +} + +template +std::optional FrontendParser::parseSet(std::string_view setCommand, + std::string_view testCommand, unsigned testCommandMinLen, bool useProcessedText) +{ + if (setCommand == testCommand || + (testCommandMinLen && setCommand.length() >= testCommandMinLen && + std::string(testCommand).find(setCommand) == 0)) + { + Node node; + + if (const auto arg = lexer.getToken(); arg.type != Token::TYPE_EOF) + { + node.arg = useProcessedText ? arg.processedText : arg.rawText; + + if (!parseEof()) + return InvalidNode(); + } + + return node; + } + + return std::nullopt; +} + +FrontendParser::AnyShowNode FrontendParser::parseShow() +{ + static constexpr std::string_view TOKEN_CHECKS("CHECKS"); + static constexpr std::string_view TOKEN_COLLATES("COLLATES"); + static constexpr std::string_view TOKEN_COLLATIONS("COLLATIONS"); + static constexpr std::string_view TOKEN_COMMENTS("COMMENTS"); + static constexpr std::string_view TOKEN_DATABASE("DATABASE"); + static constexpr std::string_view TOKEN_DEPENDENCIES("DEPENDENCIES"); + static constexpr std::string_view TOKEN_DEPENDENCY("DEPENDENCY"); + static constexpr std::string_view TOKEN_DOMAINS("DOMAINS"); + static constexpr std::string_view TOKEN_EXCEPTIONS("EXCEPTIONS"); + static constexpr std::string_view TOKEN_FILTERS("FILTERS"); + static constexpr std::string_view TOKEN_FUNCTIONS("FUNCTIONS"); + static constexpr std::string_view TOKEN_INDEXES("INDEXES"); + static constexpr std::string_view TOKEN_INDICES("INDICES"); + static constexpr std::string_view TOKEN_GENERATORS("GENERATORS"); + static constexpr std::string_view TOKEN_GRANTS("GRANTS"); + static constexpr std::string_view TOKEN_MAPPINGS("MAPPINGS"); + static constexpr std::string_view TOKEN_PACKAGES("PACKAGES"); + static constexpr std::string_view TOKEN_PROCEDURES("PROCEDURES"); + static constexpr std::string_view TOKEN_PUBLICATIONS("PUBLICATIONS"); + static constexpr std::string_view TOKEN_ROLES("ROLES"); + static constexpr std::string_view TOKEN_SECCLASSES("SECCLASSES"); + static constexpr std::string_view TOKEN_SEQUENCES("SEQUENCES"); + static constexpr std::string_view TOKEN_SQL("SQL"); + static constexpr std::string_view TOKEN_SYSTEM("SYSTEM"); + static constexpr std::string_view TOKEN_TABLES("TABLES"); + static constexpr std::string_view TOKEN_TRIGGERS("TRIGGERS"); + static constexpr std::string_view TOKEN_USERS("USERS"); + static constexpr std::string_view TOKEN_VER("VER"); + static constexpr std::string_view TOKEN_VERSION("VERSION"); + static constexpr std::string_view TOKEN_VIEWS("VIEWS"); + static constexpr std::string_view TOKEN_WIRE_STATISTICS("WIRE_STATISTICS"); + static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS"); + + switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type) + { + case Token::TYPE_EOF: + return ShowNode(); + + case Token::TYPE_OTHER: + { + const auto& text = showCommandToken.processedText; + + if (const auto parsed = parseShowOptName(text, TOKEN_CHECKS, 5)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_COLLATES, 7)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_COLLATIONS, 9)) + return parsed.value(); + else if (text.length() >= 7 && std::string(TOKEN_COMMENTS).find(text) == 0) + { + if (parseEof()) + return ShowCommentsNode(); + } + else if (text == TOKEN_DATABASE) + { + if (parseEof()) + return ShowDatabaseNode(); + } + else if (const auto parsed = parseShowOptName(text, TOKEN_DEPENDENCIES, 5)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_DEPENDENCY, 5)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_DOMAINS, 6)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_EXCEPTIONS, 5)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_FILTERS, 6)) + return parsed.value(); + else if (text.length() >= 4 && std::string(TOKEN_FUNCTIONS).find(text) == 0) + { + ShowFunctionsNode node; + node.name = parseName(); + + if (node.name) + { + if (const auto token = lexer.getNameToken(); + token.type == Token::TYPE_OTHER && token.rawText == ".") + { + node.package = node.name; + node.name = parseName(); + + if (parseEof()) + return node; + } + else if (token.type == Token::TYPE_EOF) + { + return node; + } + } + else + return node; + } + else if (const auto parsed = parseShowOptName(text, TOKEN_INDEXES, 3)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_INDICES, 0)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_GENERATORS, 3)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_GRANTS, 5)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_MAPPINGS, 3)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_PACKAGES, 4)) + return parsed.value(); + else if (text.length() >= 4 && std::string(TOKEN_PROCEDURES).find(text) == 0) + { + ShowProceduresNode node; + node.name = parseName(); + + if (node.name) + { + if (const auto token = lexer.getNameToken(); + token.type == Token::TYPE_OTHER && token.rawText == ".") + { + node.package = node.name; + node.name = parseName(); + + if (parseEof()) + return node; + } + else if (token.type == Token::TYPE_EOF) + { + return node; + } + } + else + return node; + } + else if (const auto parsed = parseShowOptName(text, TOKEN_PUBLICATIONS, 3)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_ROLES, 4)) + return parsed.value(); + else if (text.length() >= 6 && std::string(TOKEN_SECCLASSES).find(text) == 0) + { + const auto lexerPos = lexer.getPos(); + const auto token = lexer.getNameToken(); + + ShowSecClassesNode node; + + if (!(token.type == Token::TYPE_OTHER && token.rawText == "*")) + { + lexer.setPos(lexerPos); + node.name = parseName(); + + if (!node.name) + return InvalidNode(); + } + + const auto optDetail = parseName(); + node.detail = optDetail == "DET" || optDetail == "DETAIL"; + + if (!node.detail && optDetail) + return InvalidNode(); + + if (parseEof()) + return node; + } + else if (const auto parsed = parseShowOptName(text, TOKEN_SEQUENCES, 3)) + return parsed.value(); + else if (text == TOKEN_SQL) + { + if (const auto dialectToken = lexer.getToken(); + dialectToken.type == Token::TYPE_OTHER && dialectToken.processedText == "DIALECT") + { + if (parseEof()) + return ShowSqlDialectNode(); + } + } + else if (text.length() >= 3 && std::string(TOKEN_SYSTEM).find(text) == 0) + { + ShowSystemNode node; + + if (const auto objectType = parseName()) + { + const auto objectTypeText = std::string(objectType->c_str()); + + if ((objectTypeText.length() >= 7 && std::string(TOKEN_COLLATES).find(objectTypeText) == 0) || + (objectTypeText.length() >= 9 && std::string(TOKEN_COLLATIONS).find(objectTypeText) == 0)) + { + node.objType = obj_collation; + } + else if (objectTypeText.length() >= 4 && std::string(TOKEN_FUNCTIONS).find(objectTypeText) == 0) + node.objType = obj_udf; + else if (objectTypeText.length() >= 5 && std::string(TOKEN_TABLES).find(objectTypeText) == 0) + node.objType = obj_relation; + else if (objectTypeText.length() >= 4 && std::string(TOKEN_ROLES).find(objectTypeText) == 0) + node.objType = obj_sql_role; + else if (objectTypeText.length() >= 4 && std::string(TOKEN_PROCEDURES).find(objectTypeText) == 0) + node.objType = obj_procedure; + else if (objectTypeText.length() >= 4 && std::string(TOKEN_PACKAGES).find(objectTypeText) == 0) + node.objType = obj_package_header; + else if (objectTypeText.length() >= 3 && std::string(TOKEN_PUBLICATIONS).find(objectTypeText) == 0) + node.objType = obj_publication; + else + return InvalidNode(); + + if (!parseEof()) + return InvalidNode(); + } + + return node; + } + else if (const auto parsed = parseShowOptName(text, TOKEN_TABLES, 5)) + return parsed.value(); + else if (const auto parsed = parseShowOptName(text, TOKEN_TRIGGERS, 4)) + return parsed.value(); + else if (text == TOKEN_USERS) + { + if (parseEof()) + return ShowUsersNode(); + } + else if (text == TOKEN_VER || text == TOKEN_VERSION) + { + if (parseEof()) + return ShowVersionNode(); + } + else if (const auto parsed = parseShowOptName(text, TOKEN_VIEWS, 4)) + return parsed.value(); + else if (text.length() >= 9 && std::string(TOKEN_WIRE_STATISTICS).find(text) == 0 || + text == TOKEN_WIRE_STATS) + { + if (parseEof()) + return ShowWireStatsNode(); + } + + break; + } + } + + return InvalidNode(); +} + +template +std::optional FrontendParser::parseShowOptName(std::string_view showCommand, + std::string_view testCommand, unsigned testCommandMinLen) +{ + if (showCommand == testCommand || + (testCommandMinLen && showCommand.length() >= testCommandMinLen && + std::string(testCommand).find(showCommand) == 0)) + { + Node node; + node.name = parseName(); + + if (!parseEof()) + return InvalidNode(); + + return node; + } + + return std::nullopt; +} + +std::optional FrontendParser::parseUtilEof() +{ + const auto startPos = lexer.getPos(); + auto lastPos = startPos; + bool first = true; + + do + { + const auto token = lexer.getToken(); + + if (token.type == Token::TYPE_EOF) + { + if (first) + return std::nullopt; + + return FrontendLexer::trim(std::string(startPos, lastPos)); + } + + lastPos = lexer.getPos(); + first = false; + } while (true); + + return std::nullopt; +} diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h new file mode 100644 index 0000000000..940c94f5f7 --- /dev/null +++ b/src/isql/FrontendParser.h @@ -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) 2024 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + */ + +#ifndef FB_ISQL_FRONTEND_PARSER_H +#define FB_ISQL_FRONTEND_PARSER_H + +#include "../isql/FrontendLexer.h" +#include "../jrd/obj.h" +#include "../common/classes/MetaString.h" +#include +#include +#include +#include +#include + +class FrontendParser +{ +private: + using Token = FrontendLexer::Token; + +public: + struct Options + { + bool schemaAsDatabase = false; + }; + + struct InvalidNode {}; + + struct AddNode { Firebird::MetaString tableName; }; + struct BlobDumpViewNode { ISC_QUAD blobId; std::optional file; }; + struct ConnectNode { std::vector args; }; + struct CopyNode { Firebird::MetaString source; Firebird::MetaString destination; std::string database; }; + struct CreateDatabaseNode { std::vector args; }; + struct DropDatabaseNode {}; + struct EditNode { std::optional file; }; + struct ExitNode {}; + struct ExplainNode { std::string query; }; + struct HelpNode { std::optional command; }; + struct InputNode { std::string file; }; + struct OutputNode { std::optional file; }; + struct QuitNode {}; + struct ShellNode { std::optional command; }; + + struct SetNode {}; + struct SetAutoDdlNode { std::string arg; }; + struct SetAutoTermNode { std::string arg; }; + struct SetBailNode { std::string arg; }; + struct SetBlobDisplayNode { std::string arg; }; + struct SetBulkInsertNode { std::string statement; }; + struct SetCountNode { std::string arg; }; + struct SetEchoNode { std::string arg; }; + struct SetExecPathDisplayNode { std::string arg; }; + struct SetExplainNode { std::string arg; }; + struct SetHeadingNode { std::string arg; }; + struct SetKeepTranParamsNode { std::string arg; }; + struct SetListNode { std::string arg; }; + struct SetLocalTimeoutNode { std::string arg; }; + struct SetMaxRowsNode { std::string arg; }; + struct SetNamesNode { std::optional name; }; + struct SetPerTableStatsNode { std::string arg; }; + struct SetPlanNode { std::string arg; }; + struct SetPlanOnlyNode { std::string arg; }; + struct SetSqldaDisplayNode { std::string arg; }; + struct SetSqlDialectNode { std::string arg; }; + struct SetStatsNode { std::string arg; }; + struct SetTermNode { std::string arg; }; + struct SetTimeNode { std::string arg; }; + struct SetTransactionNode { std::string statement; }; + struct SetWarningsNode { std::string arg; }; + struct SetWidthNode { std::string column; std::string width; }; + struct SetWireStatsNode { std::string arg; }; + + struct ShowNode {}; + struct ShowChecksNode { std::optional name; }; + struct ShowCollationsNode { std::optional name; }; + struct ShowCommentsNode {}; + struct ShowDatabaseNode {}; + struct ShowDomainsNode { std::optional name; }; + struct ShowDependenciesNode { std::optional name; }; + struct ShowExceptionsNode { std::optional name; }; + struct ShowFiltersNode { std::optional name; }; + struct ShowFunctionsNode { std::optional name; std::optional package; }; + struct ShowGeneratorsNode { std::optional name; }; + struct ShowGrantsNode { std::optional name; }; + struct ShowIndexesNode { std::optional name; }; + struct ShowMappingsNode { std::optional name; }; + struct ShowPackagesNode { std::optional name; }; + struct ShowProceduresNode { std::optional name; std::optional package; }; + struct ShowPublicationsNode { std::optional name; }; + struct ShowRolesNode { std::optional name; }; + struct ShowSecClassesNode { std::optional name; bool detail = false; }; + struct ShowSqlDialectNode {}; + struct ShowSystemNode { std::optional objType; }; + struct ShowTablesNode { std::optional name; }; + struct ShowTriggersNode { std::optional name; }; + struct ShowUsersNode {}; + struct ShowVersionNode {}; + struct ShowViewsNode { std::optional name; }; + struct ShowWireStatsNode {}; + + using AnySetNode = std::variant< + SetNode, + + SetAutoDdlNode, + SetAutoTermNode, + SetBailNode, + SetBlobDisplayNode, + SetBulkInsertNode, + SetCountNode, + SetEchoNode, + SetExecPathDisplayNode, + SetExplainNode, + SetHeadingNode, + SetKeepTranParamsNode, + SetListNode, + SetLocalTimeoutNode, + SetMaxRowsNode, + SetNamesNode, + SetPerTableStatsNode, + SetPlanNode, + SetPlanOnlyNode, + SetSqldaDisplayNode, + SetSqlDialectNode, + SetStatsNode, + SetTermNode, + SetTimeNode, + SetTransactionNode, + SetWarningsNode, + SetWidthNode, + SetWireStatsNode, + + InvalidNode + >; + + using AnyShowNode = std::variant< + ShowNode, + + ShowChecksNode, + ShowCollationsNode, + ShowCommentsNode, + ShowDatabaseNode, + ShowDomainsNode, + ShowDependenciesNode, + ShowExceptionsNode, + ShowFiltersNode, + ShowFunctionsNode, + ShowGeneratorsNode, + ShowGrantsNode, + ShowIndexesNode, + ShowMappingsNode, + ShowPackagesNode, + ShowProceduresNode, + ShowPublicationsNode, + ShowRolesNode, + ShowSecClassesNode, + ShowSqlDialectNode, + ShowSystemNode, + ShowTablesNode, + ShowTriggersNode, + ShowUsersNode, + ShowVersionNode, + ShowViewsNode, + ShowWireStatsNode, + + InvalidNode + >; + + using AnyNode = std::variant< + AddNode, + BlobDumpViewNode, + ConnectNode, + CopyNode, + CreateDatabaseNode, + DropDatabaseNode, + EditNode, + ExitNode, + ExplainNode, + HelpNode, + InputNode, + OutputNode, + QuitNode, + ShellNode, + + AnySetNode, + AnyShowNode, + + InvalidNode + >; + + template + static inline constexpr bool AlwaysFalseV = false; + +public: + FrontendParser(std::string_view statement, const Options& aOptions) + : lexer(statement), + options(aOptions) + { + } + + FrontendParser(const FrontendParser&) = delete; + FrontendParser& operator=(const FrontendParser&) = delete; + +public: + static AnyNode parse(std::string_view statement, const Options& options) + { + try + { + FrontendParser parser(statement, options); + return parser.internalParse(); + } + catch (const FrontendLexer::IncompleteTokenError&) + { + return InvalidNode(); + } + } + +private: + AnyNode internalParse(); + AnySetNode parseSet(); + + template + std::optional parseSet(std::string_view setCommand, + std::string_view testCommand, unsigned testCommandMinLen = 0, bool useProcessedText = true); + + AnyShowNode parseShow(); + + template + std::optional parseShowOptName(std::string_view showCommand, + std::string_view testCommand, unsigned testCommandMinLen = 0); + + bool parseEof() + { + return lexer.getToken().type == Token::TYPE_EOF; + } + + std::optional parseName() + { + const auto token = lexer.getNameToken(); + + if (token.type != Token::TYPE_EOF) + return Firebird::MetaString(token.processedText.c_str()); + + return std::nullopt; + } + + std::optional parseFileName() + { + const auto token = lexer.getToken(); + + if (token.type == Token::TYPE_STRING || token.type == Token::TYPE_META_STRING) + return token.processedText; + else if (token.type != Token::TYPE_EOF) + return token.rawText; + + return std::nullopt; + } + + std::optional parseUtilEof(); + +private: + FrontendLexer lexer; + const Options options; +}; + +#endif // FB_ISQL_FRONTEND_PARSER_H diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 8de2929f97..7370e160fe 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -55,6 +55,8 @@ #include #include #include "../isql/FrontendLexer.h" +#include "../isql/FrontendParser.h" +#include "../common/StdHelper.h" #include "../common/utils_proto.h" #include "../common/classes/array.h" #include "../common/classes/init.h" @@ -450,30 +452,24 @@ struct ri_actions const SCHAR* ri_action_print_mixed; }; -static processing_state add_row(TEXT*); -static processing_state blobedit(const TEXT*, const TEXT* const*); +static processing_state add_row(const MetaString&); +static processing_state blobedit(ISC_QUAD, const char*); static processing_state bulk_insert_hack(const char* command); static bool bulk_insert_retriever(const char* prompt); static void check_autoterm(); static bool check_date(const tm& times); static bool check_time(const tm& times); static bool check_timestamp(const tm& times, const int msec); -static size_t chop_at(char target[], const size_t size); -static void col_check(const TEXT*, unsigned*); -static processing_state copy_table(TEXT*, TEXT*, TEXT*); -static processing_state create_db(const TEXT*, TEXT*); +static void col_check(const MetaString&, unsigned*); +static processing_state copy_table(const MetaString&, const MetaString&, const TEXT*); +static processing_state create_db(const FrontendParser::CreateDatabaseNode& node); static void do_isql(); static processing_state drop_db(); -static processing_state edit(const TEXT* const*); +static processing_state edit(const TEXT*); static processing_state end_trans(); static processing_state escape(const TEXT*); static processing_state execSetDebugCommand(); -static processing_state frontend(const TEXT*); -static processing_state frontend_set(const char* cmd, const char* const* parms, - const char* const* lparms, char* const bad_dialect_buf, bool& bad_dialect); -static void frontend_free_parms(TEXT*[], TEXT*[], TEXT parm_defaults[][1]); -static void frontend_load_parms(const TEXT* p, TEXT* parms[], TEXT* lparms[], - TEXT parm_defaults[][1]); +static processing_state frontend(const std::string&); static processing_state do_set_command(const TEXT*, bool*); static processing_state get_dialect(const char* const dialect_str, char* const bad_dialect_buf, bool& bad_dialect); @@ -483,16 +479,13 @@ static processing_state print_sets(); static processing_state explain(const TEXT*); static processing_state help(const TEXT*); static bool isyesno(const TEXT*); -static processing_state newdb(TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool); +static processing_state newdb(const TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool); static processing_state newinput(const TEXT*); static processing_state newoutput(const TEXT*); static processing_state newsize(const TEXT*, const TEXT*); static processing_state newMaxRows(const TEXT* newMaxRowsStr); static processing_state newtrans(const TEXT*); static processing_state parse_arg(int, SCHAR**, SCHAR*); //, FILE**); -#ifdef DEV_BUILD -static processing_state passthrough(const char* cmd); -#endif static unsigned print_item(TEXT**, const IsqlVar*, const unsigned); static void print_item_numeric(SINT64, int, int, TEXT*); static processing_state print_line(Firebird::IMessageMetadata*, UCHAR*, const unsigned pad[], TEXT line[]); @@ -2388,7 +2381,7 @@ static string get_numeric_value(const char* fromStr) } -static processing_state add_row(TEXT* tabname) +static processing_state add_row(const MetaString& tabname) { /************************************** * @@ -2407,7 +2400,7 @@ static processing_state add_row(TEXT* tabname) * tabname -- Name of table to insert into * **************************************/ - if (!*tabname) + if (tabname.isEmpty()) return (ps_ERR); if (!Interactive) @@ -2428,13 +2421,9 @@ static processing_state add_row(TEXT* tabname) return FAIL; } - chop_at(tabname, QUOTED_NAME_SIZE); - if (tabname[0] != DBL_QUOTE) - IUTILS_make_upper(tabname); - // Query to obtain relation information string str2; - str2.printf("SELECT * FROM %s", tabname); + str2.printf("SELECT * FROM %s", IUTILS_name_to_string(tabname).c_str()); if (global_Stmt) { @@ -2472,7 +2461,7 @@ static processing_state add_row(TEXT* tabname) // There is a question mark for each column that's known to be updatable. - insertstring.printf("INSERT INTO %s (", tabname); + insertstring.printf("INSERT INTO %s (", IUTILS_name_to_string(tabname).c_str()); unsigned i_cols = 0; { // scope @@ -2999,7 +2988,7 @@ static processing_state add_row(TEXT* tabname) } -static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) +static processing_state blobedit(ISC_QUAD blobId, const char* path) { /************************************** * @@ -3016,20 +3005,14 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) if (!ISQL_dbcheck()) return FAIL; - if (*cmd[1] == 0) + if (blobId.gds_quad_high == 0 && blobId.gds_quad_low == 0) return ps_ERR; - const TEXT* p = cmd[1]; - - // Find the high and low values of the blob id - ISC_QUAD blobid; - sscanf(p, "%" xLONGFORMAT":%" xLONGFORMAT, &blobid.gds_quad_high, &blobid.gds_quad_low); - // If it isn't an explicit blobedit, then do a dump. Since this is a // user operation, put it on the M__trans handle. processing_state rc = SKIP; - if (!strcmp(action, "BLOBVIEW")) + if (!path) { Firebird::UtilInterfacePtr utl; PathName tmpf = TempFile::create(fbStatus, "blob"); @@ -3037,7 +3020,7 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) return ps_ERR; const char* filename = tmpf.c_str(); - utl->dumpBlob(fbStatus, &blobid, DB, M__trans, filename, FB_TRUE); + utl->dumpBlob(fbStatus, &blobId, DB, M__trans, filename, FB_TRUE); if (ISQL_errmsg(fbStatus)) rc = ps_ERR; else @@ -3045,14 +3028,12 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) unlink(filename); } - else if ((!strcmp(action, "BLOBDUMP")) && (*cmd[2])) + else if (*path) { // If this is a blobdump, make sure there is a file name // We can't be sure if the BLOB is TEXT or BINARY data, // as we're not sure, we'll dump it in BINARY mode. - TEXT path[MAXPATHLEN]; - strip_quotes(cmd[2], path); - Firebird::UtilInterfacePtr()->dumpBlob(fbStatus, &blobid, DB, M__trans, path, FB_FALSE); + Firebird::UtilInterfacePtr()->dumpBlob(fbStatus, &blobId, DB, M__trans, path, FB_FALSE); } else rc = ps_ERR; @@ -3096,14 +3077,6 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) // If you came here looking for robust parsing code, you're at the wrong place. static processing_state bulk_insert_hack(const char* command) { - // Skip "SET BULK_INSERT" part. - for (int j = 0; j < 2; ++j) - { - while (*command && *command != 0x20) - ++command; - while (*command && *command == 0x20) - ++command; - } if (!*command) return ps_ERR; @@ -4095,23 +4068,7 @@ static bool check_timestamp(const tm& times, const int msec) } -// ************* -// c h o p _ a t -// ************* -// Simply ensure a given string argument fits in a size, terminator included. -static size_t chop_at(char target[], const size_t size) -{ - size_t len = strlen(target); - if (len >= size) - { - len = size - 1; - target[len] = 0; - } - return len; -} - - -static void col_check(const TEXT* tabname, unsigned* colnumber) +static void col_check(const MetaString& tabname, unsigned* colnumber) { /************************************** * @@ -4132,7 +4089,7 @@ static void col_check(const TEXT* tabname, unsigned* colnumber) FOR F IN RDB$FIELDS CROSS R IN RDB$RELATION_FIELDS WITH F.RDB$FIELD_NAME = R.RDB$FIELD_SOURCE AND - R.RDB$RELATION_NAME EQ tabname + R.RDB$RELATION_NAME EQ tabname.c_str() SORTED BY R.RDB$FIELD_POSITION, R.RDB$FIELD_NAME if ((!F.RDB$DIMENSIONS.NULL && F.RDB$DIMENSIONS) || (!F.RDB$COMPUTED_BLR.NULL)) @@ -4147,9 +4104,7 @@ static void col_check(const TEXT* tabname, unsigned* colnumber) } -static processing_state copy_table(TEXT* source, - TEXT* destination, - TEXT* otherdb) +static processing_state copy_table(const MetaString& source, const MetaString& destination, const TEXT* otherdb) { /************************************** * @@ -4164,7 +4119,7 @@ static processing_state copy_table(TEXT* source, * destination == name of newly created table * **************************************/ - if (!source[0] || !destination[0]) + if (source.isEmpty() || destination.isEmpty()) { STDERROUT("Either source or destination tables are missing"); return SKIP; @@ -4193,43 +4148,9 @@ static processing_state copy_table(TEXT* source, return END; } - chop_at(source, QUOTED_NAME_SIZE); - if (source[0] != DBL_QUOTE) - IUTILS_make_upper(source); - /* - chop_at(source_tbl, MAX_SQL_IDENTIFIER_SIZE); - TEXT source[QUOTED_NAME_SIZE]; - bool delimited_yes = source_tbl[0] == DBL_QUOTE; - if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) { - IUTILS_copy_SQL_id(source_tbl, source, DBL_QUOTE); - } - else + if (EXTRACT_list_table(source.c_str(), destination.c_str(), domain_flag, -1)) { - strcpy(source, source_tbl); - IUTILS_make_upper(source); - } - */ - - chop_at(destination, QUOTED_NAME_SIZE); - if (destination[0] != DBL_QUOTE) - IUTILS_make_upper(destination); - /* - chop_at(destination_tbl, MAX_SQL_IDENTIFIER_SIZE); - TEXT destination[QUOTED_NAME_SIZE]; - delimited_yes = destination_tbl[0] == DBL_QUOTE; - if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) { - IUTILS_copy_SQL_id(destination_tbl, destination, DBL_QUOTE); - } - else - { - strcpy(destination, destination_tbl); - IUTILS_make_upper(destination); - } - */ - - if (EXTRACT_list_table(source, destination, domain_flag, -1)) - { - IUTILS_msg_get(NOT_FOUND, errbuf, SafeArg() << source); + IUTILS_msg_get(NOT_FOUND, errbuf, SafeArg() << source.c_str()); STDERROUT(errbuf); fclose(isqlGlob.Out); } @@ -4245,7 +4166,7 @@ static processing_state copy_table(TEXT* source, sprintf(cmd, "isql -q %s -i %s", altdb, ftmp.c_str()); if (system(cmd)) { - IUTILS_msg_get(COPY_ERR, errbuf, SafeArg() << destination << altdb); + IUTILS_msg_get(COPY_ERR, errbuf, SafeArg() << destination.c_str() << altdb); STDERROUT(errbuf); } } @@ -4257,21 +4178,7 @@ static processing_state copy_table(TEXT* source, } -static void appendClause(string& to, const char* label, const TEXT* value, char quote = 0) -{ - to += ' '; - to += label; - to += ' '; - if (quote) - to += quote; - to += value; - if (quote) - to += quote; - to += ' '; -} - - -static processing_state create_db(const TEXT* statement, TEXT* d_name) +static processing_state create_db(const FrontendParser::CreateDatabaseNode& node) { /************************************** * @@ -4283,109 +4190,37 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) * Intercept create database commands to * adjust the DB and transaction handles * - * Parameters: statement == the entire statement for processing. - * * Note: SQL ROLE setting must be taken into an account no matter * that the newly created database will not have any user roles defined * in it. Role may affect right to create new database. * **************************************/ + processing_state ret = SKIP; // Disconnect from the database and cleanup ISQL_disconnect_database(false); - // Parse statement to tokens - const char* quotes = "\"'"; - string nlStatement(statement); - nlStatement += '\n'; - Firebird::Tokens toks; - toks.quotes(quotes); - toks.parse(0, nlStatement.c_str()); - - const unsigned KEY_USER = 0; - const unsigned KEY_PASS = 1; - const unsigned KEY_ROLE = 2; - const unsigned KEY_NAMES = 3; - const unsigned KEY_SET = 4; - - struct Key + for (const auto createWithRole : {true, false}) { - const char* text; - bool has; - }; + std::string statement = "create database " + node.args[0].rawText; - Key keys[5] = { - { "USER", false }, - { "PASSWORD", false }, - { "ROLE", false }, - { "NAMES", false }, - { "SET", false } - }; + // If there are global parameters, we will set them into the create statement. - for (unsigned t = 0; t < toks.getCount(); ++t) - { - Firebird::NoCaseString token(toks[t].text, toks[t].length); - unsigned k; + if (global_usr) + statement += std::string(" USER ") + isqlGlob.User; - for (k = 0; k < FB_NELEM(keys); ++k) - { - if (token == keys[k].text) - { - if (k != KEY_NAMES || keys[KEY_SET].has) - keys[k].has = true; - break; - } - } + if (global_psw) + statement += std::string(" PASSWORD '") + Password + "'"; - if (k != KEY_SET) - keys[KEY_SET].has = false; - } + if (global_role && createWithRole) + statement += std::string(" ROLE ") + isqlGlob.Role; - for (int createWithoutRole = 0; createWithoutRole < 2; ++createWithoutRole) - { - ret = SKIP; - TEXT usr[USER_LENGTH]; - TEXT psw[PASSWORD_LENGTH]; - TEXT role[ROLE_LENGTH]; + if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET) != 0) + statement += std::string(" SET NAMES '") + setValues.ISQL_charset + "'"; - string modifiedCreateStatement(statement); - - TEXT quote = DBL_QUOTE; - const TEXT* p = NULL; - - // If there is a global parameter, we will set it into the create stmt. - if (global_usr || global_role || global_psw || - (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET))) - { - strcpy(usr, isqlGlob.User); - strip_quotes(Password, psw); - strcpy(role, isqlGlob.Role); - - string clauses; - - if (global_usr && !keys[KEY_USER].has) - appendClause(clauses, keys[KEY_USER].text, usr); - - if (global_psw && !keys[KEY_PASS].has) - appendClause(clauses, keys[KEY_PASS].text, psw, SINGLE_QUOTE); - - if (global_role && (!keys[KEY_ROLE].has) && createWithoutRole == 0) - appendClause(clauses, keys[KEY_ROLE].text, role); - - if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET)&& !keys[KEY_NAMES].has) - { - string setNames = keys[KEY_SET].text; - setNames += ' '; - setNames += keys[KEY_NAMES].text; - appendClause(clauses, setNames.c_str(), setValues.ISQL_charset, SINGLE_QUOTE); - } - - if (toks.getCount() > 3) - modifiedCreateStatement.insert(toks[3].origin, clauses); - else - modifiedCreateStatement += clauses; - } + for (std::size_t i = 1; i < node.args.size(); ++i) + statement += std::string(" ") + node.args[i].rawText; // execute the create statement // If the isqlGlob.SQL_dialect is not set or set to 2, create the database @@ -4394,19 +4229,18 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) (isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) ? requested_SQL_dialect : isqlGlob.SQL_dialect; - DB = Firebird::UtilInterfacePtr()->executeCreateDatabase(fbStatus, modifiedCreateStatement.length(), - modifiedCreateStatement.c_str(), dialect, NULL); + DB = Firebird::UtilInterfacePtr()->executeCreateDatabase(fbStatus, statement.length(), + statement.c_str(), dialect, nullptr); - if ((!DB) && (createWithoutRole == 0) && (fbStatus->getErrors()[1] == isc_dsql_error)) + if (!DB && createWithRole && fbStatus->getErrors()[1] == isc_dsql_error) { - // OLd server failed to parse ROLE clause + // Old server failed to parse ROLE clause continue; } if (ISQL_errmsg(fbStatus)) - { ret = FAIL; - } + break; } @@ -4423,12 +4257,7 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) // CVC: Someone may decide to play strange games with undocumented ability // to write crap between CREATE DATABASE and the db name, as described by // Helen on CORE-932. Let's see if we can discover the real db name. - string s; - if (toks.getCount() > 2) - s.assign(toks[2].text, toks[2].length); - else - s = d_name; - strip_quotes(s.c_str(), isqlGlob.global_Db_name); + strcpy(isqlGlob.global_Db_name, node.args[0].getProcessedString().c_str()); ISQL_get_version(true); @@ -4792,7 +4621,7 @@ static processing_state drop_db() } -static processing_state edit(const TEXT* const* cmd) +static processing_state edit(const TEXT* file) { /************************************** * @@ -4810,9 +4639,6 @@ static processing_state edit(const TEXT* const* cmd) **************************************/ // Set up editing command for shell - - const TEXT* file = cmd[1]; - // If there is a file name specified, try to open it processing_state rc = SKIP; @@ -4998,9 +4824,6 @@ static processing_state escape(const TEXT* cmd) const TEXT* shellcmd = cmd; - // Search past the 'shell' keyword - shellcmd += strlen("shell"); - // Eat whitespace at beginning of command while (*shellcmd && fb_utils::isspace(*shellcmd)) shellcmd++; @@ -5058,7 +4881,7 @@ static processing_state execSetDebugCommand() } -static processing_state frontend(const TEXT* statement) +static processing_state frontend(const std::string& statement) { /************************************** * @@ -5075,186 +4898,57 @@ static processing_state frontend(const TEXT* statement) * **************************************/ - class FrontOptions : public OptionsBase - { - public: - enum front_commands - { - show, add, copy, - blobview, output, shell, set, create, drop, connect, - edit, input, quit, exit, explain, help, -#ifdef DEV_BUILD - passthrough, -#endif - wrong - }; - FrontOptions(const optionsMap* inmap, size_t insize, int wrongval) - : OptionsBase(inmap, insize, wrongval) - {} - }; + FrontendParser::Options parserOptions; + parserOptions.schemaAsDatabase = isqlGlob.major_ods > 0 && isqlGlob.major_ods < ODS_VERSION12; - static const FrontOptions::optionsMap options[] = - { - {FrontOptions::show, "SHOW", 0}, - {FrontOptions::add, "ADD", 0}, - {FrontOptions::copy, "COPY", 0}, - {FrontOptions::blobview, "BLOBVIEW", 0}, - {FrontOptions::blobview, "BLOBDUMP", 0}, - //{FrontOptions::output, "OUT", }, - {FrontOptions::output, "OUTPUT", 3}, - {FrontOptions::shell, "SHELL", 0}, - {FrontOptions::set, "SET", 0}, - {FrontOptions::create, "CREATE", 0}, - {FrontOptions::drop, "DROP", 0}, - {FrontOptions::connect, "CONNECT", 0}, - {FrontOptions::edit, "EDIT", 0}, - //{FrontOptions::input, "IN", }, - {FrontOptions::input, "INPUT", 2}, - {FrontOptions::quit, "QUIT", 0}, - {FrontOptions::exit, "EXIT", 0}, - {FrontOptions::explain, "EXPLAIN", 0}, - {FrontOptions::help, "?", 0}, - {FrontOptions::help, "HELP", 0} -#ifdef DEV_BUILD - , - {FrontOptions::passthrough, "PASSTHROUGH", 0} -#endif - }; - - TEXT errbuf[MSG_LENGTH]; - - // Store the first NUM_TERMS words as they appear in parms, using blanks - // to delimit. Each word beyond a real word gets a null char - // Shift parms to upper case, leaving original case in lparms - typedef TEXT* isql_params_t[MAX_TERMS]; - isql_params_t parms, lparms; - for (FB_SIZE_T iter = 0; iter < FB_NELEM(lparms); ++iter) - { - lparms[iter] = NULL; - parms[iter] = NULL; - } - TEXT parm_defaults[MAX_TERMS][1]; - - // Any whitespace and comments at the beginning are already swallowed by get_statement() - - // Set beginning of statement past comment - const TEXT* const cmd = statement; - if (!*cmd) - { - // In case any default transaction was started - commit it here - if (fbTrans) - commit_trans(&fbTrans); - - return SKIP; - } - - frontend_load_parms(statement, parms, lparms, parm_defaults); - - char bad_dialect_buf[512]; + const auto node = FrontendParser::parse(statement, parserOptions); bool bad_dialect = false; + char bad_dialect_buf[512]; - // Look to see if the words (parms) match any known verbs. If nothing - // matches then just hand the statement to process_statement - processing_state ret = SKIP; - const FrontOptions frontoptions(options, FB_NELEM(options), FrontOptions::wrong); - switch (frontoptions.getCommand(parms[0])) - { - case FrontOptions::show: - if (DB && !frontendTransaction()) + const auto ret = std::visit(StdVisitOverloads{ + [](const FrontendParser::InvalidNode&) { - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - return FAIL; - } + return CONT; + }, - ret = SHOW_metadata(parms, lparms); - if (fbTrans) - commit_trans(&fbTrans); - break; - - case FrontOptions::add: - if (!frontendTransaction()) + [](const FrontendParser::AddNode& node) { - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - return FAIL; - } + if (!frontendTransaction()) + return FAIL; - ret = add_row(lparms[1]); - if (fbTrans) - commit_trans(&fbTrans); - break; + const auto ret = add_row(node.tableName); + if (fbTrans) + commit_trans(&fbTrans); - case FrontOptions::copy: - if (!frontendTransaction()) + return ret; + }, + + [](const FrontendParser::BlobDumpViewNode& node) { - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - return FAIL; - } + return blobedit(node.blobId, (node.file ? node.file->c_str() : nullptr)); + }, - ret = copy_table(lparms[1], lparms[2], lparms[3]); - if (fbTrans) - commit_trans(&fbTrans); - break; - - case FrontOptions::blobview: - ret = blobedit(parms[0], lparms); - break; - - case FrontOptions::output: - ret = newoutput(lparms[1]); - break; - - case FrontOptions::shell: - ret = escape(cmd); - break; - - case FrontOptions::set: - ret = frontend_set(cmd, parms, lparms, bad_dialect_buf, bad_dialect); - break; - - case FrontOptions::create: - if (!strcmp(parms[1], "DATABASE") || - (!strcmp(parms[1], "SCHEMA") && isqlGlob.major_ods > 0 && isqlGlob.major_ods < ODS_VERSION12)) + [](const FrontendParser::ConnectNode& node) { - ret = create_db(cmd, lparms[2]); - } - else - ret = CONT; - break; - - case FrontOptions::drop: - if (!strcmp(parms[1], "DATABASE") || - (!strcmp(parms[1], "SCHEMA") && isqlGlob.major_ods > 0 && isqlGlob.major_ods < ODS_VERSION12)) - { - if (*parms[2]) - ret = ps_ERR; - else - ret = drop_db(); - } - else - ret = CONT; - break; - - case FrontOptions::connect: - { - const TEXT* psw = NULL; - const TEXT* usr = NULL; - const TEXT* sql_role_nm = NULL; + std::optional psw; + const char* usr = nullptr; + const char* sql_role_nm = nullptr; int numbufs = 0; // if a parameter is given in the command more than once, the // last one will be used. The parameters can appear each any // order, but each must provide a value. - ret = SKIP; - for (int i = 2; i < (MAX_TERMS - 1);) + auto ret = SKIP; + + for (std::size_t i = 1; i < node.args.size() - 1;) { - if (!strcmp(parms[i], "CACHE") && *lparms[i + 1]) + const auto& clause = node.args[i].processedText; + + if (clause == "CACHE") { char* err; - long value = strtol(lparms[i + 1], &err, 10); + long value = strtol(node.args[i + 1].processedText.c_str(), &err, 10); if (*err || (value <= 0) || (value >= INT_MAX)) { ret = ps_ERR; @@ -5263,85 +4957,387 @@ static processing_state frontend(const TEXT* statement) numbufs = (int) value; i += 2; } - else if (!strcmp(parms[i], "USER") && *lparms[i + 1]) + else if (clause == "USER") { - usr = lparms[i + 1]; + usr = node.args[i + 1].processedText.c_str(); i += 2; } - else if (!strcmp(parms[i], "PASSWORD") && *lparms[i + 1]) + else if (clause == "PASSWORD") { - psw = lparms[i + 1]; + psw = node.args[i + 1].getProcessedString(); i += 2; } - else if (!strcmp(parms[i], "ROLE") && *lparms[i + 1]) + else if (clause == "ROLE") { - sql_role_nm = lparms[i + 1]; + sql_role_nm = node.args[i + 1].processedText.c_str(); i += 2; } - else if (*parms[i]) + else if (!clause.empty()) { // Unrecognized option to CONNECT ret = ps_ERR; break; } else - i++; + ++i; } + if (ret != ps_ERR) - ret = newdb(lparms[1], usr, psw, numbufs, sql_role_nm, true); + { + ret = newdb( + node.args[0].getProcessedString().c_str(), + usr, + (psw ? psw->c_str() : nullptr), + numbufs, + sql_role_nm, + true); + } + + return SKIP; + }, + + [](const FrontendParser::CopyNode& node) + { + if (!frontendTransaction()) + return FAIL; + + const auto ret = copy_table(node.source, node.destination, node.database.c_str()); + + if (fbTrans) + commit_trans(&fbTrans); + + return ret; + }, + + [](const FrontendParser::CreateDatabaseNode& node) + { + return create_db(node); + }, + + [](const FrontendParser::DropDatabaseNode&) + { + return drop_db(); + }, + + [](const FrontendParser::EditNode& node) + { + return edit(node.file.value_or("").c_str()); + }, + + [](const FrontendParser::ExitNode& node) + { + return EXIT; + }, + + [](const FrontendParser::ExplainNode& node) + { + return explain(node.query.c_str()); + }, + + [](const FrontendParser::HelpNode& node) + { + return help(node.command.value_or("").c_str()); + }, + + [](const FrontendParser::InputNode& node) + { + return newinput(node.file.c_str()); + }, + + [](const FrontendParser::OutputNode& node) + { + return newoutput(node.file.value_or("").c_str()); + }, + + [](const FrontendParser::QuitNode& node) + { + return BACKOUT; + }, + + [](const FrontendParser::ShellNode& node) + { + return escape(node.command.value_or("").c_str()); + }, + + // SET commands + + [&](const FrontendParser::AnySetNode& anySet) + { + return std::visit(StdVisitOverloads{ + [](const FrontendParser::InvalidNode&) + { + return CONT; + }, + + [](const FrontendParser::SetNode&) + { + return print_sets(); + }, + + [](const FrontendParser::SetAutoDdlNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Autocommit); + }, + + [](const FrontendParser::SetAutoTermNode& node) + { + const auto ret = do_set_command(node.arg.c_str(), &setValues.AutoTerm); + if (setValues.AutoTerm) + { + isqlGlob.Termlen = 1; + strcpy(isqlGlob.global_Term, ";"); + } + return ret; + }, + + [](const FrontendParser::SetBailNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.BailOnError); + }, + + [](const FrontendParser::SetBlobDisplayNode& node) + { + // No arg means turn off blob display + if (node.arg.empty() || node.arg == "OFF") + setValues.Doblob = NO_BLOBS; + else if (node.arg == "ALL") + setValues.Doblob = ALL_BLOBS; + else + setValues.Doblob = atoi(node.arg.c_str()); + return SKIP; + }, + + [](const FrontendParser::SetBulkInsertNode& node) + { + return bulk_insert_hack(node.statement.c_str()); + }, + + [](const FrontendParser::SetCountNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Docount); + }, + + [](const FrontendParser::SetEchoNode& node) + { + const auto ret = do_set_command(node.arg.c_str(), &setValues.Echo); + if (!setValues.Echo) + ISQL_prompt(""); + return ret; + }, + + [](const FrontendParser::SetExecPathDisplayNode& node) + { + if (node.arg.empty()) + return ps_ERR; + else if (node.arg == "OFF") + setValues.ExecPathDisplay[0] = 0; + else + { + static constexpr UCHAR execPath[] = {isc_info_sql_exec_path_blr_text}; + + if (node.arg != "BLR") + return ps_ERR; + + memcpy(setValues.ExecPathDisplay, execPath, FB_NELEM(execPath)); + setValues.ExecPathDisplay[FB_NELEM(execPath)] = 0; + } + + return execSetDebugCommand(); + }, + + [](const FrontendParser::SetExplainNode& node) + { + auto ret = do_set_command(node.arg.c_str(), &setValues.ExplainPlan); + if (setValues.ExplainPlan) + ret = do_set_command("ON", &setValues.Plan); + return ret; + }, + + [](const FrontendParser::SetHeadingNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Heading); + }, + + [](const FrontendParser::SetKeepTranParamsNode& node) + { + const bool oldValue = setValues.KeepTranParams; + const auto ret = do_set_command(node.arg.c_str(), &setValues.KeepTranParams); + if ((ret != ps_ERR) && (oldValue != setValues.KeepTranParams)) + TranParams->assign(setValues.KeepTranParams ? DEFAULT_DML_TRANS_SQL : ""); + return ret; + }, + + [](const FrontendParser::SetListNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.List); + }, + + [](const FrontendParser::SetLocalTimeoutNode& node) + { + int val = strtol(node.arg.c_str(), nullptr, 10); + + if (val < 0) + return ps_ERR; + else + { + setValues.StmtTimeout = val; + return SKIP; + } + }, + + [](const FrontendParser::SetMaxRowsNode& node) + { + return newMaxRows((node.arg.empty() ? "0" : node.arg.c_str())); + }, + + [](const FrontendParser::SetNamesNode& node) + { + const auto setNames = node.name ? node.name->c_str() : DEFCHARSET; + + const size_t lgth = strlen(setNames); + if (lgth < MAXCHARSET_SIZE) + strcpy(setValues.ISQL_charset, setNames); + else + fb_utils::copy_terminate(setValues.ISQL_charset, setNames, MAXCHARSET_SIZE); + + return SKIP; + }, + + [](const FrontendParser::SetPerTableStatsNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.PerTableStats); + }, + + [](const FrontendParser::SetPlanNode& node) + { + auto ret = do_set_command(node.arg.c_str(), &setValues.Plan); + if (setValues.Planonly && !setValues.Plan) + ret = do_set_command("OFF", &setValues.Planonly); + return ret; + }, + + [](const FrontendParser::SetPlanOnlyNode& node) + { + auto ret = do_set_command(node.arg.c_str(), &setValues.Planonly); + if (setValues.Planonly && !setValues.Plan) + { + // turn on plan + ret = do_set_command("ON", &setValues.Plan); + } + return ret; + }, + + [](const FrontendParser::SetSqldaDisplayNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Sqlda_display); + }, + + [&](const FrontendParser::SetSqlDialectNode& node) + { + return get_dialect(node.arg.c_str(), bad_dialect_buf, bad_dialect); + }, + + [](const FrontendParser::SetStatsNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Stats); + }, + + [](const FrontendParser::SetTermNode& node) + { + const TEXT* termStr = node.arg.empty() ? DEFTERM : node.arg.c_str(); + + for (size_t iter = 0; iter < sizeof(FORBIDDEN_TERM_CHARS); ++iter) + { + if (strchr(termStr, FORBIDDEN_TERM_CHARS[iter])) + { + TEXT msg_string[MSG_LENGTH]; + IUTILS_msg_get(INVALID_TERM_CHARS, msg_string, SafeArg() << FORBIDDEN_TERM_CHARS_DISPLAY); + isqlGlob.printf("%s\n", msg_string); + return ps_ERR; + } + } + + setValues.AutoTerm = false; + + isqlGlob.Termlen = strlen(termStr); + if (isqlGlob.Termlen < MAXTERM_SIZE) + strcpy(isqlGlob.global_Term, termStr); + else + { + isqlGlob.Termlen = MAXTERM_SIZE - 1; + fb_utils::copy_terminate(isqlGlob.global_Term, termStr, isqlGlob.Termlen + 1); + } + + return SKIP; + }, + + [](const FrontendParser::SetTimeNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Time_display); + }, + + [](const FrontendParser::SetTransactionNode& node) + { + return newtrans(node.statement.c_str()); + }, + + [](const FrontendParser::SetWarningsNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.Warnings); + }, + + [](const FrontendParser::SetWidthNode& node) + { + return newsize(node.column.c_str(), node.width.c_str()); + }, + + [](const FrontendParser::SetWireStatsNode& node) + { + return do_set_command(node.arg.c_str(), &setValues.WireStats); + }, + + [](auto& arg) + { + static_assert(FrontendParser::AlwaysFalseV, + "Add visitor method for that set node type"); + } + }, anySet); + }, + + // SHOW commands + + [](const FrontendParser::AnyShowNode& node) + { + if (DB && !frontendTransaction()) + return FAIL; + + const auto ret = SHOW_metadata(node); + + if (fbTrans) + commit_trans(&fbTrans); + + return ret; + }, + + [](auto& arg) + { + static_assert(FrontendParser::AlwaysFalseV, "Add visitor method for that node type"); } - break; - - case FrontOptions::edit: - ret = edit(lparms); - break; - - case FrontOptions::input: - // CVC: Set by newinput() below only if successful. - //Input_file = true; - ret = newinput(lparms[1]); - break; - - case FrontOptions::quit: - ret = BACKOUT; - break; - - case FrontOptions::exit: - ret = EXIT; - break; - - case FrontOptions::explain: - ret = explain(cmd + 7); - break; - - case FrontOptions::help: - ret = help(parms[1]); - break; - -#ifdef DEV_BUILD - case FrontOptions::passthrough: - ret = passthrough(cmd + 11); - break; -#endif - - default: // Didn't match, it must be SQL - ret = CONT; - break; - } + }, node); // In case any default transaction was started - commit it here if (fbTrans) commit_trans(&fbTrans); - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - if (ret == ps_ERR) { + TEXT errbuf[MSG_LENGTH]; + if (bad_dialect) IUTILS_msg_get(CMD_ERR, errbuf, SafeArg() << bad_dialect_buf); else - IUTILS_msg_get(CMD_ERR, errbuf, SafeArg() << cmd); + IUTILS_msg_get(CMD_ERR, errbuf, SafeArg() << statement.c_str()); + STDERROUT(errbuf); } @@ -5349,426 +5345,6 @@ static processing_state frontend(const TEXT* statement) } -static void frontend_free_parms(TEXT* parms[], TEXT* lparms[], TEXT parm_defaults[][1]) -{ - for (int j = 0; j < MAX_TERMS; j++) - { - if (parms[j] && parms[j] != parm_defaults[j]) - { - ISQL_FREE(parms[j]); - ISQL_FREE(lparms[j]); - } - } -} - - -static void frontend_load_parms(const TEXT* p, TEXT* parms[], TEXT* lparms[], - TEXT parm_defaults[][1]) -{ - TEXT buffer[BUFFER_LENGTH256]; - - for (int i = 0; i < MAX_TERMS; ++i) - { - if (!*p) - { - parms[i] = lparms[i] = parm_defaults[i]; - parm_defaults[i][0] = '\0'; - continue; - } - - bool role_found = false; - TEXT* a = buffer; - int j = 0; - const bool quoted = *p == DBL_QUOTE || *p == SINGLE_QUOTE; - if (quoted) - { - if (i > 0 && (!strcmp(parms[i - 1], "ROLE"))) - role_found = true; - bool delimited_done = false; - const TEXT end_quote = *p; - j++; - *a++ = *p++; - // Allow a quoted string to have embedded spaces - // Prevent overflow - while (*p && !delimited_done && j < BUFFER_LENGTH256 - 1) - { - if (*p == end_quote) - { - j++; - *a++ = *p++; - if (*p && *p == end_quote && j < BUFFER_LENGTH256 - 1) - { - j++; // do not skip the escape quote here - *a++ = *p++; - } - else - delimited_done = true; - } - else - { - j++; - *a++ = *p++; - } - } - *a = '\0'; - } - else - { - // Prevent overflow. Do not copy the string (redundant). - while (*p && !fb_utils::isspace(*p) && j < BUFFER_LENGTH256 - 1) - { - j++; - ++p; - } - } - fb_assert(!quoted || strlen(buffer) == size_t(j)); - const size_t length = quoted ? strlen(buffer) : j; - parms[i] = (TEXT*) ISQL_ALLOC((SLONG) (length + 1)); - lparms[i] = (TEXT*) ISQL_ALLOC((SLONG) (length + 1)); - memcpy(parms[i], quoted ? buffer : p - j, length); - parms[i][length] = 0; - while (*p && fb_utils::isspace(*p)) - p++; - strcpy(lparms[i], parms[i]); - if (!role_found) - IUTILS_make_upper(parms[i]); - } -} - - -// *********************** -// f r o n t e n d _ s e t -// *********************** -// Validates and executes the SET {option {params}} command. -static processing_state frontend_set(const char* cmd, const char* const* parms, - const char* const* lparms, char* const bad_dialect_buf, bool& bad_dialect) -{ - - class SetOptions : public OptionsBase - { - public: - enum set_commands - { - stat, count, list, plan, planonly, explain, blobdisplay, echo, autoddl, - autoterm, width, transaction, terminator, names, time, - sqlda_display, - exec_path_display, - sql, warning, sqlCont, heading, bail, - bulk_insert, maxrows, stmtTimeout, - keepTranParams, perTableStats, wireStats, - wrong - }; - SetOptions(const optionsMap* inmap, size_t insize, int wrongval) - : OptionsBase(inmap, insize, wrongval) - {} - }; - - static const SetOptions::optionsMap options[] = - { - {SetOptions::stat, "STATS", 4}, - {SetOptions::count, "COUNT", 0}, - {SetOptions::list, "LIST", 0}, - {SetOptions::plan, "PLAN", 0}, - {SetOptions::planonly, "PLANONLY", 0}, - {SetOptions::explain, "EXPLAIN", 0}, - {SetOptions::blobdisplay, "BLOBDISPLAY", 4}, - {SetOptions::echo, "ECHO", 0}, - {SetOptions::autoddl, "AUTODDL", 4}, - {SetOptions::autoterm, "AUTOTERM", 5}, - {SetOptions::width, "WIDTH", 0}, - {SetOptions::transaction, "TRANSACTION", 5}, - {SetOptions::terminator, "TERMINATOR", 4}, - {SetOptions::names, "NAMES", 0}, - {SetOptions::time, "TIME", 0}, - {SetOptions::sqlda_display, "SQLDA_DISPLAY", 0}, - {SetOptions::exec_path_display, "EXEC_PATH_DISPLAY", 0}, - {SetOptions::sql, "SQL", 0}, - {SetOptions::warning, "WARNINGS", 7}, - {SetOptions::warning, "WNG", 0}, - {SetOptions::sqlCont, "GENERATOR", 0}, - {SetOptions::sqlCont, "STATISTICS", 0}, - {SetOptions::heading, "HEADING", 0}, - {SetOptions::bail, "BAIL", 0}, - {SetOptions::bulk_insert, "BULK_INSERT", 0}, - {SetOptions::maxrows, "ROWCOUNT", 0}, // legacy, compatible with FB2.5 - {SetOptions::maxrows, "MAXROWS", 0}, - {SetOptions::sqlCont, "ROLE", 0}, - {SetOptions::sqlCont, "TRUSTED", 0}, // TRUSTED ROLE, will get DSQL error other case - {SetOptions::stmtTimeout, "LOCAL_TIMEOUT", 0}, - {SetOptions::sqlCont, "DECFLOAT", 0}, - {SetOptions::keepTranParams, "KEEP_TRAN_PARAMS", 9}, - {SetOptions::perTableStats, "PER_TABLE_STATS", 7}, - {SetOptions::wireStats, "WIRE_STATS", 4} - }; - - // Display current set options - if (!*parms[1]) - return print_sets(); - - processing_state ret = SKIP; - const SetOptions setoptions(options, FB_NELEM(options), SetOptions::wrong); - switch (setoptions.getCommand(parms[1])) - { - case SetOptions::sqlCont: - ret = CONT; - break; - - case SetOptions::stat: - ret = do_set_command(parms[2], &setValues.Stats); - break; - - case SetOptions::count: - ret = do_set_command(parms[2], &setValues.Docount); - break; - - case SetOptions::list: - ret = do_set_command(parms[2], &setValues.List); - break; - - case SetOptions::plan: - ret = do_set_command(parms[2], &setValues.Plan); - if (setValues.Planonly && !setValues.Plan) - ret = do_set_command("OFF", &setValues.Planonly); - break; - - case SetOptions::planonly: - ret = do_set_command (parms[2], &setValues.Planonly); - if (setValues.Planonly && !setValues.Plan) - { - // turn on plan - ret = do_set_command ("ON", &setValues.Plan); - } - break; - - case SetOptions::explain: - ret = do_set_command(parms[2], &setValues.ExplainPlan); - if (setValues.ExplainPlan) - ret = do_set_command("ON", &setValues.Plan); - break; - - case SetOptions::blobdisplay: - // No arg means turn off blob display - if (!*parms[2] || !strcmp(parms[2], "OFF")) - setValues.Doblob = NO_BLOBS; - else if (!strcmp(parms[2], "ALL")) - setValues.Doblob = ALL_BLOBS; - else - setValues.Doblob = atoi(parms[2]); - break; - - case SetOptions::echo: - ret = do_set_command(parms[2], &setValues.Echo); - if (!setValues.Echo) - ISQL_prompt(""); - break; - - case SetOptions::autoddl: - ret = do_set_command(parms[2], &setValues.Autocommit); - break; - - case SetOptions::autoterm: - ret = do_set_command(parms[2], &setValues.AutoTerm); - if (setValues.AutoTerm) - { - isqlGlob.Termlen = 1; - strcpy(isqlGlob.global_Term, ";"); - } - break; - - case SetOptions::width: - ret = newsize(parms[2][0] == '"' ? lparms[2] : parms[2], parms[3]); - break; - - case SetOptions::transaction: - ret = newtrans(cmd); - break; - - case SetOptions::terminator: - { - const TEXT* a = (*lparms[2]) ? lparms[2] : DEFTERM; - for (size_t iter = 0; iter < sizeof(FORBIDDEN_TERM_CHARS); ++iter) - { - if (strchr(a, FORBIDDEN_TERM_CHARS[iter])) - { - TEXT msg_string[MSG_LENGTH]; - IUTILS_msg_get(INVALID_TERM_CHARS, msg_string, SafeArg() << FORBIDDEN_TERM_CHARS_DISPLAY); - isqlGlob.printf("%s\n", msg_string); - return ps_ERR; - } - } - - setValues.AutoTerm = false; - - isqlGlob.Termlen = strlen(a); - if (isqlGlob.Termlen < MAXTERM_SIZE) - { - strcpy(isqlGlob.global_Term, a); - } - else - { - isqlGlob.Termlen = MAXTERM_SIZE - 1; - fb_utils::copy_terminate(isqlGlob.global_Term, a, isqlGlob.Termlen + 1); - } - } - break; - - case SetOptions::names: - if (!*parms[2]) - { - const size_t lgth = strlen(DEFCHARSET); - if (lgth < MAXCHARSET_SIZE) - strcpy(setValues.ISQL_charset, DEFCHARSET); - else - fb_utils::copy_terminate(setValues.ISQL_charset, DEFCHARSET, MAXCHARSET_SIZE); - } - else - { - const size_t lgth = strlen(parms[2]); - if (lgth < MAXCHARSET_SIZE) - strcpy(setValues.ISQL_charset, parms[2]); - else - fb_utils::copy_terminate(setValues.ISQL_charset, parms[2], MAXCHARSET_SIZE); - } - break; - - case SetOptions::time: - if (fb_utils::stricmp(parms[2], "ZONE") == 0) - ret = CONT; // pass SET TIME ZONE command to server - else - ret = do_set_command(parms[2], &setValues.Time_display); - break; - - case SetOptions::sqlda_display: - ret = do_set_command(parms[2], &setValues.Sqlda_display); - break; - - case SetOptions::exec_path_display: - ret = SKIP; - - if (!*parms[2]) - ret = ps_ERR; - else if (strcmp(parms[2], "OFF") == 0) - setValues.ExecPathDisplay[0] = 0; - else - { - Firebird::Array execPath; - - for (int parNum = 2; parNum < MAX_TERMS - 1 && *parms[parNum]; ++parNum) - { - const char* param = parms[parNum]; - UCHAR code; - - if (strcmp(param, "BLR") == 0) - code = isc_info_sql_exec_path_blr_text; - else - { - ret = ps_ERR; - break; - } - - if (execPath.exist(code)) - { - ret = ps_ERR; - break; - } - - execPath.push(code); - } - - if (ret != ps_ERR) - { - if (execPath.getCount() < sizeof(setValues.ExecPathDisplay)) - { - memcpy(setValues.ExecPathDisplay, execPath.begin(), execPath.getCount()); - setValues.ExecPathDisplay[execPath.getCount()] = 0; - } - else - ret = ps_ERR; - } - } - - if (ret != ps_ERR) - ret = execSetDebugCommand(); - - break; - - case SetOptions::sql: - if (!strcmp(parms[2], "DIALECT")) - ret = get_dialect(parms[3], bad_dialect_buf, bad_dialect); - else - ret = ps_ERR; - break; - - case SetOptions::warning: - ret = do_set_command (parms[2], &setValues.Warnings); - break; - - case SetOptions::heading: - ret = do_set_command(parms[2], &setValues.Heading); - break; - - case SetOptions::bail: - ret = do_set_command(parms[2], &setValues.BailOnError); - break; - - case SetOptions::bulk_insert: - if (*parms[2]) - ret = bulk_insert_hack(cmd); - else - ret = ps_ERR; - break; - - case SetOptions::maxrows: - ret = newMaxRows((*lparms[2]) ? lparms[2] : "0"); - break; - - case SetOptions::stmtTimeout: - { - int val = strtol(parms[2], NULL, 10); - if (val < 0) - ret = ps_ERR; - else - { - setValues.StmtTimeout = val; - ret = SKIP; - } - } - break; - - case SetOptions::keepTranParams: - { - const bool oldValue = setValues.KeepTranParams; - ret = do_set_command(parms[2], &setValues.KeepTranParams); - if ((ret != ps_ERR) && (oldValue != setValues.KeepTranParams)) - TranParams->assign(setValues.KeepTranParams ? DEFAULT_DML_TRANS_SQL : ""); - } - - break; - - case SetOptions::perTableStats: - ret = do_set_command(parms[2], &setValues.PerTableStats); - break; - - case SetOptions::wireStats: - ret = do_set_command(parms[2], &setValues.WireStats); - break; - - default: - //{ - // TEXT msg_string[MSG_LENGTH]; - // IUTILS_msg_get(VALID_OPTIONS, msg_string); - // isqlGlob.printf("%s\n", msg_string); - //} - //setoptions.showCommands(isqlGlob.Out); - //ret = ps_ERR; - ret = CONT; // pass unknown SET command to server as is - break; - } - - return ret; -} - - static processing_state do_set_command(const TEXT* parm, bool* global_flag) { /************************************** @@ -6721,7 +6297,7 @@ static bool printUser(const char* dbName) } -static processing_state newdb(TEXT* dbname, +static processing_state newdb(const TEXT* dbname, const TEXT* usr, const TEXT* psw, int numbufs, @@ -6755,14 +6331,16 @@ static processing_state newdb(TEXT* dbname, // out. We will restore it after the disconnect. The save_database buffer // will also be used to translate dbname to the proper character set. - const SLONG len = static_cast(chop_at(dbname, MAXPATHLEN)); + const SLONG len = strnlen(dbname, MAXPATHLEN - 1); SCHAR* save_database = (SCHAR*) ISQL_ALLOC(len + 1); if (!save_database) return ps_ERR; - strcpy(save_database, dbname); + strncpy(save_database, dbname, len); + save_database[len] = 0; ISQL_disconnect_database(false); - strcpy(dbname, save_database); + strcpy(isqlGlob.global_Db_name, save_database); + dbname = isqlGlob.global_Db_name; ISQL_FREE(save_database); TEXT local_psw[BUFFER_LENGTH256]; @@ -6775,12 +6353,10 @@ static processing_state newdb(TEXT* dbname, local_usr[0] = 0; local_sql_role[0] = 0; - // Strip quotes if well-intentioned - - strip_quotes(dbname, isqlGlob.global_Db_name); if (usr) strcpy(local_usr, usr); - strip_quotes(psw, local_psw); + if (psw) + strcpy(local_psw, psw); // if local user is not specified, see if global options are // specified - don't let a global role setting carry forward if a @@ -6964,20 +6540,17 @@ static processing_state newinput(const TEXT* infile) return ps_ERR; } - TEXT path[MAXPATHLEN]; - strip_quotes(infile, path); - PathName file; - if (PathUtils::isRelative(path)) + if (PathUtils::isRelative(infile)) { PathName newPath, temp; PathUtils::splitLastComponent(newPath, temp, Filelist->Ifp().fileName(false)); - PathUtils::concatPath(file, newPath, path); + PathUtils::concatPath(file, newPath, infile); } else - file = path; + file = infile; // filelist is a linked list of file pointers. We must add a node to // the linked list before discarding the current Ifp. @@ -6987,11 +6560,11 @@ static processing_state newinput(const TEXT* infile) if (fp) { Filelist->insertIfp(); - Filelist->Ifp().init(fp, file.c_str(), path); + Filelist->Ifp().init(fp, file.c_str(), infile); } else { - IUTILS_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << path); + IUTILS_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << infile); STDERROUT(errbuf); return FAIL; } @@ -7020,9 +6593,6 @@ static processing_state newoutput(const TEXT* outfile) if (*outfile) { - TEXT path[MAXPATHLEN]; - strip_quotes(outfile, path); - outfile = path; FILE* fp = os_utils::fopen(outfile, "a"); if (fp) { @@ -7071,18 +6641,7 @@ static processing_state newsize(const TEXT* colname, const TEXT* sizestr) * Add a column name and print width to collist * **************************************/ - if (!*colname || (strlen(colname) >= QUOTED_NAME_SIZE)) - return ps_ERR; - - char buf[QUOTED_NAME_SIZE]; - if (colname[0] == DBL_QUOTE) - { - strcpy(buf, colname); - IUTILS_remove_and_unescape_quotes(buf, DBL_QUOTE); - colname = buf; - } - - if (strlen(colname) > MAX_SQL_IDENTIFIER_LEN) + if (!*colname || strlen(colname) > MAX_SQL_IDENTIFIER_LEN) return ps_ERR; // If no size is given, remove the entry @@ -7601,25 +7160,6 @@ static processing_state parse_arg(int argc, SCHAR** argv, SCHAR* tabname) } -// ********************* -// p a s s t h r o u g h -// ********************* -// Execute a command directly on the server. No interpretation done in isql. -// Use with care. Only for debug builds. -#ifdef DEV_BUILD -static processing_state passthrough(const char* cmd) -{ - if (!DB) - return FAIL; - - M__trans = DB->execute(fbStatus, M__trans, 0, cmd, isqlGlob.SQL_dialect, NULL, NULL, NULL, NULL); - if (ISQL_errmsg(fbStatus)) - return ps_ERR; - - return SKIP; -} -#endif - static bool checkSpecial(TEXT* const p, const int length, const double value) { /************************************** diff --git a/src/isql/iutils.cpp b/src/isql/iutils.cpp index b5e3e276d1..09dc0a537f 100644 --- a/src/isql/iutils.cpp +++ b/src/isql/iutils.cpp @@ -37,6 +37,7 @@ #include "../common/utils_proto.h" #include +using namespace Firebird; using MsgFormat::SafeArg; @@ -148,6 +149,16 @@ void IUTILS_msg_get(USHORT number, USHORT size, TEXT* msg, const SafeArg& args) fb_msg_format(NULL, ISQL_MSG_FAC, number, size, msg, args); } + +string IUTILS_name_to_string(const MetaString& name) +{ + if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION) + return name.toQuotedString(); + else + return name.c_str(); +} + + void IUTILS_printf(FILE* fp, const char* buffer) { /************************************** diff --git a/src/isql/iutils_proto.h b/src/isql/iutils_proto.h index 4982e17c97..c61826081a 100644 --- a/src/isql/iutils_proto.h +++ b/src/isql/iutils_proto.h @@ -24,6 +24,7 @@ #ifndef ISQL_IUTILS_PROTO_H #define ISQL_IUTILS_PROTO_H +#include "../common/classes/MetaString.h" #include "../common/classes/SafeArg.h" #include @@ -33,6 +34,7 @@ void IUTILS_msg_get(USHORT number, TEXT* msg, const MsgFormat::SafeArg& args = MsgFormat::SafeArg()); void IUTILS_msg_get(USHORT number, USHORT size, TEXT* msg, const MsgFormat::SafeArg& args = MsgFormat::SafeArg()); +Firebird::string IUTILS_name_to_string(const Firebird::MetaString& name); void IUTILS_printf(FILE*, const char*); void IUTILS_printf2(FILE*, const char*, ...); void IUTILS_put_errmsg(USHORT number, const MsgFormat::SafeArg& args); diff --git a/src/isql/show.epp b/src/isql/show.epp index 83903e99a7..540f758a26 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -45,6 +45,7 @@ #include "../isql/isql.h" #include "../jrd/intl.h" #include "../common/intlobj_new.h" +#include "../common/StdHelper.h" #include "../common/classes/AlignedBuffer.h" #include "../common/classes/ClumpletReader.h" #include "../isql/isql_proto.h" @@ -68,8 +69,7 @@ #include #endif -using Firebird::string; -using Firebird::MetaString; +using namespace Firebird; using MsgFormat::SafeArg; @@ -83,7 +83,6 @@ DATABASE DB = EXTERN COMPILETIME "yachts.lnk" RUNTIME isqlGlob.global_Db_name; enum commentMode {cmmShow, cmmExtract}; -static void remove_delimited_double_quotes(TEXT*); static void make_priv_string(USHORT, char*, bool); static processing_state show_all_tables(SSHORT); static void show_charsets(SSHORT char_set_id, SSHORT collation); @@ -99,23 +98,23 @@ static processing_state show_dialect(); static processing_state show_domains(const SCHAR*); static processing_state show_exceptions(const SCHAR*); static processing_state show_filters(const SCHAR*); -static processing_state show_functions(const SCHAR* funcname, bool quoted, bool system, const SCHAR* msg = nullptr); +static processing_state show_functions( + const char* funcname, const char* packname, bool quoted, bool system, const char* msg = nullptr); static processing_state show_func_legacy(const MetaString&); static processing_state show_func(const MetaString&, const MetaString&); static processing_state show_generators(const SCHAR*); static void show_index(SCHAR*, SCHAR*, const SSHORT, const SSHORT, const SSHORT); -static processing_state show_indices(const SCHAR* const*); -static processing_state show_proc(const SCHAR*, bool, bool, const char* msg = nullptr); +static processing_state show_indices(const char*); +static processing_state show_proc(const char*, const char*, bool, bool, const char* msg = nullptr); static processing_state show_packages(const SCHAR* package_name, bool, const SCHAR* = NULL); static processing_state show_publications(const SCHAR* pub_name, bool, const SCHAR* = NULL); static void show_pub_table(const SCHAR* table_name); static processing_state show_role(const SCHAR*, bool, const char* msg = NULL); -static processing_state show_secclass(const char* object, const char* opt); +static processing_state show_secclass(const std::optional& object, bool detail); static processing_state show_table(const SCHAR*, bool); static processing_state show_trigger(const SCHAR*, bool, bool); static processing_state show_users(); static processing_state show_users12(); -static void parse_package(const char* procname, MetaString& package, MetaString& procedure); static processing_state show_wireStats(); const char* const spaces = " "; @@ -2067,7 +2066,7 @@ void SHOW_print_metadata_text_blob(FILE* fp, ISC_QUAD* blobid, bool escape_squot } -processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) +processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node) { /************************************** * @@ -2078,752 +2077,720 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) * Functional description * If somebody presses the show ..., come here to * interpret the desired command. - * Paramters: - * cmd -- Array of words for the command * **************************************/ - class ShowOptions : public OptionsBase + static const OptionsBase::optionsMap options[] = { - public: - enum show_commands - { - role, table, view, system, index, domain, exception, - filter, function, generator, grant, procedure, trigger, - check, database, comment, dependency, collation, security_class, - users, package, publication, schema, map, wireStats, - wrong - }; - ShowOptions(const optionsMap* inmap, size_t insize, int wrongval) - : OptionsBase(inmap, insize, wrongval) - {} + {0, "ROLES", 4}, // ROLE + {0, "TABLES", 5}, // TABLE + {0, "VIEWS", 4}, // VIEW + {0, "SYSTEM", 3}, // SYS + {0, "INDEXES", 3}, // IND + {0, "INDICES", 0}, + {0, "DOMAINS", 6}, // DOMAIN + {0, "EXCEPTIONS", 5}, // EXCEPTION + {0, "FILTERS", 6}, // FILTER + {0, "FUNCTIONS", 4}, // FUNCTION + {0, "GENERATORS", 3}, // GEN + {0, "SEQUENCES", 3}, // SEQ + {0, "GRANTS", 5}, // GRANT + {0, "PROCEDURES", 4}, // PROC + {0, "TRIGGERS", 4}, // TRIG + {0, "CHECKS", 5}, // CHECK + {0, "DB", 0}, + {0, "DATABASE", 0}, + {0, "COMMENTS", 7}, // COMMENT + {0, "DEPENDENCY", 5}, // DEPEN + {0, "DEPENDENCIES", 5}, // DEPEN + {0, "COLLATES", 7}, // COLLATE + {0, "COLLATIONS", 9}, // COLLATION + {0, "SECCLASSES", 6}, // SECCLA + {0, "USERS", 0}, + {0, "PACKAGES", 4}, // PACK + ///{0, "SCHEMAS", 4}, // SCHE + {0, "MAPPINGS", 3}, // MAP + {0, "PUBLICATIONS", 3}, // PUB + {0, "WIRE_STATISTICS", 9}, // WIRE_STAT + {0, "WIRE_STATS", 10} }; - static const ShowOptions::optionsMap options[] = - { - //{role, "ROLE"}, - {ShowOptions::role, "ROLES", 4}, - //{table, "TABLE"}, - {ShowOptions::table, "TABLES", 5}, - //{view, "VIEW"}, - {ShowOptions::view, "VIEWS", 4}, - //{system, "SYS"}, - {ShowOptions::system, "SYSTEM", 3}, - //{index, "IND"}, - {ShowOptions::index, "INDEXES", 3}, - {ShowOptions::index, "INDICES", 0}, - //{domain, "DOMAIN"}, - {ShowOptions::domain, "DOMAINS", 6}, - //{exception, "EXCEPTION"}, - {ShowOptions::exception, "EXCEPTIONS", 5}, - //{filter, "FILTER"}, - {ShowOptions::filter, "FILTERS", 6}, - //{function, "FUNCTION"}, - {ShowOptions::function, "FUNCTIONS", 4}, - //{generator, "GEN"}, - //{generator, "GENERATOR"}, - {ShowOptions::generator, "GENERATORS", 3}, - //{generator, "SEQ"}, - //{generator, "SEQUENCE"}, - {ShowOptions::generator, "SEQUENCES", 3}, - //{grant, "GRANT"}, - {ShowOptions::grant, "GRANTS", 5}, - //{procedure, "PROC"}, - //{procedure, "PROCEDURE"}, - {ShowOptions::procedure, "PROCEDURES", 4}, - //{trigger, "TRIG"}, - //{trigger, "TRIGGER"}, - {ShowOptions::trigger, "TRIGGERS", 4}, - //{check, "CHECK"}, - {ShowOptions::check, "CHECKS", 5}, - {ShowOptions::database, "DB", 0}, - {ShowOptions::database, "DATABASE", 0}, - //{comment, "COMMENT"}, - {ShowOptions::comment, "COMMENTS", 7}, - {ShowOptions::dependency, "DEPENDENCY", 5}, - {ShowOptions::dependency, "DEPENDENCIES", 5}, - {ShowOptions::collation, "COLLATES", 7}, - {ShowOptions::collation, "COLLATIONS", 9}, - {ShowOptions::security_class, "SECURITY CLASSES", 12}, - {ShowOptions::security_class, "SECCLASSES", 6}, - {ShowOptions::users, "USERS", 0}, - {ShowOptions::package, "PACKAGES", 4}, - {ShowOptions::schema, "SCHEMAS", 4}, - {ShowOptions::map, "MAPPING", 3}, - {ShowOptions::publication, "PUBLICATIONS", 3}, - {ShowOptions::wireStats, "WIRE_STATISTICS", 9}, - {ShowOptions::wireStats, "WIRE_STATS", 10} - }; + const OptionsBase showoptions(options, FB_NELEM(options), 0); - const ShowOptions showoptions(options, FB_NELEM(options), ShowOptions::wrong); - - - // Can't show nothing, return an error - - if (!cmd[1] || !*cmd[1]) + if (!std::holds_alternative(node) && + !std::holds_alternative(node)) { - TEXT msg_string[MSG_LENGTH]; - IUTILS_msg_get(VALID_OPTIONS, msg_string); - isqlGlob.printf("%s\n", msg_string); - showoptions.showCommands(isqlGlob.Out); - return ps_ERR; - } - - processing_state ret = SKIP; - // Only show version and show sql dialect work if there is no db attached - bool handled = true; - if ((!strcmp(cmd[1], "VERSION")) || (!strcmp(cmd[1], "VER"))) - { - TEXT msg_string[MSG_LENGTH]; - IUTILS_msg_get(VERSION, msg_string, SafeArg() << FB_VERSION); - isqlGlob.printf("%s%s", msg_string, NEWLINE); - isqlGlob.printf("Server version:%s", NEWLINE); - VersionCallback callback; - Firebird::UtilInterfacePtr()->getFbVersion(fbStatus, DB, &callback); - if (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS) - { - IUTILS_msg_get(CANNOT_GET_SRV_VER, msg_string); - STDERROUT(msg_string); - } - } - else if (!strcmp(cmd[1], "SQL")) - { - if (!strcmp(cmd[2], "DIALECT")) - ret = show_dialect(); - else - ret = ps_ERR; - } - else - { - handled = false; if (!ISQL_dbcheck()) - ret = ps_ERR; + return ps_ERR; } - if (ret == ps_ERR || handled) - return ret; - - TEXT SQL_id_for_grant[BUFFER_LENGTH256]; int key = 0; + MetaString notFoundName; - switch (showoptions.getCommand(cmd[1])) - { - case ShowOptions::role: - if (isqlGlob.major_ods >= ODS_VERSION9) + const auto ret = std::visit(StdVisitOverloads{ + [](const FrontendParser::InvalidNode&) { - if (*cmd[2]) + return CONT; + }, + + [&](const FrontendParser::ShowNode& node) + { + TEXT msg_string[MSG_LENGTH]; + IUTILS_msg_get(VALID_OPTIONS, msg_string); + isqlGlob.printf("%s\n", msg_string); + showoptions.showCommands(isqlGlob.Out); + return ps_ERR; + }, + + [&](const FrontendParser::ShowChecksNode& node) + { + const auto ret = show_check((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) { - if (*cmd[2] == '"') + if (node.name) { - remove_delimited_double_quotes(lcmd[2]); - ret = show_role(lcmd[2], false); + FOR FIRST 1 R IN RDB$RELATIONS + WITH R.RDB$RELATION_NAME EQ node.name->c_str() + { + key = NO_CHECKS_ON_REL; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + + notFoundName = node.name.value(); + } + + if (!key) + key = NO_TABLE; + } + + return ret; + }, + + [&](const FrontendParser::ShowCollationsNode& node) + { + const auto ret = show_collations((node.name ? node.name->c_str() : ""), 0); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_COLLATION; + notFoundName = node.name.value(); + } + else + key = NO_COLLATIONS; + } + + return ret; + }, + + [&](const FrontendParser::ShowCommentsNode&) + { + const auto ret = show_comments(cmmShow, 0); + + if (ret == OBJECT_NOT_FOUND) + key = NO_COMMENTS; + + return ret; + }, + + [](const FrontendParser::ShowDatabaseNode&) + { + show_db(); + return SKIP; + }, + + [&](const FrontendParser::ShowDependenciesNode& node) + { + const auto ret = show_dependencies((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + key = NO_DEPENDENCIES; + + return ret; + }, + + [&](const FrontendParser::ShowDomainsNode& node) + { + const auto ret = show_domains((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_DOMAIN; + notFoundName = node.name.value(); + } + else + key = NO_DOMAINS; + } + + return ret; + }, + + [&](const FrontendParser::ShowExceptionsNode& node) + { + const auto ret = show_exceptions((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_EXCEPTION; + notFoundName = node.name.value(); + } + else + key = NO_EXCEPTIONS; + } + + return ret; + }, + + [&](const FrontendParser::ShowFiltersNode& node) + { + const auto ret = show_filters((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_FILTER; + notFoundName = node.name.value(); + } + else + key = NO_FILTERS; + } + + return ret; + }, + + [&](const FrontendParser::ShowFunctionsNode& node) + { + const auto ret = show_functions( + (node.name ? node.name->c_str() : ""), + (node.package ? node.package->c_str() : ""), + false, false); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_FUNCTION; + notFoundName = node.name.value(); + } + else + key = NO_FUNCTIONS; + } + + return ret; + }, + + [&](const FrontendParser::ShowGeneratorsNode& node) + { + const auto ret = show_generators((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_GEN; + notFoundName = node.name.value(); + } + else + key = NO_GENS; + } + + return ret; + }, + + [&](const FrontendParser::ShowGrantsNode& node) + { + processing_state ret; + + if (node.name) + ret = SHOW_grants(node.name->c_str(), "", obj_any); + else + ret = EXTRACT_list_grants(""); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + FOR FIRST 1 R IN RDB$RELATIONS + WITH R.RDB$RELATION_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_REL; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + + if (!key) + { + FOR FIRST 1 P IN RDB$PROCEDURES + WITH P.RDB$PROCEDURE_NAME EQ node.name->c_str() AND + P.RDB$PACKAGE_NAME MISSING + { + key = NO_GRANT_ON_PROC; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 R IN RDB$ROLES + WITH R.RDB$ROLE_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_ROL; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 F IN RDB$FUNCTIONS + WITH F.RDB$FUNCTION_NAME EQ node.name->c_str() AND + F.RDB$PACKAGE_NAME MISSING + { + key = NO_GRANT_ON_FUN; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 G IN RDB$GENERATORS + WITH G.RDB$GENERATOR_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_GEN; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 E IN RDB$EXCEPTIONS + WITH E.RDB$EXCEPTION_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_XCP; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 F IN RDB$FIELDS + WITH F.RDB$FIELD_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_FLD; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 CS IN RDB$CHARACTER_SETS + WITH CS.RDB$CHARACTER_SET_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_CS; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + { + FOR FIRST 1 C IN RDB$COLLATIONS + WITH C.RDB$COLLATION_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_COLL; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key && isqlGlob.major_ods >= ODS_VERSION12) + { + FOR FIRST 1 P IN RDB$PACKAGES + WITH P.RDB$PACKAGE_NAME EQ node.name->c_str() + { + key = NO_GRANT_ON_PKG; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + } + + if (!key) + key = NO_OBJECT; + + notFoundName = node.name.value(); + } + else + key = NO_GRANT_ON_ANY; + } + + return ret; + }, + + [&](const FrontendParser::ShowIndexesNode& node) + { + const auto ret = show_indices((node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + FOR FIRST 1 R IN RDB$RELATIONS + WITH R.RDB$RELATION_NAME EQ node.name->c_str() + { + key = NO_INDICES_ON_REL; + } + END_FOR + ON_ERROR + // Ignore any error + END_ERROR + + notFoundName = node.name.value(); + + if (!key) + key = NO_REL_OR_INDEX; + } + else + key = NO_INDICES; + } + + return ret; + }, + + [&](const FrontendParser::ShowMappingsNode& node) + { + const auto ret = SHOW_maps(false, (node.name ? node.name->c_str() : "")); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_MAP; + notFoundName = node.name.value(); + } + else + key = NO_MAPS; + } + + return ret; + }, + + [&](const FrontendParser::ShowPackagesNode& node) + { + const auto ret = show_packages((node.name ? node.name->c_str() : ""), false); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_PACKAGE; + notFoundName = node.name.value(); + } + else + key = NO_PACKAGES; + } + + return ret; + }, + + [&](const FrontendParser::ShowProceduresNode& node) + { + const auto ret = show_proc( + (node.name ? node.name->c_str() : ""), + (node.package ? node.package->c_str() : ""), + false, false); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_PROC; + notFoundName = node.name.value(); + } + else + key = NO_PROCS; + } + + return ret; + }, + + [&](const FrontendParser::ShowPublicationsNode& node) + { + const auto ret = show_publications((node.name ? node.name->c_str() : ""), false); + + if (ret == OBJECT_NOT_FOUND) + { + if (node.name) + { + key = NO_PUBLICATION; + notFoundName = node.name.value(); + } + else + key = NO_PUBLICATIONS; + } + + return ret; + }, + + [&](const FrontendParser::ShowRolesNode& node) + { + processing_state ret; + + if (isqlGlob.major_ods >= ODS_VERSION9) + { + if (node.name) + { + ret = show_role(node.name->c_str(), false); + + if (ret == OBJECT_NOT_FOUND) + { + key = NO_ROLE; + notFoundName = node.name.value(); + } } else { - ret = show_role(cmd[2], false); + ret = show_role(nullptr, false); + if (ret == OBJECT_NOT_FOUND) + key = NO_ROLES; } - - if (ret == OBJECT_NOT_FOUND) - key = NO_ROLE; } else { - ret = show_role(NULL, false); - if (ret == OBJECT_NOT_FOUND) - key = NO_ROLES; + ret = OBJECT_NOT_FOUND; + key = NO_ROLES; } - } - else - { - ret = OBJECT_NOT_FOUND; - key = NO_ROLES; - } - break; - case ShowOptions::table: - if (*cmd[2]) + return ret; + }, + + [&](const FrontendParser::ShowSecClassesNode& node) { - if (*cmd[2] == '"') + const auto ret = show_secclass(node.name, node.detail); + + if (ret == OBJECT_NOT_FOUND) { - remove_delimited_double_quotes(lcmd[2]); - ret = show_table(lcmd[2], false); + if (node.name) + { + key = NO_SECCLASS; + notFoundName = node.name.value(); + } + else + key = NO_DB_WIDE_SECCLASS; + } + + return ret; + }, + + [](const FrontendParser::ShowSqlDialectNode& node) + { + return show_dialect(); + }, + + [](const FrontendParser::ShowSystemNode& node) + { + if (node.objType) + { + switch (node.objType.value()) + { + case obj_collation: + show_collations("", 1); + break; + + case obj_udf: + show_functions(nullptr, nullptr, false, true); + break; + + case obj_relation: + show_all_tables(1); + break; + + case obj_sql_role: + show_role(nullptr, true); + break; + + case obj_procedure: + show_proc(nullptr, nullptr, false, true); + break; + + case obj_package_header: + show_packages(nullptr, true); + break; + + case obj_publication: + show_publications(nullptr, true); + break; + + default: + return ps_ERR; + } } else { - ret = show_table(cmd[2], false); - } - - if (ret == OBJECT_NOT_FOUND) - key = NO_TABLE; - } - else - { - ret = show_all_tables(0); - if (ret == OBJECT_NOT_FOUND) - key = NO_TABLES; - } - break; - - case ShowOptions::view: - if (*cmd[2]) - { - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_table(lcmd[2], true); - } - else - { - ret = show_table(cmd[2], true); - } - - if (ret == OBJECT_NOT_FOUND) - key = NO_VIEW; - } - else - { - ret = show_all_tables(-1); - if (ret == OBJECT_NOT_FOUND) - key = NO_VIEWS; - } - break; - - case ShowOptions::system: - if (*cmd[2]) - { - switch (showoptions.getCommand(cmd[2])) - { - case ShowOptions::collation: - show_collations("", 1); - break; - - case ShowOptions::function: - show_functions(nullptr, false, true); - break; - - case ShowOptions::table: + TEXT msg[MSG_LENGTH]; + IUTILS_msg_get(MSG_TABLES, msg); + isqlGlob.printf("%s%s", msg, NEWLINE); show_all_tables(1); - break; - - case ShowOptions::role: - show_role(NULL, true); - break; - - case ShowOptions::procedure: - show_proc(NULL, false, true); - break; - - case ShowOptions::package: - show_packages(NULL, true); - break; - - case ShowOptions::publication: - show_publications(NULL, true); - break; - - default: - return ps_ERR; + IUTILS_msg_get(MSG_FUNCTIONS, msg); + show_functions(nullptr, nullptr, false, true, msg); + IUTILS_msg_get(MSG_PROCEDURES, msg); + show_proc(nullptr, nullptr, false, true, msg); + IUTILS_msg_get(MSG_PACKAGES, msg); + show_packages(nullptr, true, msg); + IUTILS_msg_get(MSG_COLLATIONS, msg); + show_collations("", 1, msg, true); + IUTILS_msg_get(MSG_ROLES, msg); + show_role(nullptr, true, msg); + IUTILS_msg_get(MSG_PUBLICATIONS, msg); + show_publications(nullptr, true, msg); } - } - else - { - TEXT msg[MSG_LENGTH]; - IUTILS_msg_get(MSG_TABLES, msg); - isqlGlob.printf("%s%s", msg, NEWLINE); - show_all_tables(1); - IUTILS_msg_get(MSG_FUNCTIONS, msg); - show_functions(nullptr, false, true, msg); - IUTILS_msg_get(MSG_PROCEDURES, msg); - show_proc(NULL, false, true, msg); - IUTILS_msg_get(MSG_PACKAGES, msg); - show_packages(NULL, true, msg); - IUTILS_msg_get(MSG_COLLATIONS, msg); - show_collations("", 1, msg, true); - IUTILS_msg_get(MSG_ROLES, msg); - show_role(NULL, true, msg); - IUTILS_msg_get(MSG_PUBLICATIONS, msg); - show_publications(NULL, true, msg); - } - break; - case ShowOptions::index: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_indices(lcmd); - } - else - { - ret = show_indices(cmd); - } + return SKIP; + }, - if (ret == OBJECT_NOT_FOUND) + [&](const FrontendParser::ShowTablesNode& node) { - if (*cmd[2]) + processing_state ret; + + if (node.name) { - FOR FIRST 1 R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ cmd[2] + ret = show_table(node.name->c_str(), false); - key = NO_INDICES_ON_REL; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - if (!key) - key = NO_REL_OR_INDEX; + if (ret == OBJECT_NOT_FOUND) + { + key = NO_TABLE; + notFoundName = node.name.value(); + } } else { - key = NO_INDICES; + ret = show_all_tables(0); + if (ret == OBJECT_NOT_FOUND) + key = NO_TABLES; } - } - break; - case ShowOptions::domain: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_domains(lcmd[2]); - } - else - ret = show_domains(cmd[2]); + return ret; + }, - if (ret == OBJECT_NOT_FOUND) + [&](const FrontendParser::ShowTriggersNode& node) { - if (*cmd[2]) - key = NO_DOMAIN; - else - key = NO_DOMAINS; - } - break; + const auto ret = show_trigger((node.name ? node.name->c_str() : ""), true, true); - case ShowOptions::exception: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_exceptions(lcmd[2]); - } - else - ret = show_exceptions(cmd[2]); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_EXCEPTION; - else - key = NO_EXCEPTIONS; - } - break; - - case ShowOptions::filter: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_filters(lcmd[2]); - } - else - ret = show_filters(cmd[2]); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_FILTER; - else - key = NO_FILTERS; - } - break; - - case ShowOptions::function: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_functions(lcmd[2], true, false); - } - else - ret = show_functions(cmd[2], false, false); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_FUNCTION; - else - key = NO_FUNCTIONS; - } - break; - - case ShowOptions::generator: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_generators(lcmd[2]); - } - else - ret = show_generators(cmd[2]); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_GEN; - else - key = NO_GENS; - } - break; - - case ShowOptions::grant: - if (*cmd[2]) - { - if (*cmd[2] == '"') + if (ret == OBJECT_NOT_FOUND) { - remove_delimited_double_quotes(lcmd[2]); - strcpy(SQL_id_for_grant, lcmd[2]); + if (node.name) + { + key = NO_TRIGGER; + notFoundName = node.name.value(); + } + else + key = NO_TRIGGERS; } - else - strcpy(SQL_id_for_grant, cmd[2]); - ret = SHOW_grants(SQL_id_for_grant, "", obj_any); - } - else - { - strcpy(SQL_id_for_grant, cmd[2]); - ret = EXTRACT_list_grants(""); - } - if (ret == OBJECT_NOT_FOUND) + return ret; + }, + + [&](const FrontendParser::ShowUsersNode& node) { - if (*cmd[2]) + const auto ret = show_users(); + + if (ret == OBJECT_NOT_FOUND) // It seems impossible, but... + key = NO_CONNECTED_USERS; + + return ret; + }, + + [](const FrontendParser::ShowVersionNode&) + { + TEXT msg_string[MSG_LENGTH]; + IUTILS_msg_get(VERSION, msg_string, SafeArg() << FB_VERSION); + isqlGlob.printf("%s%s", msg_string, NEWLINE); + isqlGlob.printf("Server version:%s", NEWLINE); + VersionCallback callback; + UtilInterfacePtr()->getFbVersion(fbStatus, DB, &callback); + if (fbStatus->getState() & IStatus::STATE_ERRORS) { - FOR FIRST 1 R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_REL; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - if (!key) - { - FOR FIRST 1 P IN RDB$PROCEDURES - WITH P.RDB$PROCEDURE_NAME EQ SQL_id_for_grant AND - P.RDB$PACKAGE_NAME MISSING - - key = NO_GRANT_ON_PROC; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 R IN RDB$ROLES - WITH R.RDB$ROLE_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_ROL; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 F IN RDB$FUNCTIONS - WITH F.RDB$FUNCTION_NAME EQ SQL_id_for_grant AND - F.RDB$PACKAGE_NAME MISSING - - key = NO_GRANT_ON_FUN; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 G IN RDB$GENERATORS - WITH G.RDB$GENERATOR_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_GEN; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 E IN RDB$EXCEPTIONS - WITH E.RDB$EXCEPTION_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_XCP; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 F IN RDB$FIELDS - WITH F.RDB$FIELD_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_FLD; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 CS IN RDB$CHARACTER_SETS - WITH CS.RDB$CHARACTER_SET_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_CS; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - { - FOR FIRST 1 C IN RDB$COLLATIONS - WITH C.RDB$COLLATION_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_COLL; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key && isqlGlob.major_ods >= ODS_VERSION12) - { - FOR FIRST 1 P IN RDB$PACKAGES - WITH P.RDB$PACKAGE_NAME EQ SQL_id_for_grant - - key = NO_GRANT_ON_PKG; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - } - if (!key) - key = NO_OBJECT; + IUTILS_msg_get(CANNOT_GET_SRV_VER, msg_string); + STDERROUT(msg_string); } - else { - key = NO_GRANT_ON_ANY; - } - } - break; - case ShowOptions::procedure: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_proc(lcmd[2], true, false); - } - else - ret = show_proc(cmd[2], false, false); + return SKIP; + }, - if (ret == OBJECT_NOT_FOUND) + [&](const FrontendParser::ShowViewsNode& node) { - if (*cmd[2]) - key = NO_PROC; - else - key = NO_PROCS; - } - break; + processing_state ret; - case ShowOptions::trigger: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_trigger(lcmd[2], true, true); - } - else - ret = show_trigger(cmd[2], true, true); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) + if (node.name) { - /* - FOR FIRST 1 R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ cmd[2] + ret = show_table(node.name->c_str(), true); - key = NO_TRIGGERS_ON_REL; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; - if (!key) - key = NO_REL_OR_TRIGGER; - */ - key = NO_TRIGGER; + if (ret == OBJECT_NOT_FOUND) + { + key = NO_VIEW; + notFoundName = node.name.value(); + } } else - key = NO_TRIGGERS; - } - break; - - case ShowOptions::check: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_check(lcmd[2]); - } - else - ret = show_check(cmd[2]); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) { - FOR FIRST 1 R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ cmd[2] - - key = NO_CHECKS_ON_REL; - END_FOR - ON_ERROR - // Ignore any error - END_ERROR; + ret = show_all_tables(-1); + if (ret == OBJECT_NOT_FOUND) + key = NO_VIEWS; } - if (!key) - key = NO_TABLE; - } - break; - case ShowOptions::database: - show_db(); - break; + return ret; + }, - case ShowOptions::comment: - ret = show_comments(cmmShow, 0); - if (ret == OBJECT_NOT_FOUND) - key = NO_COMMENTS; - break; - - case ShowOptions::collation: - if (*cmd[2] == '"') + [](const FrontendParser::ShowWireStatsNode&) { - remove_delimited_double_quotes(lcmd[2]); - ret = show_collations(lcmd[2], -1); - } - else - ret = show_collations(cmd[2], 0); + return show_wireStats(); + }, - if (ret == OBJECT_NOT_FOUND) + [](auto& arg) { - if (*cmd[2]) - key = NO_COLLATION; - else - key = NO_COLLATIONS; + static_assert(FrontendParser::AlwaysFalseV, + "Add visitor method for that show node type"); } - break; - - case ShowOptions::dependency: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_dependencies(lcmd[2]); - } - else - ret = show_dependencies(cmd[2]); - - if (ret == OBJECT_NOT_FOUND) - key = NO_DEPENDENCIES; - break; - - case ShowOptions::security_class: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_secclass(lcmd[2], cmd[3]); - } - else - ret = show_secclass(cmd[2], cmd[3]); - - if (ret == OBJECT_NOT_FOUND) - { - if (!strcmp(cmd[2], "*")) - key = NO_DB_WIDE_SECCLASS; - else - key = NO_SECCLASS; - } - break; - - case ShowOptions::users: - ret = show_users(); - if (ret == OBJECT_NOT_FOUND) // It seems impossible, but... - key = NO_CONNECTED_USERS; - break; - - case ShowOptions::package: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_packages(lcmd[2], false); - } - else - ret = show_packages(cmd[2], false); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_PACKAGE; - else - key = NO_PACKAGES; - } - break; - - case ShowOptions::map: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = SHOW_maps(false, lcmd[2]); - } - else - ret = SHOW_maps(false, cmd[2]); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_MAP; - else - key = NO_MAPS; - } - break; - - case ShowOptions::publication: - if (*cmd[2] == '"') - { - remove_delimited_double_quotes(lcmd[2]); - ret = show_publications(lcmd[2], false); - } - else - ret = show_publications(cmd[2], false); - - if (ret == OBJECT_NOT_FOUND) - { - if (*cmd[2]) - key = NO_PUBLICATION; - else - key = NO_PUBLICATIONS; - } - break; - - case ShowOptions::schema: - return ps_ERR; - break; - - case ShowOptions::wireStats: - ret = show_wireStats(); - break; - - default: - return ps_ERR; - } // switch + }, node); if (ret == OBJECT_NOT_FOUND) { TEXT key_string[MSG_LENGTH]; - if (*cmd[2] == '"') - IUTILS_msg_get(key, key_string, SafeArg() << lcmd[2]); - else - IUTILS_msg_get(key, key_string, SafeArg() << cmd[2]); + IUTILS_msg_get(key, key_string, SafeArg() << notFoundName.c_str()); STDERROUT(key_string); } @@ -2831,23 +2798,6 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) } -static void remove_delimited_double_quotes(TEXT* string) -{ -/************************************** - * - * r e m o v e _ d e l i m i t e d _ d o u b l e _ q u o t e s - * - ************************************** - * - * Functional description - * Remove the delimited double quotes. Blanks could be part of - * delimited SQL identifier. Unescape embedded double quotes. - * - **************************************/ - IUTILS_remove_and_unescape_quotes(string, DBL_QUOTE); -} - - static void make_priv_string(USHORT flags, char* string, bool useAny) { /************************************** @@ -4267,7 +4217,8 @@ static processing_state show_filters(const SCHAR* object) } -static processing_state show_functions(const SCHAR* funcname, bool quoted, bool system, const SCHAR* msg) +static processing_state show_functions( + const char* funcname, const char* packname, bool quoted, bool system, const char* msg) { /************************************** * @@ -4352,14 +4303,11 @@ static processing_state show_functions(const SCHAR* funcname, bool quoted, bool processing_state return_state = OBJECT_NOT_FOUND; - MetaString package, function; - if (quoted) - function = funcname; - else - parse_package(funcname, package, function); + const MetaString function(funcname); + const MetaString package(packname); FOR FUN IN RDB$FUNCTIONS - WITH FUN.RDB$FUNCTION_NAME EQ function.c_str() + WITH FUN.RDB$FUNCTION_NAME EQ funcname AND FUN.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '') if (!FUN.RDB$MODULE_NAME.NULL) @@ -4838,7 +4786,7 @@ static void show_index(SCHAR* relation_name, } -static processing_state show_indices(const SCHAR* const* cmd) +static processing_state show_indices(const char* name) { /************************************** * @@ -4858,8 +4806,6 @@ static processing_state show_indices(const SCHAR* const* cmd) // The names stored in the database are all upper case - const SCHAR* name = cmd[2]; - if (*name) { FOR IDX IN RDB$INDICES WITH @@ -5187,7 +5133,7 @@ processing_state SHOW_maps(bool extract, const SCHAR* map_name) } -static processing_state show_proc(const SCHAR* procname, bool quoted, bool sys, const char* msg) +static processing_state show_proc(const char* procname, const char* packname, bool quoted, bool sys, const char* msg) { /************************************** * @@ -5282,11 +5228,9 @@ static processing_state show_proc(const SCHAR* procname, bool quoted, bool sys, // A procedure was named, so print all the info on that procedure bool first = true; - MetaString package, procedure; - if (quoted) - procedure = procname; - else - parse_package(procname, package, procedure); + + const MetaString procedure(procname); + const MetaString package(packname); FOR PRC IN RDB$PROCEDURES WITH PRC.RDB$PROCEDURE_NAME EQ procedure.c_str() AND @@ -5463,23 +5407,6 @@ static processing_state show_proc(const SCHAR* procname, bool quoted, bool sys, } -static void parse_package(const char* procname, MetaString& package, MetaString& procedure) -{ - for (const char* point = procname; *point; ++point) - { - if (*point == '.') - { - package.assign(procname, point - procname); - procedure = ++point; - return; - } - } - - package = ""; - procedure = procname; -} - - static processing_state show_publications(const SCHAR* pub_name, bool sys, const SCHAR* msg) { /************************************** @@ -5783,21 +5710,15 @@ static processing_state show_role(const SCHAR* object, bool system, const char* // Show low-level, GDML security for an object. It may be table/view or procedure. // Using SHOW SECCLASS DET[AIL] will print the contents of the sec blob. // Using SHOW SECCLASS * DET[AIL] will print the db-wide sec class in rdb$database. -static processing_state show_secclass(const char* object, const char* opt) +static processing_state show_secclass(const std::optional& object, bool detail) { - if (!object || !*object) - return ps_ERR; - - const bool detail = opt && - (fb_utils::stricmp(opt, "DETAIL") == 0 || - fb_utils::stricmp(opt, "DET") == 0); IsqlVar var; memset(&var, 0, sizeof(var)); var.subType = isc_blob_acl; int count = 0; - if (strcmp(object, "*") == 0) + if (!object) { FOR D IN RDB$DATABASE CROSS SC IN RDB$SECURITY_CLASSES @@ -5822,7 +5743,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR REL IN RDB$RELATIONS CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH REL.RDB$RELATION_NAME EQ object + WITH REL.RDB$RELATION_NAME EQ object->c_str() ++count; isqlGlob.printf("%s's main sec class %s%s", REL.RDB$VIEW_BLR.NULL ? "Table" : "View", @@ -5840,7 +5761,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR REL2 IN RDB$RELATIONS CROSS SC IN RDB$SECURITY_CLASSES - WITH REL2.RDB$RELATION_NAME EQ object + WITH REL2.RDB$RELATION_NAME EQ object->c_str() AND REL2.RDB$DEFAULT_CLASS EQ SC.RDB$SECURITY_CLASS ++count; isqlGlob.printf("%s's default sec class %s%s", @@ -5860,7 +5781,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR RF IN RDB$RELATION_FIELDS CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH RF.RDB$RELATION_NAME EQ object + WITH RF.RDB$RELATION_NAME EQ object->c_str() SORTED BY RF.RDB$FIELD_POSITION ++count; isqlGlob.printf(" Field %s - sec class %s%s", fb_utils::exact_name(RF.RDB$FIELD_NAME), @@ -5879,7 +5800,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR PR IN RDB$PROCEDURES CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH PR.RDB$PROCEDURE_NAME EQ object AND + WITH PR.RDB$PROCEDURE_NAME EQ object->c_str() AND PR.RDB$PACKAGE_NAME MISSING ++count; isqlGlob.printf("Procedure's sec class %s%s", @@ -5900,7 +5821,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR FUN IN RDB$FUNCTIONS CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH FUN.RDB$FUNCTION_NAME EQ object AND + WITH FUN.RDB$FUNCTION_NAME EQ object->c_str() AND FUN.RDB$PACKAGE_NAME MISSING ++count; isqlGlob.printf("Function's sec class %s%s", @@ -5919,7 +5840,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR PKG IN RDB$PACKAGES CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH PKG.RDB$PACKAGE_NAME EQ object + WITH PKG.RDB$PACKAGE_NAME EQ object->c_str() ++count; isqlGlob.printf("Package's sec class %s%s", fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); @@ -5937,7 +5858,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR GEN IN RDB$GENERATORS CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH GEN.RDB$GENERATOR_NAME EQ object + WITH GEN.RDB$GENERATOR_NAME EQ object->c_str() ++count; isqlGlob.printf("Sequence's sec class %s%s", fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); @@ -5955,7 +5876,7 @@ static processing_state show_secclass(const char* object, const char* opt) FOR XCP IN RDB$EXCEPTIONS CROSS SC IN RDB$SECURITY_CLASSES OVER RDB$SECURITY_CLASS - WITH XCP.RDB$EXCEPTION_NAME EQ object + WITH XCP.RDB$EXCEPTION_NAME EQ object->c_str() ++count; isqlGlob.printf("Exception's sec class %s%s", fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); diff --git a/src/isql/show_proto.h b/src/isql/show_proto.h index 169131e863..bbbe704e8e 100644 --- a/src/isql/show_proto.h +++ b/src/isql/show_proto.h @@ -26,6 +26,7 @@ #include "../common/classes/fb_string.h" #include +#include "../isql/FrontendParser.h" #include "../jrd/obj.h" void SHOW_comments(bool force); @@ -36,7 +37,7 @@ 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, bool avoid_end_in_single_line_comment = false); -processing_state SHOW_metadata(const SCHAR* const*, SCHAR**); +processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node); void SHOW_read_owner(); const Firebird::string SHOW_trigger_action(SINT64); processing_state SHOW_maps(bool extract, const SCHAR* map_name); diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp new file mode 100644 index 0000000000..61a571dfdb --- /dev/null +++ b/src/isql/tests/FrontendParserTest.cpp @@ -0,0 +1,621 @@ +/* + * 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) 2024 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + */ + +#include "firebird.h" +#include "boost/test/unit_test.hpp" +#include "../FrontendParser.h" +#include + +using namespace Firebird; + +BOOST_AUTO_TEST_SUITE(ISqlSuite) +BOOST_AUTO_TEST_SUITE(FrontendParserSuite) +BOOST_AUTO_TEST_SUITE(FrontendParserTests) + + +BOOST_AUTO_TEST_CASE(ParseCommandTest) +{ + const FrontendParser::Options parserOptions; + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "add", parserOptions))); + BOOST_TEST((std::get(FrontendParser::parse( + "add table1", parserOptions)).tableName == "TABLE1")); + BOOST_TEST((std::get(FrontendParser::parse( + "add \"table2\"", parserOptions)).tableName == "table2")); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "blobdump", parserOptions))); + + { + const auto blobDump1 = std::get(FrontendParser::parse( + "blobdump 1:2 /tmp/blob.txt", parserOptions)); + BOOST_TEST(blobDump1.blobId.gds_quad_high == 1); + BOOST_TEST(blobDump1.blobId.gds_quad_low == 2u); + BOOST_TEST(blobDump1.file.value() == "/tmp/blob.txt"); + + const auto blobDump2 = std::get(FrontendParser::parse( + "blobdump 1:2 'C:\\A dir\\blob.txt'", parserOptions)); + BOOST_TEST((blobDump2.file.value() == "C:\\A dir\\blob.txt")); + } + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "blobview", parserOptions))); + + { + const auto blobView1 = std::get(FrontendParser::parse( + "blobview 1:2", parserOptions)); + BOOST_TEST(blobView1.blobId.gds_quad_high == 1); + BOOST_TEST(blobView1.blobId.gds_quad_low == 2u); + BOOST_TEST(!blobView1.file); + } + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "connect", parserOptions))); + + { + const auto connect1 = std::get(FrontendParser::parse( + "connect 'test.fdb'", parserOptions)); + BOOST_TEST(connect1.args[0].getProcessedString() == "test.fdb"); + + const auto connect2 = std::get(FrontendParser::parse( + "connect 'test.fdb' user user", parserOptions)); + BOOST_TEST(connect2.args.size() == 3u); + } + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "copy", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "copy source destination", parserOptions))); + + { + const auto copy1 = std::get(FrontendParser::parse( + "copy source \"destination\" localhost:/tmp/database.fdb", parserOptions)); + BOOST_TEST((copy1.source == "SOURCE")); + BOOST_TEST((copy1.destination == "destination")); + BOOST_TEST(copy1.database == "localhost:/tmp/database.fdb"); + } + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "create", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "create database", parserOptions))); + + { + const auto createDatabase1 = std::get(FrontendParser::parse( + "create database 'test.fdb'", parserOptions)); + BOOST_TEST(createDatabase1.args[0].getProcessedString() == "test.fdb"); + + const auto createDatabase2 = std::get(FrontendParser::parse( + "create database 'test.fdb' user user", parserOptions)); + BOOST_TEST(createDatabase2.args.size() == 3u); + } + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "drop database x", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "drop database", parserOptions))); + + BOOST_TEST(!std::get(FrontendParser::parse( + "edit", parserOptions)).file); + BOOST_TEST(std::get(FrontendParser::parse( + "edit /tmp/file.sql", parserOptions)).file.value() == "/tmp/file.sql"); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "exit x", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "exit", parserOptions))); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "explain", parserOptions))); + BOOST_TEST(std::get(FrontendParser::parse( + "explain select 1 from rdb$database", parserOptions)).query == "select 1 from rdb$database"); + + BOOST_TEST(!std::get(FrontendParser::parse( + "help", parserOptions)).command.has_value()); + BOOST_TEST(std::get(FrontendParser::parse( + "help set", parserOptions)).command.value() == "SET"); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "help set x", parserOptions))); + + BOOST_TEST(!std::get(FrontendParser::parse( + "?", parserOptions)).command.has_value()); + BOOST_TEST(std::get(FrontendParser::parse( + "? set", parserOptions)).command.value() == "SET"); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "? set x", parserOptions))); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "input", parserOptions))); + BOOST_TEST(std::get(FrontendParser::parse( + "input /tmp/file.sql", parserOptions)).file == "/tmp/file.sql"); + + BOOST_TEST(!std::get(FrontendParser::parse( + "output", parserOptions)).file); + BOOST_TEST(std::get(FrontendParser::parse( + "output /tmp/file.txt", parserOptions)).file.value() == "/tmp/file.txt"); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "quit", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "quit x", parserOptions))); + + BOOST_TEST(!std::get(FrontendParser::parse( + "shell", parserOptions)).command); + BOOST_TEST(std::get(FrontendParser::parse( + "shell ls -l /tmp", parserOptions)).command.value() == "ls -l /tmp"); +} + +BOOST_AUTO_TEST_CASE(ParseSetTest) +{ + const FrontendParser::Options parserOptions; + + const auto parseSet = [&](const std::string_view text) { + return std::get(FrontendParser::parse(text, parserOptions)); + }; + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "\"set\"", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set x", parserOptions))); + + BOOST_TEST(std::holds_alternative(parseSet("set"))); + + BOOST_TEST(std::get(parseSet( + "set auto")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set auto on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set auto off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set autoddl")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set autoddl on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set autoddl off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set autoterm")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set autoterm on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set autoterm off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set bail")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set bail on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set bail off x", parserOptions))); + + BOOST_TEST((std::get(parseSet( + "set bulk_insert insert into mytable (a, b) values (1, ?)")).statement == + "insert into mytable (a, b) values (1, ?)")); + + BOOST_TEST(std::get(parseSet( + "set blob")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set blob on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set blob off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set blobdisplay")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set blobdisplay on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set blobdisplay off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set count")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set count on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set count off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set echo")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set echo on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set echo off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set exec_path_display")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set exec_path_display blr")).arg == "BLR")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set exec_path_display off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set explain")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set explain on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set explain off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set heading")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set heading on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set heading off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set keep_tran")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set keep_tran on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set keep_tran off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set keep_tran_params")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set keep_tran_params on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set keep_tran_params off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set list")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set list on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set list off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set local_timeout")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set local_timeout 80")).arg == "80")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set local_timeout 90 x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set maxrows")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set maxrows 80")).arg == "80")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set maxrows 90 x", parserOptions))); + + BOOST_TEST(!std::get(parseSet( + "set names")).name.has_value()); + BOOST_TEST((std::get(parseSet( + "set names utf8")).name == "UTF8")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set names utf8 x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set per_tab")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set per_tab on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set per_tab off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set per_table_stats")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set per_table_stats on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set per_table_stats off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set plan")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set plan on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set plan off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set planonly")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set planonly on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set planonly off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set rowcount")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set rowcount 80")).arg == "80")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set rowcount 90 x", parserOptions))); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set sql", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set sql dialect", parserOptions))); + BOOST_TEST((std::get(parseSet( + "set sql dialect 3")).arg == "3")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set sql dialect 3 x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set sqlda_display")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set sqlda_display on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set sqlda_display off x", parserOptions))); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set sta", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set sta on", parserOptions))); + BOOST_TEST(std::get(parseSet( + "set stat")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set stat on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set stat off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set stats")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set stats on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set stats off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set term")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set term !")).arg == "!")); + BOOST_TEST((std::get(parseSet( + "set term Go")).arg == "Go")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set term a b", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set terminator")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set terminator !")).arg == "!")); + BOOST_TEST((std::get(parseSet( + "set terminator Go")).arg == "Go")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set terminator a b", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set time")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set time on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set time off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set transaction")).statement == "set transaction"); + BOOST_TEST(std::get(parseSet( + "set transaction read committed")).statement == "set transaction read committed"); + + BOOST_TEST(std::get(parseSet( + "set warning")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set warning on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set warning off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set warnings")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set warnings on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set warnings off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set wng")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set wng on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set wng off x", parserOptions))); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set width", parserOptions))); + BOOST_TEST((std::get(parseSet( + "set width x")).column == "X")); + BOOST_TEST(std::get(parseSet( + "set width x")).width.empty()); + BOOST_TEST((std::get(parseSet( + "set width x 80")).column == "X")); + BOOST_TEST((std::get(parseSet( + "set width x 90")).width == "90")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set width x 90 y", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set wire")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set wire on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set wire off x", parserOptions))); + + BOOST_TEST(std::get(parseSet( + "set wire_stats")).arg.empty()); + BOOST_TEST((std::get(parseSet( + "set wire_stats on")).arg == "ON")); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set wire_stats off x", parserOptions))); + + // Engine commands. + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set decfloat", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set decfloat x", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set generator", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set generator x", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set role", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set role x", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set statistics", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set statistics x", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set time zone", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set trusted", parserOptions))); + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "set trusted x", parserOptions))); +} + +BOOST_AUTO_TEST_CASE(ParseShowTest) +{ + const FrontendParser::Options parserOptions; + + const auto parseShow = [&](const std::string_view text) { + return std::get(FrontendParser::parse(text, parserOptions)); + }; + + BOOST_TEST((std::holds_alternative(parseShow("show")))); + + BOOST_TEST(!std::get(parseShow( + "show check")).name); + BOOST_TEST((std::get(parseShow( + "show check name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show collate")).name); + BOOST_TEST((std::get(parseShow( + "show collate name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show collation")).name); + BOOST_TEST((std::get(parseShow( + "show collation name")).name == "NAME")); + + BOOST_TEST(std::holds_alternative(parseShow( + "show comments"))); + + BOOST_TEST(!std::get(parseShow( + "show depen")).name); + BOOST_TEST((std::get(parseShow( + "show depen name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show domain")).name); + BOOST_TEST((std::get(parseShow( + "show domain name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show excep")).name); + BOOST_TEST((std::get(parseShow( + "show excep name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show filter")).name); + BOOST_TEST((std::get(parseShow( + "show filter name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show func")).name); + BOOST_TEST((std::get(parseShow( + "show func name")).name == "NAME")); + BOOST_TEST((std::get(parseShow( + "show func package.name")).package == "PACKAGE")); + BOOST_TEST((std::get(parseShow( + "show func package.name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show ind")).name); + BOOST_TEST((std::get(parseShow( + "show index name")).name == "NAME")); + BOOST_TEST((std::get(parseShow( + "show indices name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show gen")).name); + BOOST_TEST((std::get(parseShow( + "show generator name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show map")).name); + BOOST_TEST((std::get(parseShow( + "show mapping name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show pack")).name); + BOOST_TEST((std::get(parseShow( + "show package name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show proc")).name); + BOOST_TEST((std::get(parseShow( + "show proc name")).name == "NAME")); + BOOST_TEST((std::get(parseShow( + "show proc package.name")).package == "PACKAGE")); + BOOST_TEST((std::get(parseShow( + "show proc package.name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show pub")).name); + BOOST_TEST((std::get(parseShow( + "show publication name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show role")).name); + BOOST_TEST((std::get(parseShow( + "show roles name")).name == "NAME")); + + BOOST_TEST(std::holds_alternative(FrontendParser::parse( + "show seccla", parserOptions))); + BOOST_TEST(!std::get(parseShow( + "show seccla *")).detail); + BOOST_TEST(std::get(parseShow( + "show seccla * detail")).detail); + BOOST_TEST(!std::get(parseShow( + "show seccla * detail")).name); + BOOST_TEST((std::get(parseShow( + "show secclasses name")).name == "NAME")); + + BOOST_TEST((!std::get(parseShow( + "show system")).objType.has_value())); + BOOST_TEST((!std::get(parseShow( + "show system table")).objType == obj_relation)); + BOOST_TEST((std::get(parseShow( + "show table \"test\"")).name == "test")); + BOOST_TEST((std::get(parseShow( + "show table \"te\"\"st\"")).name == "te\"st")); + + BOOST_TEST(!std::get(parseShow( + "show table")).name); + BOOST_TEST((std::get(parseShow( + "show tables name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show trig")).name); + BOOST_TEST((std::get(parseShow( + "show triggers name")).name == "NAME")); + + BOOST_TEST(!std::get(parseShow( + "show view")).name); + BOOST_TEST((std::get(parseShow( + "show views name")).name == "NAME")); + + BOOST_TEST(std::holds_alternative(parseShow( + "show wire_stat"))); + BOOST_TEST(std::holds_alternative(parseShow( + "show wire_statistics"))); +} + + +BOOST_AUTO_TEST_SUITE_END() // FrontendParserTests +BOOST_AUTO_TEST_SUITE_END() // FrontendParserSuite +BOOST_AUTO_TEST_SUITE_END() // ISqlSuite diff --git a/src/isql/tests/ISqlTest.cpp b/src/isql/tests/ISqlTest.cpp index 2531df796c..dddf8ff03e 100644 --- a/src/isql/tests/ISqlTest.cpp +++ b/src/isql/tests/ISqlTest.cpp @@ -4,16 +4,3 @@ #define BOOST_TEST_MODULE ISqlTest #include "boost/test/included/unit_test.hpp" - - -// TODO: Remove. - -BOOST_AUTO_TEST_SUITE(IsqlSuite) -BOOST_AUTO_TEST_SUITE(DummySuite) - -BOOST_AUTO_TEST_CASE(DummyTest) -{ -} - -BOOST_AUTO_TEST_SUITE_END() // DummySuite -BOOST_AUTO_TEST_SUITE_END() // IsqlSuite diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 0bfd7bd55d..77e8faa7cb 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -24,6 +24,8 @@ #ifndef JRD_OBJ_H #define JRD_OBJ_H +#include "../common/gdsassert.h" + // 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. @@ -76,10 +78,11 @@ const ObjectType obj_index_condition = 37; const ObjectType obj_type_MAX = 38; -// used in the parser only / no relation with obj_type_MAX (should be greater) +// not used in metadata / no relation with obj_type_MAX (should be greater) const ObjectType obj_user_or_role= 100; const ObjectType obj_parameter = 101; const ObjectType obj_column = 102; +const ObjectType obj_publication = 103; const ObjectType obj_any = 255; From 73573554efad4be66365750540f05633d293e344 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 30 Dec 2024 10:39:53 -0300 Subject: [PATCH 20/49] Better temporary backward compatibility. --- src/isql/FrontendLexer.cpp | 41 +++++++++++++++++++++++-------------- src/isql/FrontendLexer.h | 3 --- src/isql/FrontendParser.cpp | 4 ++-- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/isql/FrontendLexer.cpp b/src/isql/FrontendLexer.cpp index 2a3c3602a0..fe75aad657 100644 --- a/src/isql/FrontendLexer.cpp +++ b/src/isql/FrontendLexer.cpp @@ -187,22 +187,8 @@ FrontendLexer::Token FrontendLexer::getToken() switch (toupper(*pos)) { - case '(': - token.type = Token::TYPE_OPEN_PAREN; - token.processedText = *pos++; - break; - - case ')': - token.type = Token::TYPE_CLOSE_PAREN; - token.processedText = *pos++; - break; - - case ',': - token.type = Token::TYPE_COMMA; - token.processedText = *pos++; - break; - case ';': + case '.': token.type = Token::TYPE_OTHER; token.processedText = *pos++; break; @@ -237,6 +223,7 @@ FrontendLexer::Token FrontendLexer::getNameToken() if (const auto optStringToken = getStringToken(); optStringToken.has_value()) return optStringToken.value(); + /*** Revert to strict parsing with schemas support branch. const auto start = pos; bool first = true; @@ -265,6 +252,30 @@ FrontendLexer::Token FrontendLexer::getNameToken() std::transform(token.processedText.begin(), token.processedText.end(), token.processedText.begin(), toupper); + return token; + ***/ + + const auto start = pos; + + switch (toupper(*pos)) + { + case ';': + token.type = Token::TYPE_OTHER; + token.processedText = *pos++; + break; + + default: + while (pos != end && !fb_utils::isspace(*pos) && *pos != '.') + ++pos; + + token.processedText = std::string(start, pos); + std::transform(token.processedText.begin(), token.processedText.end(), + token.processedText.begin(), toupper); + break; + } + + token.rawText = std::string(start, pos); + return token; } diff --git a/src/isql/FrontendLexer.h b/src/isql/FrontendLexer.h index 1357da7a14..bab64c8de3 100644 --- a/src/isql/FrontendLexer.h +++ b/src/isql/FrontendLexer.h @@ -39,9 +39,6 @@ public: TYPE_EOF, TYPE_STRING, TYPE_META_STRING, - TYPE_OPEN_PAREN, - TYPE_CLOSE_PAREN, - TYPE_COMMA, TYPE_OTHER }; diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index 46735e7350..d5d299fa2e 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -529,7 +529,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() if (node.name) { - if (const auto token = lexer.getNameToken(); + if (const auto token = lexer.getToken(); token.type == Token::TYPE_OTHER && token.rawText == ".") { node.package = node.name; @@ -565,7 +565,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() if (node.name) { - if (const auto token = lexer.getNameToken(); + if (const auto token = lexer.getToken(); token.type == Token::TYPE_OTHER && token.rawText == ".") { node.package = node.name; From 2c9152390a699d9d6ce6b1798beb0bb668687804 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 30 Dec 2024 11:57:30 -0300 Subject: [PATCH 21/49] Change tests log_level to error. --- builds/install/arch-specific/android/BuildFinalPackage.sh | 2 +- builds/win32/run_tests.bat | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builds/install/arch-specific/android/BuildFinalPackage.sh b/builds/install/arch-specific/android/BuildFinalPackage.sh index b7611e905f..aced8334b6 100755 --- a/builds/install/arch-specific/android/BuildFinalPackage.sh +++ b/builds/install/arch-specific/android/BuildFinalPackage.sh @@ -43,7 +43,7 @@ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "rm -rf $AndroidDir $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "mkdir $AndroidDir" $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName push gen/$InitialDebugTar $AndroidDir/ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir && tar xvf $InitialDebugTar)" -$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./common_test --log_level=all && ./libEngine14_test --log_level=all)" +$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./common_test --log_level=error && ./libEngine14_test --log_level=error)" $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./AfterUntar.sh)" $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName pull $AndroidDir/firebird/firebird.msg gen/Release/firebird/ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName pull $AndroidDir/firebird/security6.fdb gen/Release/firebird/ diff --git a/builds/win32/run_tests.bat b/builds/win32/run_tests.bat index a40fd740ef..9942fbaf71 100644 --- a/builds/win32/run_tests.bat +++ b/builds/win32/run_tests.bat @@ -5,8 +5,8 @@ @call setenvvar.bat %* @if errorlevel 1 (goto :END) -@%FB_BIN_DIR%\common_test --log_level=all || exit /b -@%FB_BIN_DIR%\engine_test --log_level=all || exit /b -@%FB_BIN_DIR%\isql_test --log_level=all || exit /b +@%FB_BIN_DIR%\common_test --log_level=error || exit /b +@%FB_BIN_DIR%\engine_test --log_level=error || exit /b +@%FB_BIN_DIR%\isql_test --log_level=error || exit /b :END From 7ff5eab2086a93da04170318af9af0a60d6e3975 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 30 Dec 2024 11:59:12 -0300 Subject: [PATCH 22/49] Run isql_test in android build. --- builds/install/arch-specific/android/BuildFinalPackage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/install/arch-specific/android/BuildFinalPackage.sh b/builds/install/arch-specific/android/BuildFinalPackage.sh index aced8334b6..94518b77e2 100755 --- a/builds/install/arch-specific/android/BuildFinalPackage.sh +++ b/builds/install/arch-specific/android/BuildFinalPackage.sh @@ -43,7 +43,7 @@ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "rm -rf $AndroidDir $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "mkdir $AndroidDir" $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName push gen/$InitialDebugTar $AndroidDir/ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir && tar xvf $InitialDebugTar)" -$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./common_test --log_level=error && ./libEngine14_test --log_level=error)" +$ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./common_test --log_level=error && ./libEngine14_test --log_level=error && ./isql_test --log_level=error)" $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName shell "(cd $AndroidDir/firebird && ./AfterUntar.sh)" $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName pull $AndroidDir/firebird/firebird.msg gen/Release/firebird/ $ANDROID_HOME/platform-tools/adb -s $AndroidDeviceName pull $AndroidDir/firebird/security6.fdb gen/Release/firebird/ From c3c8dd99e97ce869aba1bcb8bb9978a495693f54 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 30 Dec 2024 20:16:28 +0000 Subject: [PATCH 23/49] 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 a53b7891c3..5b1a378e30 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:565 + FORMAL BUILD NUMBER:570 */ -#define PRODUCT_VER_STRING "6.0.0.565" -#define FILE_VER_STRING "WI-T6.0.0.565" -#define LICENSE_VER_STRING "WI-T6.0.0.565" -#define FILE_VER_NUMBER 6, 0, 0, 565 +#define PRODUCT_VER_STRING "6.0.0.570" +#define FILE_VER_STRING "WI-T6.0.0.570" +#define LICENSE_VER_STRING "WI-T6.0.0.570" +#define FILE_VER_NUMBER 6, 0, 0, 570 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "565" +#define FB_BUILD_NO "570" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 96fb86b0b3..0a67bb669e 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=565 +BuildNum=570 NowAt=`pwd` cd `dirname $0` From e7d5a003ea3fbe89ae333a96288a1faabc133e40 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 31 Dec 2024 14:17:40 -0300 Subject: [PATCH 24/49] Fix problem with CONNECT ROLE subclause - thanks Pavel Zotov. --- src/isql/isql.epp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 7370e160fe..640765d2aa 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -4969,7 +4969,7 @@ static processing_state frontend(const std::string& statement) } else if (clause == "ROLE") { - sql_role_nm = node.args[i + 1].processedText.c_str(); + sql_role_nm = node.args[i + 1].rawText.c_str(); i += 2; } else if (!clause.empty()) From 81bba14984b1bf99a01480f6de9f4caa3ad196e3 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 31 Dec 2024 20:16:16 +0000 Subject: [PATCH 25/49] 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 5b1a378e30..b235c5a9c3 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:570 + FORMAL BUILD NUMBER:571 */ -#define PRODUCT_VER_STRING "6.0.0.570" -#define FILE_VER_STRING "WI-T6.0.0.570" -#define LICENSE_VER_STRING "WI-T6.0.0.570" -#define FILE_VER_NUMBER 6, 0, 0, 570 +#define PRODUCT_VER_STRING "6.0.0.571" +#define FILE_VER_STRING "WI-T6.0.0.571" +#define LICENSE_VER_STRING "WI-T6.0.0.571" +#define FILE_VER_NUMBER 6, 0, 0, 571 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "570" +#define FB_BUILD_NO "571" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 0a67bb669e..4a2770d795 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=570 +BuildNum=571 NowAt=`pwd` cd `dirname $0` From b4e2ea4db36d11a25245220736ee009e87b7c71b Mon Sep 17 00:00:00 2001 From: TreeHunter <60896014+TreeHunter9@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:30:26 +0300 Subject: [PATCH 26/49] Fix race in shutdown thread start (#8380) Previously we assign value to shutdownSemaphore after shutdownThread is started, where it is already needed. So we can have situation where shutdownThread instantly leaving due to shutdownSemaphore == nullptr, and we are left with a server that can only be stopped with kill -9. Co-authored-by: Artyom Ivanov --- src/yvalve/why.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index cc722930c2..2aff649f03 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -831,11 +831,11 @@ private: explicit CtrlCHandler(MemoryPool& p) : ShutdownInit(p) { + shutdownSemaphore = &semaphore; Thread::start(shutdownThread, 0, 0, &handle); procInt = ISC_signal(SIGINT, handlerInt, 0); procTerm = ISC_signal(SIGTERM, handlerTerm, 0); - shutdownSemaphore = &semaphore; } ~CtrlCHandler() From b2d03a0f51de2d9b25639d0e6e0f8c55f9e45487 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 10 Jan 2025 11:19:54 +0300 Subject: [PATCH 27/49] More correct plan output for subqueries generated during NOT IN transformation --- src/dsql/BoolNodes.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 38975713c5..b677ae5e11 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -2176,16 +2176,20 @@ BoolExprNode* RseBoolNode::convertNeqAllToNotAny(thread_db* tdbb, CompilerScratc andNode->arg1 = missNode; + RseNode* newInnerRse1 = innerRse->clone(csb->csb_pool); + newInnerRse1->flags |= RseNode::FLAG_SUB_QUERY; + RseBoolNode* rseBoolNode = FB_NEW_POOL(csb->csb_pool) RseBoolNode(csb->csb_pool, blr_any); - rseBoolNode->rse = innerRse; + rseBoolNode->rse = newInnerRse1; rseBoolNode->ownSavepoint = this->ownSavepoint; andNode->arg2 = rseBoolNode; - RseNode* newInnerRse = innerRse->clone(csb->csb_pool); + RseNode* newInnerRse2 = innerRse->clone(csb->csb_pool); + newInnerRse2->flags |= RseNode::FLAG_SUB_QUERY; rseBoolNode = FB_NEW_POOL(csb->csb_pool) RseBoolNode(csb->csb_pool, blr_any); - rseBoolNode->rse = newInnerRse; + rseBoolNode->rse = newInnerRse2; rseBoolNode->ownSavepoint = this->ownSavepoint; orNode->arg2 = rseBoolNode; @@ -2201,16 +2205,16 @@ BoolExprNode* RseBoolNode::convertNeqAllToNotAny(thread_db* tdbb, CompilerScratc outerRseNeq->blrOp = blr_eql; // If there was a boolean on the stream, append (AND) the new one - if (newInnerRse->rse_boolean) + if (newInnerRse2->rse_boolean) { andNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode(csb->csb_pool, blr_and); - andNode->arg1 = newInnerRse->rse_boolean; + andNode->arg1 = newInnerRse2->rse_boolean; andNode->arg2 = boolean; boolean = andNode; } - newInnerRse->rse_boolean = boolean; + newInnerRse2->rse_boolean = boolean; SubExprNodeCopier copier(csb->csb_pool, csb); return copier.copy(tdbb, static_cast(newNode)); From c20f37a6f187c1d1356ea6f268af9aa4e23a2721 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 10 Jan 2025 20:17:22 +0000 Subject: [PATCH 28/49] 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 b235c5a9c3..770837ddc7 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:571 + FORMAL BUILD NUMBER:573 */ -#define PRODUCT_VER_STRING "6.0.0.571" -#define FILE_VER_STRING "WI-T6.0.0.571" -#define LICENSE_VER_STRING "WI-T6.0.0.571" -#define FILE_VER_NUMBER 6, 0, 0, 571 +#define PRODUCT_VER_STRING "6.0.0.573" +#define FILE_VER_STRING "WI-T6.0.0.573" +#define LICENSE_VER_STRING "WI-T6.0.0.573" +#define FILE_VER_NUMBER 6, 0, 0, 573 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "571" +#define FB_BUILD_NO "573" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 4a2770d795..76696189ca 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=571 +BuildNum=573 NowAt=`pwd` cd `dirname $0` From d1b61224eec9aab1c630713d604e0a3eb5d6e823 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 12 Jan 2025 14:37:54 -0300 Subject: [PATCH 29/49] Changes post review to #8358 - thanks Artyom. --- src/isql/FrontendLexer.h | 2 +- src/isql/FrontendParser.cpp | 374 ++++++++++++++++++------------------ src/isql/FrontendParser.h | 3 +- 3 files changed, 190 insertions(+), 189 deletions(-) diff --git a/src/isql/FrontendLexer.h b/src/isql/FrontendLexer.h index bab64c8de3..d0e5df1382 100644 --- a/src/isql/FrontendLexer.h +++ b/src/isql/FrontendLexer.h @@ -46,7 +46,7 @@ public: std::string rawText; std::string processedText; - std::string getProcessedString() const + const std::string& getProcessedString() const { return type == FrontendLexer::Token::TYPE_STRING || type == FrontendLexer::Token::TYPE_META_STRING ? processedText : rawText; diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index d5d299fa2e..11d39bbce1 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -48,58 +48,111 @@ FrontendParser::AnyNode FrontendParser::internalParse() const auto commandToken = lexer.getToken(); - if (commandToken.type == Token::TYPE_OTHER) + if (commandToken.type != Token::TYPE_OTHER) + return InvalidNode(); + + const auto& command = commandToken.processedText; + + if (command == TOKEN_ADD) { - const auto& command = commandToken.processedText; - - if (command == TOKEN_ADD) + if (auto tableName = parseName()) { - if (const auto tableName = parseName()) - { - AddNode node; - node.tableName = std::move(tableName.value()); + AddNode node; + node.tableName = std::move(tableName.value()); - if (parseEof()) - return node; - } + if (parseEof()) + return node; } - else if (command == TOKEN_BLOBDUMP || command == TOKEN_BLOBVIEW) + } + else if (command == TOKEN_BLOBDUMP || command == TOKEN_BLOBVIEW) + { + if (const auto blobId = lexer.getToken(); blobId.type != Token::TYPE_EOF) { - if (const auto blobId = lexer.getToken(); blobId.type != Token::TYPE_EOF) + BlobDumpViewNode node; + + // Find the high and low values of the blob id + if (blobId.processedText.empty()) + return InvalidNode(); + + sscanf(blobId.processedText.c_str(), "%" xLONGFORMAT":%" xLONGFORMAT, + &node.blobId.gds_quad_high, &node.blobId.gds_quad_low); + + if (command == TOKEN_BLOBDUMP) { - BlobDumpViewNode node; - - // Find the high and low values of the blob id - if (blobId.processedText.empty()) - return InvalidNode(); - - sscanf(blobId.processedText.c_str(), "%" xLONGFORMAT":%" xLONGFORMAT, - &node.blobId.gds_quad_high, &node.blobId.gds_quad_low); - - if (command == TOKEN_BLOBDUMP) + if (auto file = parseFileName()) { - if (const auto file = parseFileName()) - { - node.file = std::move(file.value()); + node.file = std::move(file.value()); - if (parseEof()) - return node; - } - } - else - { if (parseEof()) return node; } } + else + { + if (parseEof()) + return node; + } } - else if (command == TOKEN_CONNECT) + } + else if (command == TOKEN_CONNECT) + { + ConnectNode node; + + do { - ConnectNode node; + auto token = lexer.getToken(); + + if (token.type == Token::TYPE_EOF) + { + if (node.args.empty()) + break; + else + return node; + } + else if (token.type != Token::TYPE_OTHER && + token.type != Token::TYPE_STRING && + token.type != Token::TYPE_META_STRING) + { + return InvalidNode(); + } + + node.args.push_back(std::move(token)); + } while(true); + } + else if (command == TOKEN_COPY) + { + CopyNode node; + + if (auto source = parseName()) + node.source = std::move(source.value()); + else + return InvalidNode(); + + if (auto destination = parseName()) + node.destination = std::move(destination.value()); + else + return InvalidNode(); + + if (auto database = parseFileName()) + node.database = std::move(database.value()); + else + return InvalidNode(); + + if (parseEof()) + return node; + } + else if (command == TOKEN_CREATE) + { + if (const auto createWhat = lexer.getToken(); + createWhat.type == Token::TYPE_OTHER && + (createWhat.processedText == "DATABASE" || + (options.schemaAsDatabase && createWhat.processedText == "SCHEMA"))) + { + CreateDatabaseNode node; do { - const auto token = lexer.getToken(); + auto token = lexer.getToken(); if (token.type == Token::TYPE_EOF) { @@ -118,148 +171,95 @@ FrontendParser::AnyNode FrontendParser::internalParse() node.args.push_back(std::move(token)); } while(true); } - else if (command == TOKEN_COPY) - { - CopyNode node; - - if (const auto source = parseName()) - node.source = std::move(source.value()); - else - return InvalidNode(); - - if (const auto destination = parseName()) - node.destination = std::move(destination.value()); - else - return InvalidNode(); - - if (const auto database = parseFileName()) - node.database = std::move(database.value()); - else - return InvalidNode(); - - if (parseEof()) - return node; - } - else if (command == TOKEN_CREATE) - { - if (const auto createWhat = lexer.getToken(); - createWhat.type == Token::TYPE_OTHER && - (createWhat.processedText == "DATABASE" || - (options.schemaAsDatabase && createWhat.processedText == "SCHEMA"))) - { - CreateDatabaseNode node; - - do - { - const auto token = lexer.getToken(); - - if (token.type == Token::TYPE_EOF) - { - if (node.args.empty()) - break; - else - return node; - } - else if (token.type != Token::TYPE_OTHER && - token.type != Token::TYPE_STRING && - token.type != Token::TYPE_META_STRING) - { - return InvalidNode(); - } - - node.args.push_back(std::move(token)); - } while(true); - } - } - else if (command == TOKEN_DROP) - { - if (const auto dropWhat = lexer.getToken(); - dropWhat.type == Token::TYPE_OTHER && - (dropWhat.processedText == "DATABASE" || - (options.schemaAsDatabase && dropWhat.processedText == "SCHEMA"))) - { - if (parseEof()) - return DropDatabaseNode(); - } - } - else if (command == TOKEN_EDIT) - { - EditNode node; - node.file = parseFileName(); - - if (parseEof()) - return node; - } - else if (command == TOKEN_EXIT) + } + else if (command == TOKEN_DROP) + { + if (const auto dropWhat = lexer.getToken(); + dropWhat.type == Token::TYPE_OTHER && + (dropWhat.processedText == "DATABASE" || + (options.schemaAsDatabase && dropWhat.processedText == "SCHEMA"))) { if (parseEof()) - return ExitNode(); + return DropDatabaseNode(); } - else if (command == TOKEN_EXPLAIN) - { - ExplainNode node; + } + else if (command == TOKEN_EDIT) + { + EditNode node; + node.file = parseFileName(); - if (const auto query = parseUtilEof()) - { - node.query = std::move(query.value()); - return node; - } - } - else if (command == TOKEN_HELP || command == "?") - { - HelpNode node; + if (parseEof()) + return node; + } + else if (command == TOKEN_EXIT) + { + if (parseEof()) + return ExitNode(); + } + else if (command == TOKEN_EXPLAIN) + { + ExplainNode node; - if (const auto token = lexer.getToken(); token.type == Token::TYPE_EOF) - return node; - else if (token.type == Token::TYPE_OTHER) - { - node.command = token.processedText; - - if (parseEof()) - return node; - } - } - else if (command.length() >= 2 && TOKEN_INPUT.find(command) == 0) + if (auto query = parseUtilEof()) { - if (const auto file = parseFileName()) - { - InputNode node; - node.file = std::move(file.value()); - - if (parseEof()) - return node; - } - } - else if (command.length() >= 3 && TOKEN_OUTPUT.find(command) == 0) - { - OutputNode node; - node.file = parseFileName(); - - if (parseEof()) - return node; - } - else if (command == TOKEN_QUIT) - { - if (parseEof()) - return QuitNode(); - } - else if (command == TOKEN_SET) - { - if (const auto setNode = parseSet(); !std::holds_alternative(setNode)) - return setNode; - } - else if (command == TOKEN_SHELL) - { - ShellNode node; - node.command = parseUtilEof(); + node.query = std::move(query.value()); return node; } - else if (command == TOKEN_SHOW) + } + else if (command == TOKEN_HELP || command == "?") + { + HelpNode node; + + if (const auto token = lexer.getToken(); token.type == Token::TYPE_EOF) + return node; + else if (token.type == Token::TYPE_OTHER) { - if (const auto showNode = parseShow(); !std::holds_alternative(showNode)) - return showNode; + node.command = token.processedText; + + if (parseEof()) + return node; } } + else if (command.length() >= 2 && TOKEN_INPUT.find(command) == 0) + { + if (auto file = parseFileName()) + { + InputNode node; + node.file = std::move(file.value()); + + if (parseEof()) + return node; + } + } + else if (command.length() >= 3 && TOKEN_OUTPUT.find(command) == 0) + { + OutputNode node; + node.file = parseFileName(); + + if (parseEof()) + return node; + } + else if (command == TOKEN_QUIT) + { + if (parseEof()) + return QuitNode(); + } + else if (command == TOKEN_SET) + { + if (const auto setNode = parseSet(); !std::holds_alternative(setNode)) + return setNode; + } + else if (command == TOKEN_SHELL) + { + ShellNode node; + node.command = parseUtilEof(); + return node; + } + else if (command == TOKEN_SHOW) + { + if (const auto showNode = parseShow(); !std::holds_alternative(showNode)) + return showNode; + } return InvalidNode(); } @@ -389,7 +389,7 @@ FrontendParser::AnySetNode FrontendParser::parseSet() return parsed.value(); } - else if (text.length() >= 5 && std::string(TOKEN_TRANSACTION).find(text) == 0) + else if (text.length() >= 5 && TOKEN_TRANSACTION.find(text) == 0) { SetTransactionNode node; node.statement = lexer.getBuffer(); @@ -434,7 +434,7 @@ std::optional FrontendParser::parseSet(std::string_v { if (setCommand == testCommand || (testCommandMinLen && setCommand.length() >= testCommandMinLen && - std::string(testCommand).find(setCommand) == 0)) + testCommand.find(setCommand) == 0)) { Node node; @@ -502,7 +502,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_COLLATIONS, 9)) return parsed.value(); - else if (text.length() >= 7 && std::string(TOKEN_COMMENTS).find(text) == 0) + else if (text.length() >= 7 && TOKEN_COMMENTS.find(text) == 0) { if (parseEof()) return ShowCommentsNode(); @@ -522,7 +522,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_FILTERS, 6)) return parsed.value(); - else if (text.length() >= 4 && std::string(TOKEN_FUNCTIONS).find(text) == 0) + else if (text.length() >= 4 && TOKEN_FUNCTIONS.find(text) == 0) { ShowFunctionsNode node; node.name = parseName(); @@ -558,7 +558,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_PACKAGES, 4)) return parsed.value(); - else if (text.length() >= 4 && std::string(TOKEN_PROCEDURES).find(text) == 0) + else if (text.length() >= 4 && TOKEN_PROCEDURES.find(text) == 0) { ShowProceduresNode node; node.name = parseName(); @@ -586,7 +586,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_ROLES, 4)) return parsed.value(); - else if (text.length() >= 6 && std::string(TOKEN_SECCLASSES).find(text) == 0) + else if (text.length() >= 6 && TOKEN_SECCLASSES.find(text) == 0) { const auto lexerPos = lexer.getPos(); const auto token = lexer.getNameToken(); @@ -622,7 +622,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() return ShowSqlDialectNode(); } } - else if (text.length() >= 3 && std::string(TOKEN_SYSTEM).find(text) == 0) + else if (text.length() >= 3 && TOKEN_SYSTEM.find(text) == 0) { ShowSystemNode node; @@ -630,22 +630,22 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() { const auto objectTypeText = std::string(objectType->c_str()); - if ((objectTypeText.length() >= 7 && std::string(TOKEN_COLLATES).find(objectTypeText) == 0) || - (objectTypeText.length() >= 9 && std::string(TOKEN_COLLATIONS).find(objectTypeText) == 0)) + if ((objectTypeText.length() >= 7 && TOKEN_COLLATES.find(objectTypeText) == 0) || + (objectTypeText.length() >= 9 && TOKEN_COLLATIONS.find(objectTypeText) == 0)) { node.objType = obj_collation; } - else if (objectTypeText.length() >= 4 && std::string(TOKEN_FUNCTIONS).find(objectTypeText) == 0) + else if (objectTypeText.length() >= 4 && TOKEN_FUNCTIONS.find(objectTypeText) == 0) node.objType = obj_udf; - else if (objectTypeText.length() >= 5 && std::string(TOKEN_TABLES).find(objectTypeText) == 0) + else if (objectTypeText.length() >= 5 && TOKEN_TABLES.find(objectTypeText) == 0) node.objType = obj_relation; - else if (objectTypeText.length() >= 4 && std::string(TOKEN_ROLES).find(objectTypeText) == 0) + else if (objectTypeText.length() >= 4 && TOKEN_ROLES.find(objectTypeText) == 0) node.objType = obj_sql_role; - else if (objectTypeText.length() >= 4 && std::string(TOKEN_PROCEDURES).find(objectTypeText) == 0) + else if (objectTypeText.length() >= 4 && TOKEN_PROCEDURES.find(objectTypeText) == 0) node.objType = obj_procedure; - else if (objectTypeText.length() >= 4 && std::string(TOKEN_PACKAGES).find(objectTypeText) == 0) + else if (objectTypeText.length() >= 4 && TOKEN_PACKAGES.find(objectTypeText) == 0) node.objType = obj_package_header; - else if (objectTypeText.length() >= 3 && std::string(TOKEN_PUBLICATIONS).find(objectTypeText) == 0) + else if (objectTypeText.length() >= 3 && TOKEN_PUBLICATIONS.find(objectTypeText) == 0) node.objType = obj_publication; else return InvalidNode(); @@ -672,7 +672,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() } else if (const auto parsed = parseShowOptName(text, TOKEN_VIEWS, 4)) return parsed.value(); - else if (text.length() >= 9 && std::string(TOKEN_WIRE_STATISTICS).find(text) == 0 || + else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 || text == TOKEN_WIRE_STATS) { if (parseEof()) @@ -692,7 +692,7 @@ std::optional FrontendParser::parseShowOptName(std: { if (showCommand == testCommand || (testCommandMinLen && showCommand.length() >= testCommandMinLen && - std::string(testCommand).find(showCommand) == 0)) + testCommand.find(showCommand) == 0)) { Node node; node.name = parseName(); @@ -708,8 +708,8 @@ std::optional FrontendParser::parseShowOptName(std: std::optional FrontendParser::parseUtilEof() { - const auto startPos = lexer.getPos(); - auto lastPos = startPos; + const auto startIt = lexer.getPos(); + auto lastPosIt = startIt; bool first = true; do @@ -721,10 +721,10 @@ std::optional FrontendParser::parseUtilEof() if (first) return std::nullopt; - return FrontendLexer::trim(std::string(startPos, lastPos)); + return FrontendLexer::trim(std::string(startIt, lastPosIt)); } - lastPos = lexer.getPos(); + lastPosIt = lexer.getPos(); first = false; } while (true); diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h index 940c94f5f7..aa52a0a6fd 100644 --- a/src/isql/FrontendParser.h +++ b/src/isql/FrontendParser.h @@ -210,13 +210,14 @@ public: template static inline constexpr bool AlwaysFalseV = false; -public: +private: FrontendParser(std::string_view statement, const Options& aOptions) : lexer(statement), options(aOptions) { } +public: FrontendParser(const FrontendParser&) = delete; FrontendParser& operator=(const FrontendParser&) = delete; From a5526e898d3ed8125d23941e65f91cbcc4b89a8f Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 12 Jan 2025 14:48:15 -0300 Subject: [PATCH 30/49] Change (now posix) tests log_level to error. --- builds/posix/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/posix/Makefile.in b/builds/posix/Makefile.in index d2871b3dca..e64fcc0f45 100644 --- a/builds/posix/Makefile.in +++ b/builds/posix/Makefile.in @@ -799,7 +799,7 @@ install install-embedded silent_install package packages dist: .PHONY: tests tests_process run_tests run_tests_process -log_level ?= all +log_level ?= error tests: $(MAKE) TARGET?=$(DefaultTarget) tests_process From 9cef81f47dd41cd57baa8eed298d9a2e49a18f83 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 12 Jan 2025 14:48:36 -0300 Subject: [PATCH 31/49] Fix warnings in test code. --- src/common/classes/tests/ArrayTest.cpp | 18 +++--- .../classes/tests/DoublyLinkedListTest.cpp | 6 +- src/common/tests/StringTest.cpp | 56 +++++++++---------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/common/classes/tests/ArrayTest.cpp b/src/common/classes/tests/ArrayTest.cpp index 031366f33a..bed187bc46 100644 --- a/src/common/classes/tests/ArrayTest.cpp +++ b/src/common/classes/tests/ArrayTest.cpp @@ -14,7 +14,7 @@ BOOST_AUTO_TEST_CASE(ConstructionWithStdInitializerTest) { Array array(*getDefaultMemoryPool(), {1, 2, 3, 4}); - BOOST_TEST(array.getCount() == 4); + BOOST_TEST(array.getCount() == 4u); BOOST_TEST(array[0] == 1); BOOST_TEST(array[3] == 4); } @@ -23,22 +23,22 @@ BOOST_AUTO_TEST_CASE(ClearTest) { Array array(*getDefaultMemoryPool(), {1, 2, 3, 4}); - BOOST_TEST(array.getCount() == 4); + BOOST_TEST(array.getCount() == 4u); array.clear(); - BOOST_TEST(array.getCount() == 0); + BOOST_TEST(array.getCount() == 0u); } BOOST_AUTO_TEST_CASE(IsEmptyAndHasDataTest) { Array array(*getDefaultMemoryPool(), {1, 2, 3, 4}); - BOOST_TEST(array.getCount() > 0); + BOOST_TEST(array.getCount() > 0u); BOOST_TEST(!array.isEmpty()); BOOST_TEST(array.hasData()); array.clear(); - BOOST_TEST(array.getCount() == 0); + BOOST_TEST(array.getCount() == 0u); BOOST_TEST(array.isEmpty()); BOOST_TEST(!array.hasData()); } @@ -46,12 +46,12 @@ BOOST_AUTO_TEST_CASE(IsEmptyAndHasDataTest) BOOST_AUTO_TEST_CASE(CapacityAndCountTest) { Array array1(10); - BOOST_TEST(array1.getCapacity() == 10); - BOOST_TEST(array1.getCount() == 0); + BOOST_TEST(array1.getCapacity() == 10u); + BOOST_TEST(array1.getCount() == 0u); Array array2(*getDefaultMemoryPool(), 11); - BOOST_TEST(array2.getCapacity() == 11); - BOOST_TEST(array2.getCount() == 0); + BOOST_TEST(array2.getCapacity() == 11u); + BOOST_TEST(array2.getCount() == 0u); } BOOST_AUTO_TEST_SUITE_END() // ArrayTests diff --git a/src/common/classes/tests/DoublyLinkedListTest.cpp b/src/common/classes/tests/DoublyLinkedListTest.cpp index 75f473103a..18a047d3a8 100644 --- a/src/common/classes/tests/DoublyLinkedListTest.cpp +++ b/src/common/classes/tests/DoublyLinkedListTest.cpp @@ -17,7 +17,7 @@ BOOST_AUTO_TEST_CASE(ConstructionWithStdInitializerTest) { DoublyLinkedList list(*getDefaultMemoryPool(), {1, 2, 3, 4}); - BOOST_TEST(list.getCount() == 4); + BOOST_TEST(list.getCount() == 4u); BOOST_TEST(list.front() == 1); BOOST_TEST(list.back() == 4); } @@ -26,10 +26,10 @@ BOOST_AUTO_TEST_CASE(ClearTest) { DoublyLinkedList list(*getDefaultMemoryPool(), {1, 2, 3, 4}); - BOOST_TEST(list.getCount() == 4); + BOOST_TEST(list.getCount() == 4u); list.clear(); - BOOST_TEST(list.getCount() == 0); + BOOST_TEST(list.getCount() == 0u); } BOOST_AUTO_TEST_CASE(SpliceTest) diff --git a/src/common/tests/StringTest.cpp b/src/common/tests/StringTest.cpp index 08c7815f73..7f0b07ddd4 100644 --- a/src/common/tests/StringTest.cpp +++ b/src/common/tests/StringTest.cpp @@ -418,47 +418,47 @@ BOOST_AUTO_TEST_CASE(FindTest) // 9 string b = "345"; - check(a.find(b), 3); - check(a.find("45"), 4); - check(a.find('5'), 5); + check(a.find(b), 3u); + check(a.find("45"), 4u); + check(a.find('5'), 5u); check(a.find("ZZ"), string::npos); - check(a.rfind(b), 9); - check(a.rfind("45"), 10); - check(a.rfind('5'), 11); + check(a.rfind(b), 9u); + check(a.rfind("45"), 10u); + check(a.rfind('5'), 11u); check(a.rfind("ZZ"), string::npos); - check(a.find("45", 8), 10); + check(a.find("45", 8), 10u); - check(a.find_first_of("aub"), 6); - check(a.find_first_of(b), 3); - check(a.find_first_of("54"), 4); - check(a.find_first_of('5'), 5); + check(a.find_first_of("aub"), 6u); + check(a.find_first_of(b), 3u); + check(a.find_first_of("54"), 4u); + check(a.find_first_of('5'), 5u); check(a.find_first_of("ZZ"), string::npos); - check(a.find_last_of("aub"), 8); - check(a.find_last_of(b), 11); - check(a.find_last_of("54"), 11); - check(a.find_last_of('5'), 11); + check(a.find_last_of("aub"), 8u); + check(a.find_last_of(b), 11u); + check(a.find_last_of("54"), 11u); + check(a.find_last_of('5'), 11u); check(a.find_last_of("ZZ"), string::npos); - check(a.find_first_of("45", 8), 10); + check(a.find_first_of("45", 8u), 10u); b = "010"; - check(a.find_first_not_of("aub"), 0); - check(a.find_first_not_of(b), 2); - check(a.find_first_not_of("0102"), 3); - check(a.find_first_not_of('0'), 1); + check(a.find_first_not_of("aub"), 0u); + check(a.find_first_not_of(b), 2u); + check(a.find_first_not_of("0102"), 3u); + check(a.find_first_not_of('0'), 1u); check(a.find_first_not_of(a), string::npos); b = "878"; - check(a.find_last_not_of("aub"), 14); - check(a.find_last_not_of(b), 12); - check(a.find_last_not_of("78"), 12); - check(a.find_last_not_of('8'), 13); + check(a.find_last_not_of("aub"), 14u); + check(a.find_last_not_of(b), 12u); + check(a.find_last_not_of("78"), 12u); + check(a.find_last_not_of('8'), 13u); check(a.find_last_not_of(a), string::npos); - check(a.find_first_not_of("u345", 8), 12); + check(a.find_first_not_of("u345", 8u), 12u); } BOOST_AUTO_TEST_CASE(SubstrTest) @@ -466,13 +466,13 @@ BOOST_AUTO_TEST_CASE(SubstrTest) string a = lbl; string b; - b = a.substr(3, 4); + b = a.substr(3u, 4u); validate(b, "3456"); - b = a.substr(5, 20); + b = a.substr(5u, 20u); validate(b, "56789"); - b = a.substr(50, 20); + b = a.substr(50u, 20u); validate(b, ""); } From 058983cca9176cbd5f82b894cf65c7bb8146b838 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 12 Jan 2025 20:15:47 +0000 Subject: [PATCH 32/49] 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 770837ddc7..2d65ef1385 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:573 + FORMAL BUILD NUMBER:576 */ -#define PRODUCT_VER_STRING "6.0.0.573" -#define FILE_VER_STRING "WI-T6.0.0.573" -#define LICENSE_VER_STRING "WI-T6.0.0.573" -#define FILE_VER_NUMBER 6, 0, 0, 573 +#define PRODUCT_VER_STRING "6.0.0.576" +#define FILE_VER_STRING "WI-T6.0.0.576" +#define LICENSE_VER_STRING "WI-T6.0.0.576" +#define FILE_VER_NUMBER 6, 0, 0, 576 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "573" +#define FB_BUILD_NO "576" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 76696189ca..babe632159 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=573 +BuildNum=576 NowAt=`pwd` cd `dirname $0` From c709689758761b19f80b8a959e50de86a4302ec5 Mon Sep 17 00:00:00 2001 From: Xeonacid Date: Sat, 11 Jan 2025 21:44:01 +0800 Subject: [PATCH 33/49] Add riscv64 define for cds --- extern/libcds/cds/compiler/gcc/compiler_macro.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extern/libcds/cds/compiler/gcc/compiler_macro.h b/extern/libcds/cds/compiler/gcc/compiler_macro.h index c36c6f9c05..a1a42e66a7 100644 --- a/extern/libcds/cds/compiler/gcc/compiler_macro.h +++ b/extern/libcds/cds/compiler/gcc/compiler_macro.h @@ -132,6 +132,11 @@ # define CDS_PROCESSOR__NAME "LOONGARCH" # define CDS_PROCESSOR__NICK "loongarch" # define CDS_BUILD_BITS 64 +#elif defined(__riscv) && __riscv_xlen == 64 +# define CDS_PROCESSOR_ARCH CDS_PROCESSOR_RISCV64 +# define CDS_PROCESSOR__NAME "RISC-V64" +# define CDS_PROCESSOR__NICK "riscv64" +# define CDS_BUILD_BITS 64 #else # if defined(CDS_USE_LIBCDS_ATOMIC) # error "Libcds does not support atomic implementation for the processor architecture. Try to use C++11-compatible compiler and remove CDS_USE_LIBCDS_ATOMIC flag from compiler command line" From 7e293a3edd86511ba6087cc47dbbb1c0f3522354 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 13 Jan 2025 20:16:34 +0000 Subject: [PATCH 34/49] 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 2d65ef1385..91d2bcb102 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:576 + FORMAL BUILD NUMBER:577 */ -#define PRODUCT_VER_STRING "6.0.0.576" -#define FILE_VER_STRING "WI-T6.0.0.576" -#define LICENSE_VER_STRING "WI-T6.0.0.576" -#define FILE_VER_NUMBER 6, 0, 0, 576 +#define PRODUCT_VER_STRING "6.0.0.577" +#define FILE_VER_STRING "WI-T6.0.0.577" +#define LICENSE_VER_STRING "WI-T6.0.0.577" +#define FILE_VER_NUMBER 6, 0, 0, 577 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "576" +#define FB_BUILD_NO "577" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index babe632159..de3eb9f546 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=576 +BuildNum=577 NowAt=`pwd` cd `dirname $0` From 655c7eb54119106fbc756bd8627e24e8f632b254 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Mon, 13 Jan 2025 22:02:14 -0300 Subject: [PATCH 35/49] Fix case problem in username - thanks Pavel Zotov. --- src/isql/isql.epp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 640765d2aa..80c4d9c34d 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -4959,7 +4959,7 @@ static processing_state frontend(const std::string& statement) } else if (clause == "USER") { - usr = node.args[i + 1].processedText.c_str(); + usr = node.args[i + 1].rawText.c_str(); i += 2; } else if (clause == "PASSWORD") From 29873bc71272a6aab24cc61c819494b7fd0273ea Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Thu, 9 Jan 2025 14:39:19 +0300 Subject: [PATCH 36/49] Do not add fakely used (ignored but unmatched) conjuncts to the inversion match list. This should fix #8379: Incorrect cardinality estimation for retrievals with multiple compound indices having common set of fields. --- src/jrd/optimizer/Retrieval.cpp | 50 +++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 22e3889306..c1923928c1 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -1473,21 +1473,21 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) invCandidate->nonFullMatchedSegments = 0; invCandidate->matchedSegments = currentInv->matchedSegments; invCandidate->dependencies = currentInv->dependencies; - matches.clear(); for (const auto currentMatch : currentInv->matches) { - if (!matches.exist(currentMatch)) - matches.add(currentMatch); + if (!invCandidate->matches.exist(currentMatch)) + invCandidate->matches.add(currentMatch); } - if (currentInv->boolean) + if (const auto currentMatch = currentInv->boolean) { - if (!matches.exist(currentInv->boolean)) - matches.add(currentInv->boolean); + if (!invCandidate->matches.exist(currentMatch)) + invCandidate->matches.add(currentMatch); } - invCandidate->matches.join(matches); + matches.assign(invCandidate->matches); + if (customPlan) continue; @@ -1661,16 +1661,19 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) invCandidate->dependencies = bestCandidate->dependencies; invCandidate->condition = bestCandidate->condition; - 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 (!invCandidate->matches.exist(bestMatch)) + invCandidate->matches.add(bestMatch); } - if (bestCandidate->boolean) + + if (const auto bestMatch = bestCandidate->boolean) { - if (!matches.exist(bestCandidate->boolean)) - matches.add(bestCandidate->boolean); + if (!invCandidate->matches.exist(bestMatch)) + invCandidate->matches.add(bestMatch); } + + matches.join(invCandidate->matches); } else if (!bestCandidate->condition) { @@ -1697,15 +1700,17 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) for (const auto bestMatch : bestCandidate->matches) { - if (!matches.exist(bestMatch)) - matches.add(bestMatch); + if (!invCandidate->matches.exist(bestMatch)) + invCandidate->matches.add(bestMatch); } - if (bestCandidate->boolean) + if (const auto bestMatch = bestCandidate->boolean) { - if (!matches.exist(bestCandidate->boolean)) - matches.add(bestCandidate->boolean); + if (!invCandidate->matches.exist(bestMatch)) + invCandidate->matches.add(bestMatch); } + + matches.join(invCandidate->matches); } if (invCandidate->unique) @@ -1739,10 +1744,13 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) invCandidate->cost += navigationCandidate->cost; ++invCandidate->indexes; invCandidate->navigated = true; - } - if (invCandidate) - invCandidate->matches.join(matches); + for (const auto navMatch : navigationCandidate->matches) + { + if (!invCandidate->matches.exist(navMatch)) + invCandidate->matches.add(navMatch); + } + } return invCandidate; } From 5167c588a418d0f8f6627c752cf20812a30f2d25 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Thu, 9 Jan 2025 14:59:05 +0300 Subject: [PATCH 37/49] Revert "Better (methinks) inversion choice when the OR condition includes a node which is also matched directly" This reverts commit ebcd4dde644fbf886dcf191b61eeadf72bec74dd. --- src/jrd/optimizer/Retrieval.cpp | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index c1923928c1..e909182359 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -1496,35 +1496,19 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) // Look if a match is already used by previous matches. bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false; - if (currentInv->boolean) + for (const auto currentMatch : currentInv->matches) { - if (matches.exist(currentInv->boolean)) + if (matches.exist(currentMatch)) { anyMatchAlreadyUsed = true; if (navigationCandidate && - navigationCandidate->matches.exist(currentInv->boolean)) + navigationCandidate->matches.exist(currentMatch)) { matchUsedByNavigation = true; } - } - } - else - { - for (const auto currentMatch : currentInv->matches) - { - if (matches.exist(currentMatch)) - { - anyMatchAlreadyUsed = true; - if (navigationCandidate && - navigationCandidate->matches.exist(currentMatch)) - { - matchUsedByNavigation = true; - } - - break; - } + break; } } From 91dce62553a0c74fea4c033110f0091f8e128f55 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 13 Jan 2025 21:46:22 +0300 Subject: [PATCH 38/49] Rework fix for #5751 (Available indices are not used in some cases if ORDER BY expression is a filtered one) and also attempt to fix the special case for OR conditions partially matched to an index --- src/jrd/optimizer/Retrieval.cpp | 90 +++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index e909182359..5ecacf746e 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -1494,45 +1494,69 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) return invCandidate; } - // Look if a match is already used by previous matches. - bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false; - for (const auto currentMatch : currentInv->matches) + if (!customPlan) { - if (matches.exist(currentMatch)) - { - anyMatchAlreadyUsed = true; + // Look if a match is already used by previous matches + bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false; - if (navigationCandidate && - navigationCandidate->matches.exist(currentMatch)) - { - matchUsedByNavigation = true; - } - - break; - } - } - - if (currentInv->boolean && matches.exist(currentInv->boolean)) - anyMatchAlreadyUsed = true; - - if (anyMatchAlreadyUsed && !customPlan) - { - currentInv->used = true; - - if (matchUsedByNavigation) - continue; - - // If a match on this index was already used by another - // index, add also the other matches from this index. for (const auto currentMatch : currentInv->matches) { - if (!matches.exist(currentMatch)) - matches.add(currentMatch); + if (matches.exist(currentMatch)) + { + anyMatchAlreadyUsed = true; + + if (navigationCandidate && + navigationCandidate->matches.exist(currentMatch)) + { + matchUsedByNavigation = true; + } + + break; + } } - // Restart loop, because other indexes could also be excluded now. - restartLoop = true; - break; + if (const auto currentMatch = currentInv->boolean) + { + if (matches.exist(currentMatch)) + { + anyMatchAlreadyUsed = true; + + if (navigationCandidate && + navigationCandidate->matches.exist(currentMatch)) + { + matchUsedByNavigation = true; + } + } + else if (matchUsedByNavigation) + anyMatchAlreadyUsed = false; + } + + // If some match was already used by another index, skip this index + + if (anyMatchAlreadyUsed) + { + if (!matchUsedByNavigation) + { + // Add the other matches from this index + + for (const auto currentMatch : currentInv->matches) + { + if (!matches.exist(currentMatch)) + matches.add(currentMatch); + } + + if (const auto currentMatch = currentInv->boolean) + { + if (!matches.exist(currentMatch)) + matches.add(currentMatch); + } + } + + // Restart loop, because other indexes could also be excluded now + currentInv->used = true; + restartLoop = true; + break; + } } if (!bestCandidate) From 21e9ed016ee079a5a0b28fcf8f3c1b2a829afeff Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 14 Jan 2025 20:16:30 +0000 Subject: [PATCH 39/49] 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 91d2bcb102..dc37b26482 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:577 + FORMAL BUILD NUMBER:581 */ -#define PRODUCT_VER_STRING "6.0.0.577" -#define FILE_VER_STRING "WI-T6.0.0.577" -#define LICENSE_VER_STRING "WI-T6.0.0.577" -#define FILE_VER_NUMBER 6, 0, 0, 577 +#define PRODUCT_VER_STRING "6.0.0.581" +#define FILE_VER_STRING "WI-T6.0.0.581" +#define LICENSE_VER_STRING "WI-T6.0.0.581" +#define FILE_VER_NUMBER 6, 0, 0, 581 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "577" +#define FB_BUILD_NO "581" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index de3eb9f546..d387a85430 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=577 +BuildNum=581 NowAt=`pwd` cd `dirname $0` From e4c56776c00f36d1282ee5fe2ce3f235167e49d2 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 14 Jan 2025 21:12:56 -0300 Subject: [PATCH 40/49] Fix problem with failed CONNECT and SET BAIL - thanks Pavel Zotov. --- src/isql/isql.epp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 80c4d9c34d..486aa6fa63 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -4993,7 +4993,7 @@ static processing_state frontend(const std::string& statement) true); } - return SKIP; + return ret; }, [](const FrontendParser::CopyNode& node) From fe178a14042c911a15062be544781d6e0f5fd502 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 15 Jan 2025 10:08:42 +0300 Subject: [PATCH 41/49] Frontport the reworked implementation of the full outer join --- src/jrd/optimizer/Optimizer.h | 2 +- src/jrd/optimizer/OuterJoin.cpp | 38 +++++++++++++++++++------------ src/jrd/recsrc/FullOuterJoin.cpp | 29 ++++++++++++++++++++--- src/jrd/recsrc/NestedLoopJoin.cpp | 7 +++--- src/jrd/recsrc/RecordSource.h | 6 +++-- 5 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index fc12bd4dd5..c9c5c20f11 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -922,7 +922,7 @@ public: RecordSource* generate(); private: - RecordSource* process(const JoinType joinType); + RecordSource* process(StreamList* outerStreams = nullptr); thread_db* const tdbb; Optimizer* const optimizer; diff --git a/src/jrd/optimizer/OuterJoin.cpp b/src/jrd/optimizer/OuterJoin.cpp index 343ac1605f..876b7209eb 100644 --- a/src/jrd/optimizer/OuterJoin.cpp +++ b/src/jrd/optimizer/OuterJoin.cpp @@ -94,15 +94,25 @@ OuterJoin::OuterJoin(thread_db* aTdbb, Optimizer* opt, RecordSource* OuterJoin::generate() { - const auto outerJoinRsb = process(OUTER_JOIN); - if (!optimizer->isFullJoin()) - return outerJoinRsb; + { + fb_assert(optimizer->isLeftJoin()); + return process(); + } - // A FULL JOIN B is currently implemented similar to (A LEFT JOIN B) UNION ALL (B ANTI-JOIN A). + StreamList outerStreams; + const auto outerJoinRsb = process(&outerStreams); + + // A FULL JOIN B is currently implemented similar to: + // + // (A LEFT JOIN B) + // UNION ALL + // (B LEFT JOIN A WHERE A.* IS NULL) + // + // See also FullOuterJoin class implementation. // // At this point we already have the first part -- (A LEFT JOIN B) -- ready, - // so just swap the sides and make an anti-join. + // so just swap the sides and make the second (inverted) join. auto& outerStream = joinStreams[0]; auto& innerStream = joinStreams[1]; @@ -131,15 +141,15 @@ RecordSource* OuterJoin::generate() iter.reset(CMP_clone_node_opt(tdbb, csb, iter)); } - const auto antiJoinRsb = process(ANTI_JOIN); + const auto antiJoinRsb = process(); // Allocate and return the final join record source - return FB_NEW_POOL(getPool()) FullOuterJoin(csb, outerJoinRsb, antiJoinRsb); + return FB_NEW_POOL(getPool()) FullOuterJoin(csb, outerJoinRsb, antiJoinRsb, outerStreams); } -RecordSource* OuterJoin::process(const JoinType joinType) +RecordSource* OuterJoin::process(StreamList* outerStreams) { BoolExprNode* boolean = nullptr; @@ -153,8 +163,7 @@ RecordSource* OuterJoin::process(const JoinType joinType) { fb_assert(!outerStream.rsb); outerStream.rsb = optimizer->generateRetrieval(outerStream.number, - optimizer->isFullJoin() ? nullptr : sortPtr, - true, false, &boolean); + optimizer->isFullJoin() ? nullptr : sortPtr, true, false, &boolean); } else { @@ -173,13 +182,15 @@ RecordSource* OuterJoin::process(const JoinType joinType) boolean = optimizer->composeBoolean(); } + if (outerStreams) + outerStream.rsb->findUsedStreams(*outerStreams); + if (innerStream.number != INVALID_STREAM) { fb_assert(!innerStream.rsb); // AB: the sort clause for the inner stream of an OUTER JOIN // should never be used for the index retrieval - innerStream.rsb = optimizer->generateRetrieval(innerStream.number, nullptr, - false, (joinType == OUTER_JOIN) ? true : false); + innerStream.rsb = optimizer->generateRetrieval(innerStream.number, nullptr, false, true); } // Generate a parent filter record source for any remaining booleans that @@ -189,8 +200,7 @@ RecordSource* OuterJoin::process(const JoinType joinType) // Allocate and return the join record source - return FB_NEW_POOL(getPool()) - NestedLoopJoin(csb, outerStream.rsb, innerRsb, boolean, joinType); + return FB_NEW_POOL(getPool()) NestedLoopJoin(csb, outerStream.rsb, innerRsb, boolean); }; diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index 1578b1100b..d570fba14c 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -37,10 +37,13 @@ using namespace Jrd; // Data access: full outer join // ---------------------------- -FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2) +FullOuterJoin::FullOuterJoin(CompilerScratch* csb, + RecordSource* arg1, RecordSource* arg2, + const StreamList& checkStreams) : RecordSource(csb), m_arg1(arg1), - m_arg2(arg2) + m_arg2(arg2), + m_checkStreams(csb->csb_pool, checkStreams) { fb_assert(m_arg1 && m_arg2); @@ -97,7 +100,27 @@ bool FullOuterJoin::internalGetRecord(thread_db* tdbb) const m_arg2->open(tdbb); } - return m_arg2->getRecord(tdbb); + // We should exclude matching records from the right-joined (second) record source, + // as they're already returned from the left-joined (first) record source + + while (m_arg2->getRecord(tdbb)) + { + bool matched = false; + + for (const auto stream : m_checkStreams) + { + if (request->req_rpb[stream].rpb_number.isValid()) + { + matched = true; + break; + } + } + + if (!matched) + return true; + } + + return false; } bool FullOuterJoin::refetchRecord(thread_db* /*tdbb*/) const diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index 2f89112807..9380d6a1d0 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -53,10 +53,11 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSour } } -NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner, - BoolExprNode* boolean, JoinType joinType) +NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, + RecordSource* outer, RecordSource* inner, + BoolExprNode* boolean) : RecordSource(csb), - m_joinType(joinType), + m_joinType(OUTER_JOIN), m_args(csb->csb_pool), m_boolean(boolean) { diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index ee65daf6f1..1fb0fc2974 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -1137,7 +1137,7 @@ namespace Jrd public: NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSource* const* args); NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner, - BoolExprNode* boolean, JoinType joinType); + BoolExprNode* boolean); void close(thread_db* tdbb) const override; @@ -1168,7 +1168,8 @@ namespace Jrd class FullOuterJoin : public RecordSource { public: - FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2); + FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2, + const StreamList& checkStreams); void close(thread_db* tdbb) const override; @@ -1191,6 +1192,7 @@ namespace Jrd private: NestConst m_arg1; NestConst m_arg2; + const StreamList m_checkStreams; }; class HashJoin : public RecordSource From 69690fd44e2227a4580e2c0bf1a71971d84a5128 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Wed, 15 Jan 2025 16:53:09 +0300 Subject: [PATCH 42/49] Fix a crash appeared after the bugfix for #8185 (SIGSEGV with WHERE CURRENT OF statement with statement cache turned on). Reproducible with QA test bugs.core_5231 (release build only). This fix is a very simple one, just to avoid the object state being read after its possible removal. The returned usage counter is not used anyway. --- src/dsql/DsqlStatements.cpp | 8 ++------ src/dsql/DsqlStatements.h | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp index 47117b26a6..c72f38fd95 100644 --- a/src/dsql/DsqlStatements.cpp +++ b/src/dsql/DsqlStatements.cpp @@ -56,17 +56,15 @@ void DsqlStatement::rethrowDdlException(status_exception& ex, bool metadataUpdat status_exception::raise(newVector); } -int DsqlStatement::release() +void DsqlStatement::release() { fb_assert(refCounter.value() > 0); - int refCnt = --refCounter; - if (!refCnt) + if (!--refCounter) { if (cacheKey) { dsqlAttachment->dbb_statement_cache->statementGoingInactive(cacheKey); - refCnt = refCounter; } else { @@ -74,8 +72,6 @@ int DsqlStatement::release() dsqlAttachment->deletePool(&getPool()); } } - - return refCnt; } void DsqlStatement::doRelease() diff --git a/src/dsql/DsqlStatements.h b/src/dsql/DsqlStatements.h index 1ab48fc986..399852bf63 100644 --- a/src/dsql/DsqlStatements.h +++ b/src/dsql/DsqlStatements.h @@ -83,12 +83,12 @@ protected: virtual ~DsqlStatement() = default; public: - int addRef() + void addRef() { - return ++refCounter; + ++refCounter; } - int release(); + void release(); bool isCursorBased() const { From a0aa2921000b55e9fa740e3d9a959ce2eeddaf68 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 15 Jan 2025 20:16:18 +0000 Subject: [PATCH 43/49] 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 dc37b26482..751c0518c4 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:581 + FORMAL BUILD NUMBER:584 */ -#define PRODUCT_VER_STRING "6.0.0.581" -#define FILE_VER_STRING "WI-T6.0.0.581" -#define LICENSE_VER_STRING "WI-T6.0.0.581" -#define FILE_VER_NUMBER 6, 0, 0, 581 +#define PRODUCT_VER_STRING "6.0.0.584" +#define FILE_VER_STRING "WI-T6.0.0.584" +#define LICENSE_VER_STRING "WI-T6.0.0.584" +#define FILE_VER_NUMBER 6, 0, 0, 584 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "581" +#define FB_BUILD_NO "584" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index d387a85430..a86bb56353 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=581 +BuildNum=584 NowAt=`pwd` cd `dirname $0` From 03b615b8e124d5e61ac93a572eb766fe323ec541 Mon Sep 17 00:00:00 2001 From: MochalovAlexey Date: Thu, 16 Jan 2025 17:35:50 +0300 Subject: [PATCH 44/49] remove redundant plugin reload on super server, update it only on classic after stateLock ast (#8393) Co-authored-by: aleksey.mochalov --- src/jrd/CryptoManager.cpp | 66 ++++++++++----------------------------- src/jrd/CryptoManager.h | 1 + 2 files changed, 18 insertions(+), 49 deletions(-) diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index e5c1e1bffc..b3e1727330 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -367,7 +367,7 @@ namespace Jrd { return; fb_assert(tdbb); - lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT); + lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT | CRYPT_RELOAD_PLUGIN); } void CryptoManager::lockAndReadHeader(thread_db* tdbb, unsigned flags) @@ -407,9 +407,15 @@ namespace Jrd { crypt = hdr->hdr_flags & Ods::hdr_encrypted; process = hdr->hdr_flags & Ods::hdr_crypt_process; + if (flags & CRYPT_RELOAD_PLUGIN && cryptPlugin) + { + PluginManagerInterfacePtr()->releasePlugin(cryptPlugin); + cryptPlugin = NULL; + } + // tdbb w/o attachment comes when database is shutting down in the end of detachDatabase() // the only needed here page is header, i.e. we can live w/o cryptPlugin - if ((crypt || process) && tdbb->getAttachment()) + if ((crypt || process) && !cryptPlugin && tdbb->getAttachment()) { ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); hdr.getClumplets(hc); @@ -418,56 +424,18 @@ namespace Jrd { else keyName = ""; - if (!cryptPlugin) + loadPlugin(tdbb, hdr->hdr_crypt_plugin); + pluginName = hdr->hdr_crypt_plugin; + string valid; + calcValidation(valid, cryptPlugin); + if (hc.find(Ods::HDR_crypt_hash)) { - loadPlugin(tdbb, hdr->hdr_crypt_plugin); - pluginName = hdr->hdr_crypt_plugin; - string valid; - calcValidation(valid, cryptPlugin); - if (hc.find(Ods::HDR_crypt_hash)) - { - hc.getString(hash); - if (hash != valid) - (Arg::Gds(isc_bad_crypt_key) << keyName).raise(); - } - else - hash = valid; + hc.getString(hash); + if (hash != valid) + (Arg::Gds(isc_bad_crypt_key) << keyName).raise(); } else - { - for (GetPlugins keyControl(IPluginManager::TYPE_KEY_HOLDER, dbb.dbb_config); - keyControl.hasData(); keyControl.next()) - { - // check does keyHolder want to provide a key for us - IKeyHolderPlugin* keyHolder = keyControl.plugin(); - - FbLocalStatus st; - int keyCallbackRc = keyHolder->keyCallback(&st, tdbb->getAttachment()->att_crypt_callback); - st.check(); - if (!keyCallbackRc) - continue; - - // validate a key - AutoPlugin crypt(checkFactory->makeInstance()); - setDbInfo(crypt); - crypt->setKey(&st, 1, &keyHolder, keyName.c_str()); - - - string valid; - calcValidation(valid, crypt); - if (hc.find(Ods::HDR_crypt_hash)) - { - hc.getString(hash); - if (hash == valid) - { - // unload old plugin and set new one - PluginManagerInterfacePtr()->releasePlugin(cryptPlugin); - cryptPlugin = NULL; - cryptPlugin = crypt.release(); - } - } - } - } + hash = valid; } if (cryptPlugin && (flags & CRYPT_HDR_INIT)) diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index c75f928c67..8037e086d9 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -364,6 +364,7 @@ private: void lockAndReadHeader(thread_db* tdbb, unsigned flags = 0); static const unsigned CRYPT_HDR_INIT = 0x01; static const unsigned CRYPT_HDR_NOWAIT = 0x02; + static const unsigned CRYPT_RELOAD_PLUGIN = 0x04; void addClumplet(Firebird::string& value, Firebird::ClumpletReader& block, UCHAR tag); void calcDigitalSignature(thread_db* tdbb, Firebird::string& signature, const class Header& hdr); From 2d6bb146e722ac78e6b2730986d9b50063a2ea94 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 16 Jan 2025 20:16:17 +0000 Subject: [PATCH 45/49] 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 751c0518c4..1f37ea9946 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:584 + FORMAL BUILD NUMBER:585 */ -#define PRODUCT_VER_STRING "6.0.0.584" -#define FILE_VER_STRING "WI-T6.0.0.584" -#define LICENSE_VER_STRING "WI-T6.0.0.584" -#define FILE_VER_NUMBER 6, 0, 0, 584 +#define PRODUCT_VER_STRING "6.0.0.585" +#define FILE_VER_STRING "WI-T6.0.0.585" +#define LICENSE_VER_STRING "WI-T6.0.0.585" +#define FILE_VER_NUMBER 6, 0, 0, 585 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "584" +#define FB_BUILD_NO "585" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index a86bb56353..2582c320b2 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=584 +BuildNum=585 NowAt=`pwd` cd `dirname $0` From 3b2489c5e006d6c1cd6cd6f16c026baaf3831b26 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Sat, 18 Jan 2025 18:01:24 +0200 Subject: [PATCH 46/49] Fixed bug #8386 : Crash when creating index on table that uses UDR and ParallelWorkers > 1 --- src/jrd/WorkerAttachment.cpp | 25 ++++++++++++++++++------- src/jrd/WorkerAttachment.h | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/jrd/WorkerAttachment.cpp b/src/jrd/WorkerAttachment.cpp index 8c9a4df8a3..65798c4277 100644 --- a/src/jrd/WorkerAttachment.cpp +++ b/src/jrd/WorkerAttachment.cpp @@ -76,12 +76,12 @@ WorkerStableAttachment::~WorkerStableAttachment() fini(); } -WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, Jrd::Database* dbb) +WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, Database* dbb, JProvider* provider) { Attachment* attachment = NULL; try { - attachment = Attachment::create(dbb, NULL); + attachment = Attachment::create(dbb, provider); attachment->att_filename = dbb->dbb_filename; attachment->att_flags |= ATT_worker; @@ -120,6 +120,8 @@ void WorkerStableAttachment::fini() BackgroundContextHolder tdbb(dbb, attachment, &status_vector, FB_FUNCTION); Monitoring::cleanupAttachment(tdbb); + dbb->dbb_extManager->closeAttachment(tdbb, attachment); + attachment->releaseLocks(tdbb); LCK_fini(tdbb, LCK_OWNER_attachment); @@ -433,18 +435,17 @@ StableAttachmentPart* WorkerAttachment::doAttach(FbStatusVector* status, Databas { StableAttachmentPart* sAtt = NULL; + AutoPlugin jInstance(JProvider::getInstance()); + //jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback); + if (Config::getServerMode() == MODE_SUPER) - sAtt = WorkerStableAttachment::create(status, dbb); + sAtt = WorkerStableAttachment::create(status, dbb, jInstance); else { ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); dpb.insertString(isc_dpb_trusted_auth, DBA_USER_NAME); dpb.insertInt(isc_dpb_worker_attach, 1); - AutoPlugin jInstance(JProvider::getInstance()); - - //jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback); - JAttachment* jAtt = jInstance->attachDatabase(status, dbb->dbb_filename.c_str(), dpb.getBufferLength(), dpb.getBuffer()); @@ -456,6 +457,7 @@ StableAttachmentPart* WorkerAttachment::doAttach(FbStatusVector* status, Databas { sAtt->addRef(); // !! sAtt->getHandle()->setIdleTimeout(WORKER_IDLE_TIMEOUT); + jInstance->addRef(); } return sAtt; @@ -465,6 +467,15 @@ void WorkerAttachment::doDetach(FbStatusVector* status, StableAttachmentPart* sA { status->init(); + AutoPlugin provider; + { + AttSyncLockGuard guard(*sAtt->getSync(), FB_FUNCTION); + + Attachment* attachment = sAtt->getHandle(); + if (attachment) + provider.reset(attachment->getProvider()); + } + // if (att->att_flags & ATT_system) if (Config::getServerMode() == MODE_SUPER) { diff --git a/src/jrd/WorkerAttachment.h b/src/jrd/WorkerAttachment.h index 742f120810..dd366573f1 100644 --- a/src/jrd/WorkerAttachment.h +++ b/src/jrd/WorkerAttachment.h @@ -46,7 +46,7 @@ namespace Jrd class WorkerStableAttachment : public SysStableAttachment { public: - static WorkerStableAttachment* create(FbStatusVector* status, Jrd::Database* dbb); + static WorkerStableAttachment* create(FbStatusVector* status, Database* dbb, JProvider* provider); void fini(); From 6f17277a958128bfda18723efaecd0e9320a02b1 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 17 Jan 2025 11:24:12 +0200 Subject: [PATCH 47/49] Fixed bug #8390 : Deadlock might happens when database is shutting down with internal worker attachments exists. --- src/jrd/WorkerAttachment.cpp | 57 ++++++++++++++++++++++++++++-------- src/jrd/WorkerAttachment.h | 28 +++++++++++++++--- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/jrd/WorkerAttachment.cpp b/src/jrd/WorkerAttachment.cpp index 65798c4277..969f54a38e 100644 --- a/src/jrd/WorkerAttachment.cpp +++ b/src/jrd/WorkerAttachment.cpp @@ -49,8 +49,10 @@ const unsigned WORKER_IDLE_TIMEOUT = 60; // 1 minute /// class WorkerStableAttachment -WorkerStableAttachment::WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* attachment) : - SysStableAttachment(attachment) +WorkerStableAttachment::WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* attachment, + WorkerAttachment* workers) : + SysStableAttachment(attachment), + m_workers(workers) { UserId user; user.setUserName(""); @@ -69,6 +71,7 @@ WorkerStableAttachment::WorkerStableAttachment(FbStatusVector* status, Jrd::Atta Monitoring::publishAttachment(tdbb); initDone(); + m_workers->incWorkers(); } WorkerStableAttachment::~WorkerStableAttachment() @@ -76,7 +79,8 @@ WorkerStableAttachment::~WorkerStableAttachment() fini(); } -WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, Database* dbb, JProvider* provider) +WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, Database* dbb, + JProvider* provider, WorkerAttachment* workers) { Attachment* attachment = NULL; try @@ -85,7 +89,7 @@ WorkerStableAttachment* WorkerStableAttachment::create(FbStatusVector* status, D attachment->att_filename = dbb->dbb_filename; attachment->att_flags |= ATT_worker; - WorkerStableAttachment* sAtt = FB_NEW WorkerStableAttachment(status, attachment); + WorkerStableAttachment* sAtt = FB_NEW WorkerStableAttachment(status, attachment, workers); return sAtt; } catch (const Exception& ex) @@ -129,6 +133,8 @@ void WorkerStableAttachment::fini() } destroy(attachment); + + m_workers->decWorkers(); } /// class WorkerAttachment @@ -139,8 +145,7 @@ bool WorkerAttachment::m_shutdown = false; WorkerAttachment::WorkerAttachment() : m_idleAtts(*getDefaultMemoryPool()), - m_activeAtts(*getDefaultMemoryPool()), - m_cntUserAtts(0) + m_activeAtts(*getDefaultMemoryPool()) { } @@ -177,6 +182,30 @@ void WorkerAttachment::decUserAtts(const PathName& dbname) } } +void WorkerAttachment::incWorkers() +{ + fb_assert(Config::getServerMode() == MODE_SUPER); + + MutexLockGuard guard(m_mutex, FB_FUNCTION); + ++m_cntWorkers; +} + +void WorkerAttachment::decWorkers() +{ + fb_assert(Config::getServerMode() == MODE_SUPER); + + MutexLockGuard guard(m_mutex, FB_FUNCTION); + if (--m_cntWorkers == 0) + m_noWorkers.notifyAll(); +} + +void WorkerAttachment::waitForWorkers() +{ + MutexLockGuard guard(m_mutex, FB_FUNCTION); + while (m_cntWorkers != 0) + m_noWorkers.wait(m_mutex); +} + WorkerAttachment* WorkerAttachment::getByName(const PathName& dbname) { if (m_shutdown) @@ -230,13 +259,17 @@ void WorkerAttachment::shutdownDbb(Database* dbb) if (Config::getServerMode() != MODE_SUPER) return; - MutexLockGuard guard(m_mapMutex, FB_FUNCTION); - WorkerAttachment* item = NULL; - if (!m_map->get(dbb->dbb_filename, item)) - return; + + { + MutexLockGuard guard(m_mapMutex, FB_FUNCTION); + + if (!m_map->get(dbb->dbb_filename, item)) + return; + } item->clear(false); + item->waitForWorkers(); } StableAttachmentPart* WorkerAttachment::getAttachment(FbStatusVector* status, Database* dbb) @@ -301,7 +334,7 @@ StableAttachmentPart* WorkerAttachment::getAttachment(FbStatusVector* status, Da MutexUnlockGuard unlock(item->m_mutex, FB_FUNCTION); status->init(); - sAtt = doAttach(status, dbb); + sAtt = item->doAttach(status, dbb); if (!sAtt) { // log error ? @@ -439,7 +472,7 @@ StableAttachmentPart* WorkerAttachment::doAttach(FbStatusVector* status, Databas //jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback); if (Config::getServerMode() == MODE_SUPER) - sAtt = WorkerStableAttachment::create(status, dbb, jInstance); + sAtt = WorkerStableAttachment::create(status, dbb, jInstance, this); else { ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); diff --git a/src/jrd/WorkerAttachment.h b/src/jrd/WorkerAttachment.h index dd366573f1..3111cd322b 100644 --- a/src/jrd/WorkerAttachment.h +++ b/src/jrd/WorkerAttachment.h @@ -31,6 +31,7 @@ #include "firebird.h" #include "../common/classes/alloc.h" #include "../common/classes/array.h" +#include "../common/classes/condition.h" #include "../common/classes/fb_string.h" #include "../common/classes/GenericMap.h" #include "../common/classes/init.h" @@ -43,10 +44,13 @@ namespace Jrd { +class WorkerAttachment; + class WorkerStableAttachment : public SysStableAttachment { public: - static WorkerStableAttachment* create(FbStatusVector* status, Database* dbb, JProvider* provider); + static WorkerStableAttachment* create(FbStatusVector* status, Database* dbb, JProvider* provider, + WorkerAttachment* workers); void fini(); @@ -54,8 +58,10 @@ protected: virtual void doOnIdleTimer(Firebird::TimerImpl* timer); private: - explicit WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* att); + explicit WorkerStableAttachment(FbStatusVector* status, Jrd::Attachment* att, WorkerAttachment* workers); virtual ~WorkerStableAttachment(); + + WorkerAttachment* m_workers; }; @@ -77,6 +83,8 @@ private: class WorkerAttachment { + friend class WorkerStableAttachment; + public: explicit WorkerAttachment(); @@ -93,10 +101,14 @@ public: private: static WorkerAttachment* getByName(const Firebird::PathName& dbname); - static Jrd::StableAttachmentPart* doAttach(FbStatusVector* status, Jrd::Database* dbb); + Jrd::StableAttachmentPart* doAttach(FbStatusVector* status, Jrd::Database* dbb); static void doDetach(FbStatusVector* status, Jrd::StableAttachmentPart* sAtt); void clear(bool checkRefs); + void incWorkers(); + void decWorkers(); + void waitForWorkers(); + typedef Firebird::GenericMap > > MapDbIdToWorkAtts; @@ -109,7 +121,15 @@ private: Firebird::HalfStaticArray m_idleAtts; Firebird::SortedArray > m_activeAtts; - int m_cntUserAtts; + + // count of regular user attachments, used with non-shared Database + int m_cntUserAtts = 0; + + // count of internal worker attachments, used with shared Database + int m_cntWorkers = 0; + + // used to wait for "no internal workers" condition + Firebird::Condition m_noWorkers; }; } // namespace Jrd From 63e6ead37b5daf67025ed9ee7675c2157c221fa6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 18 Jan 2025 20:14:52 +0000 Subject: [PATCH 48/49] 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 1f37ea9946..3281ebd671 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:585 + FORMAL BUILD NUMBER:587 */ -#define PRODUCT_VER_STRING "6.0.0.585" -#define FILE_VER_STRING "WI-T6.0.0.585" -#define LICENSE_VER_STRING "WI-T6.0.0.585" -#define FILE_VER_NUMBER 6, 0, 0, 585 +#define PRODUCT_VER_STRING "6.0.0.587" +#define FILE_VER_STRING "WI-T6.0.0.587" +#define LICENSE_VER_STRING "WI-T6.0.0.587" +#define FILE_VER_NUMBER 6, 0, 0, 587 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "585" +#define FB_BUILD_NO "587" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 2582c320b2..5848c647e1 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=585 +BuildNum=587 NowAt=`pwd` cd `dirname $0` From 90401f721018e320db12ee7c5e4bdc7b28ef62a3 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Sun, 19 Jan 2025 15:06:26 +0300 Subject: [PATCH 49/49] Postfix for #7804: The partial index is not involved when filtering conditions through OR. Use the full index scan instead of multiple range scans ORed afterwards. --- src/jrd/optimizer/Retrieval.cpp | 92 ++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 5ecacf746e..ef6b11c6da 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -1414,17 +1414,37 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) for (auto inversion : inversions) { - 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->index->idx_runtime_flags & idx_plan_dont_use)) || - (!customPlan && inversion == navigationCandidate)) + if (const auto indexScratch = inversion->scratch) { - inversion->used = true; + const auto idx = indexScratch->index; + + // 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 (((idx->idx_runtime_flags & idx_plan_dont_use)) || + (!customPlan && inversion == navigationCandidate)) + { + inversion->used = true; + } + + // If the index is conditional and its condition is also present in + // some other inversion as a boolean (it represents the OR operation), + // fake these other inversions as used, so that the full index scan would + // be preferred to multiple range scans. The cost-based algorithm below + // cannot handle it currently. + + if (idx->idx_flags & idx_condition) + { + for (auto otherInversion : inversions) + { + if (otherInversion->boolean && + idx->idx_condition->sameAs(otherInversion->boolean, true)) + { + otherInversion->used = true; + } + } + } } } @@ -1460,11 +1480,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) if (!invCandidate) invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); - if (!currentInv->inversion && currentInv->scratch) - invCandidate->inversion = makeIndexScanNode(currentInv->scratch); - else - invCandidate->inversion = currentInv->inversion; - + const auto inversionNode = (!currentInv->inversion && currentInv->scratch) ? + makeIndexScanNode(currentInv->scratch) : currentInv->inversion; + invCandidate->inversion = inversionNode; invCandidate->dbkeyRanges.assign(currentInv->dbkeyRanges); invCandidate->unique = currentInv->unique; invCandidate->selectivity = currentInv->selectivity; @@ -1653,12 +1671,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) if (!invCandidate) { invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); - if (!bestCandidate->inversion && bestCandidate->scratch) { - invCandidate->inversion = makeIndexScanNode(bestCandidate->scratch); - } - else { - invCandidate->inversion = bestCandidate->inversion; - } + const auto inversionNode = (!bestCandidate->inversion && bestCandidate->scratch) ? + makeIndexScanNode(bestCandidate->scratch) : bestCandidate->inversion; + invCandidate->inversion = inversionNode; invCandidate->dbkeyRanges.assign(bestCandidate->dbkeyRanges); invCandidate->unique = bestCandidate->unique; invCandidate->selectivity = bestCandidate->selectivity; @@ -1685,17 +1700,10 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) } else if (!bestCandidate->condition) { - if (!bestCandidate->inversion && bestCandidate->scratch) - { - invCandidate->inversion = composeInversion(invCandidate->inversion, - makeIndexScanNode(bestCandidate->scratch), InversionNode::TYPE_AND); - } - else - { - invCandidate->inversion = composeInversion(invCandidate->inversion, - bestCandidate->inversion, InversionNode::TYPE_AND); - } - + const auto inversionNode = (!bestCandidate->inversion && bestCandidate->scratch) ? + makeIndexScanNode(bestCandidate->scratch) : bestCandidate->inversion; + invCandidate->inversion = composeInversion(invCandidate->inversion, + inversionNode, InversionNode::TYPE_AND); invCandidate->dbkeyRanges.join(bestCandidate->dbkeyRanges); invCandidate->unique = (invCandidate->unique || bestCandidate->unique); invCandidate->selectivity = totalSelectivity; @@ -1770,6 +1778,16 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch, if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) return false; + const auto idx = indexScratch->index; + + if (idx->idx_flags & idx_condition) + { + // If index condition matches the boolean, this should not be + // considered a match. Full index scan will be used instead. + if (idx->idx_condition->sameAs(boolean, true)) + return false; + } + const auto cmpNode = nodeAs(boolean); const auto missingNode = nodeAs(boolean); const auto listNode = nodeAs(boolean); @@ -1804,16 +1822,6 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch, ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ? cmpNode->arg3 : nullptr; - const auto idx = indexScratch->index; - - if (idx->idx_flags & idx_condition) - { - // If index condition matches the boolean, this should not be - // considered a match. Full index scan will be used instead. - if (idx->idx_condition->sameAs(boolean, true)) - return false; - } - if (idx->idx_flags & idx_expression) { // see if one side or the other is matchable to the index expression