diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2235bc29d0..051219b643 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,13 +7,12 @@ jobs: runs-on: ${{ matrix.os }} container: ${{ matrix.container }} env: - VS_VERSION: ${{ matrix.os == 'windows-2016' && '2017' || (matrix.os == 'windows-2019' && '2019' || (matrix.os == 'windows-2022' && '2022' || '')) }} + VS_VERSION: ${{ (matrix.os == 'windows-2019' && '2019' || (matrix.os == 'windows-2022' && '2022' || '')) }} strategy: fail-fast: false matrix: os: - - windows-2016 - windows-2019 platform: [x64, x86] include: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f336efd61..6bb9033ea0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,6 @@ endif() set(FB_PREFIX ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}) set(FB_IPC_NAME "FirebirdIPI") set(FB_LOGFILENAME "firebird.log") -set(FB_PIPE_NAME "interbas") set(FB_SERVICE_NAME "gds_db") set(FB_SERVICE_PORT 3050) diff --git a/builds/cmake/SourceGroups.cmake b/builds/cmake/SourceGroups.cmake index 58529882e6..b3efa9ec3f 100644 --- a/builds/cmake/SourceGroups.cmake +++ b/builds/cmake/SourceGroups.cmake @@ -42,6 +42,8 @@ source_group("ISQL files\\${EPP_TXT}" "${SSRC}/isql/${EPP}") source_group("ISQL files\\${GEN_TXT}" "${BSRC}/isql/${GEN}") source_group("JRD files" "${SSRC}/jrd/${CPP}") source_group("JRD files\\Data Access" "${SSRC}/jrd/recsrc/${CPP}") +source_group("JRD files\\Optimizer" "${SSRC}/jrd/optimizer/${CPP}") +source_group("JRD files\\Replication" "${SSRC}/jrd/replication/${CPP}") source_group("JRD files\\EXTDS" "${SSRC}/jrd/extds/${CPP}") source_group("JRD files\\${EPP_TXT}" "${SSRC}/jrd/${EPP}") source_group("JRD files\\${GEN_TXT}" "${BSRC}/jrd/${GEN}") diff --git a/builds/install/arch-specific/win32/FirebirdInstall.iss b/builds/install/arch-specific/win32/FirebirdInstall.iss index 31a5b21954..a3ec4272c3 100644 --- a/builds/install/arch-specific/win32/FirebirdInstall.iss +++ b/builds/install/arch-specific/win32/FirebirdInstall.iss @@ -384,7 +384,7 @@ Filename: msiexec.exe; Parameters: "/qn /norestart /i ""{tmp}\vccrt{#msvc_runtim #endif ;Only register Firebird if we are installing AND configuring -Filename: {app}\instreg.exe; Parameters: "install "; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Components: ClientComponent; Flags: runminimized; Check: ConfigureFirebird; +Filename: {app}\instreg.exe; Parameters: "install "; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Components: ServerComponent; Flags: runminimized; Check: ConfigureFirebird; Filename: {app}\instclient.exe; Parameters: "install fbclient"; StatusMsg: {cm:instclientCopyFbClient}; MinVersion: {#MinVer}; Components: ClientComponent; Flags: runminimized; Check: CopyFBClientLib; Filename: {app}\instclient.exe; Parameters: "install gds32"; StatusMsg: {cm:instclientGenGds32}; MinVersion: {#MinVer}; Components: ClientComponent; Flags: runminimized; Check: CopyGds32 @@ -410,7 +410,7 @@ Filename: "{#MyAppURL}/afterinstall"; Description: "After installation - What Ne Root: HKLM; Subkey: SOFTWARE\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: Firebird; ValueData: ; Flags: uninsdeletevalue; Tasks: UseApplicationTask; Check: ConfigureFirebird; ;This doesn't seem to get cleared automatically by instreg on uninstall, so lets make sure of it -Root: HKLM; Subkey: "SOFTWARE\Firebird Project"; Flags: uninsdeletekeyifempty; Components: ClientComponent DevAdminComponent ServerComponent +Root: HKLM; Subkey: "SOFTWARE\Firebird Project"; Flags: uninsdeletekeyifempty; Components: ServerComponent ;Clean up Invalid registry entries from previous installs. Root: HKLM; Subkey: "SOFTWARE\FirebirdSQL"; ValueType: none; Flags: deletekey; @@ -476,7 +476,7 @@ Source: {#FilesDir}\firebird.exe; DestDir: {app}; Components: ServerComponent; F Source: {#FilesDir}\fb_lock_print.exe; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion Source: {#FilesDir}\ib_util.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion Source: {#FilesDir}\instclient.exe; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\instreg.exe; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\instreg.exe; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion Source: {#FilesDir}\instsvc.exe; DestDir: {app}; Components: ServerComponent; MinVersion: {#MinVer}; Flags: sharedfile ignoreversion Source: {#FilesDir}\isql.exe; DestDir: {app}; Components: DevAdminComponent; Flags: ignoreversion Source: {#FilesDir}\nbackup.exe; DestDir: {app}; Components: DevAdminComponent; Flags: ignoreversion @@ -487,14 +487,25 @@ Source: {#FilesDir}\fbclient.dll; DestDir: {app}; Components: ClientComponent; F Source: {#WOW64Dir}\fbclient.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: overwritereadonly sharedfile promptifolder {#SkipFileIfDevStatus} Source: {#WOW64Dir}\instclient.exe; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion {#SkipFileIfDevStatus} #endif -Source: {#FilesDir}\icuuc??.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\icuin??.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\icudt??.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion -Source: {#FilesDir}\icudt*.dat; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icuuc??.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icuin??.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icudt??.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#FilesDir}\icudt*.dat; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +#if PlatformTarget == "x64" +Source: {#WOW64Dir}\icuuc??.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#WOW64Dir}\icuin??.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#WOW64Dir}\icudt??.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +Source: {#WOW64Dir}\icudt*.dat; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +#endif + #if PlatformTarget =="Win32" Source: {#FilesDir}\fbrmclib.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion #endif -Source: {#FilesDir}\zlib1.dll; DestDir: {app}; Components: ServerComponent; Flags: sharedfile ignoreversion + +Source: {#FilesDir}\zlib1.dll; DestDir: {app}; Components: ClientComponent; Flags: sharedfile ignoreversion +#if PlatformTarget == "x64" +Source: {#WOW64Dir}\zlib1.dll; DestDir: {app}\WOW64; Components: ClientComponent; Flags: sharedfile ignoreversion +#endif ;Rules for installation of MS runtimes are simplified with MSVC10 ;We just install the runtimes into the install dir. @@ -542,9 +553,13 @@ Source: {#WOW64Dir}\lib\*.lib; DestDir: {app}\WOW64\lib; Components: DevAdminCom ;Source: {#FilesDir}\UDF\*.txt; DestDir: {app}\UDF; Components: ServerComponent; Flags: ignoreversion; Source: {#FilesDir}\plugins.conf; DestDir: {app}; Components: ServerComponent; Flags: ignoreversion; -Source: {#FilesDir}\plugins\*.dll; DestDir: {app}\plugins; Components: ServerComponent; Flags: ignoreversion; +Source: {#FilesDir}\plugins\*.dll; DestDir: {app}\plugins; Components: ServerComponent; Flags: ignoreversion; Check: IsServerInstall; +Source: {#FilesDir}\plugins\chacha.dll; DestDir: {app}\plugins; Components: ClientComponent; Flags: ignoreversion; Check: IsNotServerInstall; Source: {#FilesDir}\plugins\*.conf; DestDir: {app}\plugins; Components: ServerComponent; Flags: ignoreversion; Source: {#FilesDir}\plugins\udr\*.*; DestDir: {app}\plugins\udr; Components: ServerComponent; Flags: ignoreversion; +#if PlatformTarget == "x64" +Source: {#WOW64Dir}\plugins\chacha*.dll; DestDir: {app}\WOW64\plugins; Components: ClientComponent; Flags: ignoreversion; +#endif Source: {#FilesDir}\misc\*.*; DestDir: {app}\misc; Components: ServerComponent; Flags: ignoreversion createallsubdirs recursesubdirs ; @@ -575,7 +590,7 @@ Filename: {app}\instclient.exe; Parameters: " remove fbclient"; StatusMsg: {cm:i Filename: {app}\wow64\instclient.exe; Parameters: " remove gds32"; StatusMsg: {cm:instclientDecLibCountGds32}; MinVersion: {#MinVer}; Flags: runminimized 32bit; RunOnceId: RemoveGDS32x86 Filename: {app}\wow64\instclient.exe; Parameters: " remove fbclient"; StatusMsg: {cm:instclientDecLibCountFbClient}; MinVersion: {#MinVer}; Flags: runminimized 32bit; RunOnceId: RemoveFbClientx86 #endif -Filename: {app}\instreg.exe; Parameters: " remove"; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Flags: runminimized; RunOnceId: RemoveRegistryEntry +Filename: {app}\instreg.exe; Parameters: " remove"; StatusMsg: {cm:instreg}; MinVersion: {#MinVer}; Components: ServerComponent; Flags: runminimized; RunOnceId: RemoveRegistryEntry [UninstallDelete] Type: files; Name: "{app}\*.lck" diff --git a/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc b/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc index d4a74a59ed..c4081472fd 100644 --- a/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc +++ b/builds/install/arch-specific/win32/FirebirdInstallEnvironmentChecks.inc @@ -890,9 +890,40 @@ begin end; +function IsServerInstall: Boolean; +var + SetupType: String; +begin + + // DOC NOTE - WizardSetupType is not well documented. If parameter is set to + // True the Description of the setup type is returned. (This is useless for us + // as our descriptions are I18n'ised. ) If set False the string declared in + // the TYPES section is returned. BUT LOWERCASED! Aargh!! + // To protect against future changes each side is the comparison is lowercased. + + SetupType := WizardSetupType ( false ); + if LowerCase( SetupType ) = LowerCase( 'ServerInstall' ) then + Result := true + else + Result := False; +end; + + +function IsNotServerInstall: Boolean; +begin + if IsServerInstall then + Result := False + else + Result := True; +end; + + function ConfigureFirebird: boolean; begin - result := (InstallAndConfigure AND Configure) = Configure; + if IsNotServerInstall then + Result := False + else + Result := (InstallAndConfigure AND Configure) = Configure; end; diff --git a/builds/install/arch-specific/win32/test_installer/fbit-help.txt b/builds/install/arch-specific/win32/test_installer/fbit-help.txt index 67919143c3..7ec59d98e8 100644 --- a/builds/install/arch-specific/win32/test_installer/fbit-help.txt +++ b/builds/install/arch-specific/win32/test_installer/fbit-help.txt @@ -1,5 +1,3 @@ - - Firebird Binary Installer Test Harness HELP (Designed with TABS=4 and console width=120) fbit {PARAM [...]] @@ -11,15 +9,22 @@ will clean up from previous broken installs. CLEAN must the first parameter and subsequent parameters are ignored. Note: - It will run silently. - By default fbit installs Firebird according to the parameters passed and then - immediately uninstalls it. A copy of the install is made, along with the install and uninstall logs. + immediately uninstalls it. A copy of the install is made, along with the install and uninstall logs. + + REQUIREMENTS + ============ + The script will attempt to detect if the current cmd session has just built firebird. + If not you must set FBINST_EXEC in the environment prior to running this script. + + Be sure to check :SET_GLOBAL_ENV for hard-coded settings + Some knowledge of InnoSetup will be useful. See %FIREBIRD%/doc/installation_scripted.txt for more info. FBIT Specific Parameters ======================== - Param Name Value Passed Comment - ---------- ------------ ------- + Param Name Value Passed to fbit Comment + ---------- -------------------- ------- HELP - Displays this screen DRYRUN - Show what will be done. No changes are made NOARCHIVE - Disables copying of install dir to %USERPROFILE%\fbit @@ -28,61 +33,74 @@ SCRIPTED - Sets VERYSILENT, SP and NOMSG TESTNAME NameOfTestRun Optional. No spaces allowed. Used for storing test run details. - The following parameters are set by default. They are unset automatically when a conflicting parameter is passed. - Default Param Value Unset by - ------------- ------------- ---------- + Default Param Default Value set by fbit Unset by + ------------- ------------------------- ---------- INTERACTIVE True SCRIPTED INSTALLTYPE ServerInstall CLIENT or DEVINST SERVICE_TASK True APPTASK SUPERSERVER True CLASSICSERVER or SUPERCLASSIC - Firebird Installer specific Parameters ====================================== - Param Name Value passed Action when set + Param Name Value passed to installer Action when set ---------- ------------------------- --------------- COPYGDSLIB CopyFbClientAsGds32Task Copy fbclient to and rename to gds32 FORCE FORCE Force installation NOAUTOSTART NULL Does not set AutoStartTask - NOCOPYFBLIB CopyFbClientToSysTask Does not copy fbclient to + NOCOPYFBLIB - Does not copy fbclient to PASSWORD /SYSDBAPASSWORD=%ISC_PASSWORD% Changes SYSDBA password from masterkey See :SET_GLOBAL_ENV Installation Tasks ================== - Param Name Value passed Comment - ------------- ------------ --------------- + Param Name Value passed to /TASKS Comment + ------------- ---------------------- --------------- APPTASK UseApplicationTask Will not install as a service CLASSICSERVER UseClassicServerTask Will configure classic server SUPERCLASSIC UseSuperClassicTask Will configure super classic - Installation Types ================== - Param Name Value passed Comment - ------------ ------------ ------- + Param Name Value passed to /TYPE Comment + ------------ --------------------- ------- CLIENT ClientInstall Minimal working client install DEVINST DeveloperInstall Everything but the server. - + SERVER_INSTALL ServerInstall Uninstallation ============== - Param Name Value passed Comment - -------------- ------------ ------- + Param Name Value passed to uninstaller Comment + -------------- --------------------------- ------- CLEAN CLEAN Completely remove the firebird install Reset list of shared dll's in the registry - + Assumes installed version of Firebird matches %FIREBIRD_BASE_VER% set in fbit script. Generic InnoSetup parameters ============================ - Param Name Value passed Comment - ---------- ------------ ------- + Param Name Value passed to installer Comment + ---------- ------------------------- ------- NOMSG SUPPRESSMSGBOXES Suppress message boxes NOCANCEL NOCANCEL Prevents user cancelling install SILENT SILENT SP SP- Disables the This will install... prompt VERYSILENT VERYSILENT + + Examples + ======== + +o Run a scripted server install: + + fbit SCRIPTED + +o Clean up previous firebird install: + + fbit CLEAN + +o Test install of firebird client: + + fbit SCRIPTED CLIENT + -------------------------- End of Fbit Help Screen ---------------------------------------- diff --git a/builds/install/arch-specific/win32/test_installer/fbit.bat b/builds/install/arch-specific/win32/test_installer/fbit.bat index 92878a5a8e..f217d68792 100644 --- a/builds/install/arch-specific/win32/test_installer/fbit.bat +++ b/builds/install/arch-specific/win32/test_installer/fbit.bat @@ -18,7 +18,7 @@ :: scripted install of Firebird. It is designed to test almost all possible :: scriptable combinations and is thus far more complicated than a typical :: install script need be. However, it can be used for testing. Note that chosen -:: settings :: used for each test run are saved into an .inf file, along with a +:: settings used for each test run are saved into an .inf file, along with a :: log of the install run. @goto :MAIN %* @@ -27,26 +27,32 @@ ::======================================================= :SET_GLOBAL_ENV @call :SET_VERBOSE_IF_DEBUG_ON +::@call :SET_VERBOSE @if defined DEBUG @echo Entering %0 +:: Uncomment this if the default command prompt takes up too much space +::@PROMPT=fbit_prompt$G -:: FBINST_EXEC must point to the package we want to test... -if not defined FBINST_EXEC ( - rem - if we have just built firebird we can test the install immediately - if defined FBBUILD_FILE_ID ( - if defined FBBUILD_FILENAME_SUFFIX ( - @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%%FBBUILD_FILENAME_SUFFIX%.exe - ) else ( - @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%.exe - ) - ) +:: Set this to the location of the firebird installer you want to test. +::@set FBINST_EXEC=%USERPROFILE%\Desktop\Firebird-5.0.0.0000_x64.exe + +:: if we have just built firebird we can test the install immediately +if defined FBBUILD_FILE_ID ( + if defined FBBUILD_FILENAME_SUFFIX ( + @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%%FBBUILD_FILENAME_SUFFIX%.exe + ) else ( + @set FBINST_EXEC=%FBBUILD_INSTALL_IMAGES%\Firebird-%FBBUILD_FILE_ID%.exe + ) ) else ( +rem FBINST_EXEC must point to the package we want to test... + if not defined FBINST_EXEC ( rem Set the actual path and filename here - or set it in the environment before running fbit. - @set FBINST_EXEC=%USERPROFILE%\Desktop\Firebird-4.0.0.2311_0_x64_RC1.exe + set FBINST_EXEC=%USERPROFILE%\Desktop\Firebird-5.0.0.0000_0_x64_RC1.exe + ) ) :: This should be set dynamically, perhaps. But for now it is hard-coded. -@set FIREBIRD_BASE_VER=Firebird_4_0 +@set FIREBIRD_BASE_VER=Firebird_5_0 :: It is possible that successive installs into the same directory may :: generate different uninstallers but for now we hard code the default. @@ -73,14 +79,16 @@ if not defined FBINST_EXEC ( :: to read. @set SHOW_FINAL_CMD= -:: change as reqd, or comment out if ISC_PASSWORD is already set in your env -@set ISC_PASSWORD="secret" +:: change as reqd +@if not defined ISC_PASSWORD ( + @set ISC_PASSWORD="secret" +) @set TAB= & @if not defined DRYRUN ( - if not exist %FBINSTALLLOGDIR% @mkdir %FBINSTALLLOGDIR% >nul 2>nul - if not exist %FBINSTALLCOPYDIR% @mkdir %FBINSTALLCOPYDIR% >nul 2>nul + if not exist %FBINSTALLLOGDIR% ( @mkdir %FBINSTALLLOGDIR% >nul 2>nul ) + if not exist %FBINSTALLCOPYDIR% ( @mkdir %FBINSTALLCOPYDIR% >nul 2>nul ) ) @if defined DEBUG @echo Leaving %0 @@ -92,7 +100,8 @@ if not defined FBINST_EXEC ( ::======================================================= :GET_OPTS @call :SET_VERBOSE_IF_DEBUG_ON -if defined DEBUG @echo Entering %0 +::@call :SET_VERBOSE +@if defined DEBUG @echo Entering %0 :: Automatically parse the commandline and place all valid options into ENV VARS :: Courtesy of this link: @@ -111,22 +120,22 @@ if defined DEBUG @echo Entering %0 :: will end up as :: flagwithdefault=flag :: Basically all this means that these variables should not be passed to runtime: -:: INTERACTIVE INSTALL INSTALLTYPE SERVER SERVICE_TASK SUPERSERVER +:: INTERACTIVE INSTALL INSTALLTYPE SERVER_INSTALL SERVICE_TASK SUPERSERVER -set "options=APPTASK: CLASSICSERVER: CLEAN: CLIENT: CMD_PARAMS: COMPONENTS: COPYGDSLIB: DEVINST: DRYRUN: FINALCMD: FULL_CMD: FORCE: HELP: INTERACTIVE:1 INSTALL:1 INSTALLTYPE:ServerInstall NOARCHIVE: NOAUTOSTART: NOCANCEL: NOCOPYFBLIB: NOMSG: NOUNINSTALL: PASSWORD: RUN_TIMESTAMP: SCRIPTED: SERVER:1 SILENT: SP: SERVICE_TASK:1 SUPERCLASSIC: SUPERSERVER:1 TASK_LIST:UseSuperServerTask TESTNAME:"" UNINSTALL: VERYSILENT: XRESULT:0" -if defined VERBOSE @echo on +set "options=APPTASK: CLASSICSERVER: CLEAN: CLIENT: CMD_PARAMS: COMPONENTS: COPYGDSLIB: DEVINST: DRYRUN: FINALCMD: FULL_CMD: FORCE: HELP: INTERACTIVE:1 INSTALL:1 INSTALLTYPE:ServerInstall NOARCHIVE: NOAUTOSTART: NOCANCEL: NOCOPYFBLIB: NOMSG: NOUNINSTALL: PASSWORD: RUN_TIMESTAMP: SCRIPTED: SERVER_INSTALL:1 SILENT: SP: SERVICE_TASK: SUPERCLASSIC: SUPERSERVER: TASK_LIST: TESTNAME:"" UNINSTALL: VERYSILENT: XRESULT:0" +@if defined VERBOSE @echo on for %%O in (%options%) do ( for /f "tokens=1,* delims=:" %%A in ("%%O") do ( set "%%A=%%~B" ) ) -if defined VERBOSE ( - call :PRINT_VARS +@if defined VERBOSE ( + @call :PRINT_VARS if NOT defined DEBUG pause ) :loop -if not "%~1"=="" ( +@if not "%~1"=="" ( set "test=!options:*%~1:=! " if "!test!"=="!options! " ( echo Error: Invalid option %~1 @@ -144,7 +153,8 @@ if not "%~1"=="" ( goto :loop ) -if defined DEBUG @echo Leaving %0 +@if defined VERBOSE ( @call :PRINT_VARS ) +@if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF ::======================================================= @@ -198,46 +208,100 @@ rem We now have everything we need for uninstall so jump to the end goto :SET_CMD_PARAMS ) -:: Fix up any incompatible assignments +:: Fix up any incompatible assignments ::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -@if defined CLASSICSERVER ( set SUPERSERVER=& set SUPERCLASSIC=) -@if defined SUPERCLASSIC ( set SUPERSERVER=& set CLASSICSERVER=) -:: Theoretically this next line is redundant -@if defined SUPERSERVER ( set SUPERCLASSIC=& set CLASSICSERVER=) +@if defined CLIENT ( + set INSTALLTYPE=ClientInstall + set DEVINST= + set SERVER_INSTALL= + set TASK_LIST= + set SERVICE_TASK= + set CLASSICSERVER= + set SUPERCLASSIC= + set SUPERSERVER= + +) + +@if defined DEVINST ( + set INSTALLTYPE=DeveloperInstall + set CLIENT= + set SERVER_INSTALL= + set TASK_LIST= + set SERVICE_TASK= + set CLASSICSERVER= + set SUPERCLASSIC= + set SUPERSERVER= +) -@if defined CLIENT ( set INSTALLTYPE=ClientInstall & set DEVINST=& set SERVER=) -@if defined DEVINST (set INSTALLTYPE=DeveloperInstall & set SERVER=) :: Theoretically this next line is redundant -@if defined SERVER (set INSTALLTYPE=ServerInstall ) +@if defined SERVER_INSTALL ( + + set INSTALLTYPE=ServerInstall + set CLIENT= + set DEVINST= + + @if defined CLASSICSERVER ( + set SUPERSERVER= + set SUPERCLASSIC= + set TASK_LIST=UseClassicServerTask + ) else ( + @if defined SUPERCLASSIC ( + set SUPERSERVER= + set CLASSICSERVER= + set TASK_LIST=UseSuperClassicTask + ) else ( + rem @if defined SUPERSERVER ( + set SUPERCLASSIC= + set CLASSICSERVER= + set SUPERSERVER=1 + set TASK_LIST=UseSuperServerTask + ) + ) +) +::@call :SET_VERBOSE +@call :PRINT_VARS In %0 - End of Fixup +:::::::::: End Fix Up incompatible assignments ::::::::::::::::::::::::::::::::: :: Now build our task list -@if defined CLASSICSERVER ( set TASK_LIST=UseClassicServerTask) -@if defined SUPERCLASSIC ( set TASK_LIST=UseSuperClassicTask) -:: Theoretically this next line is redundant -@if defined SUPERSERVER ( set TASK_LIST=UseSuperServerTask) +:: At this stage, if TASK_LIST is not defined then we are not doing a server install +@if defined TASK_LIST ( + if defined APPTASK ( + set TASK_LIST=!TASK_LIST!,UseApplicationTask + set INSTALLTYPE=CustomInstall + ) else ( + set TASK_LIST=!TASK_LIST!,UseServiceTask + ) -@if defined APPTASK ( - set TASK_LIST=!TASK_LIST!,UseApplicationTask -) else ( - set TASK_LIST=!TASK_LIST!,UseServiceTask + if NOT defined NOAUTOSTART ( + set TASK_LIST=!TASK_LIST!,AutoStartTask + set INSTALLTYPE=CustomInstall + ) ) -@if NOT defined NOAUTOSTART ( - set TASK_LIST=!TASK_LIST!,AutoStartTask + +@if NOT defined NOCOPYFBLIB ( + if not defined TASK_LIST ( + set TASK_LIST=CopyFbClientToSysTask + ) else ( + set TASK_LIST=!TASK_LIST!,CopyFbClientToSysTask + ) set INSTALLTYPE=CustomInstall ) -if NOT defined NOCOPYFBLIB ( - set TASK_LIST=!TASK_LIST!,CopyFbClientToSysTask - set INSTALLTYPE=CustomInstall -) @if defined COPYGDSLIB ( - set TASK_LIST=!TASK_LIST!,CopyFbClientAsGds32Task + if not defined TASK_LIST ( + set TASK_LIST=CopyFbClientAsGds32Task + ) else ( + set TASK_LIST=!TASK_LIST!,CopyFbClientAsGds32Task + ) set INSTALLTYPE=CustomInstall ) +@call :PRINT_VARS In %0 - End of set TASK_LIST + :SET_CMD_PARAMS :: set up the CMD_PARAMS variable we will use @@ -248,37 +312,40 @@ if NOT defined NOCOPYFBLIB ( :: Setting PASSWORD is only relevant for a server install @if defined PASSWORD ( - @if defined SERVER ( + if defined SERVER_INSTALL ( set SYSDBAPASSWORD=%ISC_PASSWORD% set CMD_PARAMS=!CMD_PARAMS! /SYSDBAPASSWORD=%SYSDBAPASSWORD% set INSTALLTYPE=CustomInstall ) ) -if defined NOMSG set CMD_PARAMS=!CMD_PARAMS! /SUPPRESSMSGBOXES -if defined SILENT set CMD_PARAMS=!CMD_PARAMS! /SILENT -if defined SP set CMD_PARAMS=!CMD_PARAMS! /SP- -if defined VERYSILENT set CMD_PARAMS=!CMD_PARAMS! /VERYSILENT +@if defined NOMSG set CMD_PARAMS=!CMD_PARAMS! /SUPPRESSMSGBOXES +@if defined SILENT set CMD_PARAMS=!CMD_PARAMS! /SILENT +@if defined SP set CMD_PARAMS=!CMD_PARAMS! /SP- +@if defined VERYSILENT set CMD_PARAMS=!CMD_PARAMS! /VERYSILENT :: Setting CustomInstall clears the default COMPONENTS list so we :: must define it manually -@if /I %INSTALLTYPE% == "CustomInstall" ( - @if defined CLIENT ( set COMPONENTS=ClientComponent) - @if defined DEVINST ( set COMPONENTS=DevAdminComponent,ClientComponent) +::echo INSTALLTYPE %INSTALLTYPE% +@if /I "%INSTALLTYPE%" == "CustomInstall" ( + if defined CLIENT ( set COMPONENTS=ClientComponent) + if defined DEVINST ( set COMPONENTS=DevAdminComponent,ClientComponent) + if defined SERVER_INSTALL ( set COMPONENTS=ServerComponent,DevAdminComponent,ClientComponent) ) else ( set COMPONENTS=ServerComponent,DevAdminComponent,ClientComponent ) -if defined INSTALL ( - set FULL_CMD=/TYPE=!INSTALLTYPE! /TASKS="!TASK_LIST!" /COMPONENTS="!COMPONENTS!" !CMD_PARAMS! +@if defined INSTALL ( + if defined TASK_LIST ( + set FULL_CMD=/TYPE=!INSTALLTYPE! /TASKS="!TASK_LIST!" /COMPONENTS="!COMPONENTS!" !CMD_PARAMS! + ) else ( + set FULL_CMD=/TYPE=!INSTALLTYPE! /COMPONENTS="!COMPONENTS!" !CMD_PARAMS! + ) ) else ( set FULL_CMD=!CMD_PARAMS! ) +@call :PRINT_VARS In %0 - After setting COMPONENTS and FULL_CMD -@if defined VERBOSE ( - @call :PRINT_VARS - if NOT defined DEBUG pause -) @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF @@ -288,8 +355,10 @@ if defined INSTALL ( :PRINT_VARS :: if a variable is not defined we don't print it, except for critical :: variables such as FINALCMD that MUST be defined. +@if not defined VERBOSE goto :EOF +@if not "%~1" == "" ( echo %* ) @echo Variables set during script execution are: -@set ADIRNAME +@set ADIRNAME 2>nul @set APPTASK 2>nul @set CLASSICSERVER 2>nul @set CLEAN 2>nul @@ -299,12 +368,15 @@ if defined INSTALL ( @set COPYGDSLIB 2>nul @set DEVINST 2>nul @set DRYRUN 2>nul -@set FBINST_EXEC +@set FBINST_EXEC 2>nul +@set FIREBIRD 2>nul @set FINALCMD 2>nul -@set FULL_CMD +@set FULL_CMD 2>nul @set FORCE 2>nul @set INTERACTIVE 2>nul @set INSTALL 2>nul +@set ISC_USER 2>nul +@set ISC_PASSWORD 2>nul @set MERGE_TASKS 2>nul @set NOARCHIVE= 2>nul @set NOAUTOSTART 2>nul @@ -315,8 +387,7 @@ if defined INSTALL ( @set PASSWORD 2>nul @set RUN_TIMESTAMP 2>nul @set SCRIPTED 2>nul -@set SERVICE_TASK 2>nul -@set SERVER 2>nul +@set SERVER_INSTALL 2>nul @set SERVICE_TASK 2>nul @set SILENT 2>nul @set SP 2>nul @@ -327,7 +398,7 @@ if defined INSTALL ( @set UNINSTALL 2>nul @set VERYSILENT 2>nul @echo. - +@if NOT defined DEBUG pause @goto :EOF ::======================================================= @@ -336,56 +407,65 @@ if defined INSTALL ( @echo. @echo. @call :SET_VERBOSE_IF_DEBUG_ON -if defined DEBUG @echo Entering %0 %* -::@call :CHECK_ENV || (@echo Check the values in SET_ENV & goto :EOF ) +@if defined DEBUG @echo Entering %~0 %* @call :CHECK_ENV -::@call :RESET_INSTALL_ENV +@if defined _err ( goto :EOF) + @call :GET_OPTS %* @call :SET_PARAMS %* ::@call :SET_VERBOSE -@if defined VERBOSE @echo on @if defined VERBOSE @echo FULL_CMD is %FULL_CMD% @if defined VERBOSE ( if NOT defined DEBUG pause ) @call :TIMESTAMP +@if defined DEBUG echo After call TIMESTAMP @set RUN_TIMESTAMP=%TIMESTAMP% @set INSTALL_TIMESTAMP=%TIMESTAMP% +@if defined DEBUG echo Before set FINALCMD +@set FINALCMD=%FBINST_EXEC% %FULL_CMD% /DIR=%FIREBIRD% /LOG=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%.log /SAVEINF=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%-saved.inf +@if defined DEBUG echo After set FINALCMD -set FINALCMD=%FBINST_EXEC% %FULL_CMD% /DIR=%FIREBIRD% /LOG=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%.log /SAVEINF=%FBINSTALLLOGDIR%\install%RUN_TIMESTAMP%-saved.inf -if defined DRYRUN ( +@if defined DRYRUN ( @echo DRYRUN - Not executing call %FINALCMD% ) else ( - @if defined SHOW_FINAL_CMD @echo Executing %FINALCMD% - call %FINALCMD% + @if defined DEBUG @echo DRYRUN not set + @if defined SHOW_FINAL_CMD (@echo Executing %FINALCMD%) + @call %FINALCMD% - @if errorlevel 1 ( + @if ERRORLEVEL 1 ( rem @echo Calling %FBINST_EXEC% failed with %ERRORLEVEL% set _err=%ERRORLEVEL% - call :ISS_ERROR %_err% %FBINST_EXEC% %FULL_CMD% + @call :ISS_ERROR %_err% %FBINST_EXEC% %FULL_CMD% set /A XRESULT+=1 + @goto :EOF ) else ( @echo Calling %FBINST_EXEC%......................SUCCESS! ) - @echo. @echo Now checking system state... - if not defined NOCOPYFBLIB ( + + @if defined SERVER_INSTALL ( + call :CHECKSERVICECONFIGURED + call :CHECKSERVICEINSTALLED + ) + @if not defined NOCOPYFBLIB ( call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll good bad err_is_fail ) else ( call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll bad good no_err_is_fail ) - if not defined COPYGDSLIB ( + @if not defined COPYGDSLIB ( call :CHECKFILEEXISTS c:\windows\system32\gds32.dll bad good no_err_is_fail ) else ( call :CHECKFILEEXISTS c:\windows\system32\gds32.dll good bad err_is_fail ) - @echo. + @echo Calling COPY_INSTALL @call :COPY_INSTALL @echo. ) - @echo. +@echo. @echo %0 completed with %XRESULT% errors @set XRESULT=0 + @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @echo. @@ -401,12 +481,14 @@ if defined DRYRUN ( @call :SET_VERBOSE_IF_DEBUG_ON ::@call :SET_VERBOSE @if defined DEBUG @echo Entering %0 %* +@if defined NOUNINSTALL ( echo NOUNINSTALL set. Not running uninstaller & exit /b 1) + ::@call :RESET_INSTALL_ENV @call :GET_OPTS %* UNINSTALL @call :SET_PARAMS ::@call :SET_VERBOSE @if defined VERBOSE @echo on -@if defined VERBOSE call :PRINT_VARS +@if defined VERBOSE @call :PRINT_VARS @if defined VERBOSE @echo FULL_CMD is %FULL_CMD% @if defined NOUNINSTALL ( @echo NOUNINSTALL was passed. Exiting %0. @@ -418,14 +500,14 @@ if defined DRYRUN ( @if defined VERBOSE ( if NOT defined DEBUG (pause) ) @if defined DRYRUN ( echo DRYRUN - Not executing call %FINALCMD% - if defined DEBUG @echo Leaving %0 + @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @echo. @echo. goto :EOF ) @if defined SHOW_FINAL_CMD @echo Executing %FINALCMD% - call %FINALCMD% 2>nul + @call %FINALCMD% 2>nul if errorlevel 1 ( set _err=%ERRORLEVEL% ) else ( @@ -433,7 +515,7 @@ if defined DRYRUN ( ) if %_err% GEQ 1 ( set _err=%ERRORLEVEL% - call :ISS_ERROR %_err% %UNINSTALLEXE% %FULL_CMD% + @call :ISS_ERROR %_err% %UNINSTALLEXE% %FULL_CMD% set /A XRESULT+=1 ) else ( echo Calling %FIREBIRD%\%UNINSTALLEXE% ................SUCCESS! @@ -452,23 +534,23 @@ if defined DRYRUN ( echo. echo Now checking system state... - call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll bad good no_err_is_fail - call :CHECKFILEEXISTS c:\windows\system32\gds32.dll bad good no_err_is_fail + @call :CHECKFILEEXISTS c:\windows\system32\fbclient.dll bad good no_err_is_fail + @call :CHECKFILEEXISTS c:\windows\system32\gds32.dll bad good no_err_is_fail if defined CLEAN ( - call :CHECKSHAREDDLLS - call :CHECKFILEEXISTS %FIREBIRD% bad good no_err_is_fail + @call :CHECKSHAREDDLLS + @call :CHECKFILEEXISTS %FIREBIRD% bad good no_err_is_fail ) echo. - call :COPY_INSTALL + @call :COPY_INSTALL echo. ) echo. echo %0 completed with %XRESULT% errors set XRESULT=0 -if defined DEBUG @echo Leaving %0 -call :UNSET_VERBOSE +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE @echo. @echo. @goto :EOF @@ -477,7 +559,7 @@ call :UNSET_VERBOSE ::===================================== :CHECKFILEEXISTS -if defined DEBUG @echo Entering %0 +@if defined DEBUG @echo Entering %0 @call :SET_VERBOSE_IF_DEBUG_ON :: DIR returns an error if file not found and zero if file is returned so we @@ -491,66 +573,108 @@ if defined DEBUG @echo Entering %0 :: - %3 - string to output if DIR throws an error :: - %4 - flag to indicate if 0 is an error or not ::@call :SET_VERBOSE -if defined VERBOSE @echo on -if defined VERBOSE @echo %* +@if defined VERBOSE @echo on +@if defined VERBOSE @echo %* dir %1 >nul 2>nul -if errorlevel 1 ( +@if errorlevel 1 ( set _err=%ERRORLEVEL% ) else ( set _err=0 ) -if %_err% EQU 0 ( +@if %_err% EQU 0 ( @echo %TAB% %1 exists - %2 ! ) else ( @echo %TAB% %1 not found - %3 ! ) -if "%4"=="err_is_fail" ( +@if "%4"=="err_is_fail" ( if %_err% GTR 0 ( set /A XRESULT+=1 @echo XRESULT++ ) ) -if "%4"=="no_err_is_fail" ( +@if "%4"=="no_err_is_fail" ( if %_err% EQU 0 ( set /A XRESULT+=1 @echo XRESULT++ ) ) -call :RESET_ERRORLEVEL +@call :RESET_ERRORLEVEL @call :UNSET_VERBOSE -if defined DEBUG @echo Leaving %0 +@if defined DEBUG @echo Leaving %0 @goto :EOF ::===CHECKFILEEXISTS================================== ::===================================== :CHECKSHAREDDLLS -if defined DEBUG @echo Entering %0 +@if defined DEBUG @echo Entering %0 @call :SET_VERBOSE_IF_DEBUG_ON @reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs > %TEMP%\shareddlls.txt -@grep --ignore-case --count firebird %TEMP%\shareddlls.txt > %TEMP%\shareddllscount.txt +::@grep --ignore-case --count firebird %TEMP%\shareddlls.txt > %TEMP%\shareddllscount.txt +type %TEMP%\shareddlls.txt | find /C /I "firebird" > %TEMP%\shareddllscount.txt set /p SHAREDDLLSCOUNT= < %TEMP%\shareddllscount.txt -if NOT defined DEBUG del /q %TEMP%\shareddll*.txt -if %SHAREDDLLSCOUNT% GTR 0 ( +@if NOT defined DEBUG del /q %TEMP%\shareddll*.txt +@if %SHAREDDLLSCOUNT% GTR 0 ( @echo %TAB% Oops - residue in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs set /A XRESULT+=1 @echo XRESULT++ ) -call :RESET_ERRORLEVEL -if defined DEBUG @echo Leaving %0 +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF ::===CHECKSHAREDDLLS=================== +::===================================== +:CHECKSERVICECONFIGURED +@if defined DEBUG @echo Entering %0 +@call :SET_VERBOSE_IF_DEBUG_ON + +:: Add test for server arch set in firebird.conf + + if defined CLASSICSERVER ( set STR_TO_TEST="servermode = classic" ) + if defined SUPERCLASSIC ( set STR_TO_TEST="servermode = superclassic" ) + if defined SUPERSERVER ( set STR_TO_TEST="servermode = super" ) + + call :CHECKSTRING %STR_TO_TEST% %FIREBIRD%\\firebird.conf + if ERRORLEVEL 1 ( + @echo %TAB% %STR_TO_TEST% not set in %FIREBIRD%\\firebird.conf + set /A XRESULT+=1 + @echo XRESULT++ + ) + +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE +@goto :EOF +::===CHECKSERVICECONFIGURED=================== + + + +::===================================== +:CHECKSERVICEINSTALLED +@if defined DEBUG @echo Entering %0 +@call :SET_VERBOSE_IF_DEBUG_ON + + %FIREBIRD%\\instsvc.exe q + + +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE +@goto :EOF +::===CHECKSERVICEINSTALLED=================== + + ::===================================== :COPY_INSTALL @call :SET_VERBOSE_IF_DEBUG_ON -if defined DEBUG @echo Entering %0 +@if defined DEBUG @echo Entering %0 ::@call :SET_VERBOSE -if defined VERBOSE @echo on +@if defined VERBOSE @echo on :: ADIRNAME should normally be set during install and persist for uninstall @if not defined ADIRNAME ( @@ -590,10 +714,13 @@ if defined VERBOSE @echo on :CHECK_ENV :: TODO - add more checks for the environment declared in SET_GLOBAL_ENV if not exist %FBINST_EXEC% ( + echo %~0 failed echo Cannot find %FBINST_EXEC% - exit /b 1 + echo Check the setting of FBINST_EXEC + set _err=1 + exit /b %_err% ) -@goto :EOF +goto :EOF ::===CHECK_ENV========================= ::===================================== @@ -604,6 +731,7 @@ if not exist %FBINST_EXEC% ( :: not have the same value as the internal ERRORLEVEL. We have to execute an :: arbitrary command that cannot fail if we want to really reset the internal :: variable. +@set _err= @ver > nul @goto :EOF ::===RESET_ERRORLEVEL================== @@ -620,12 +748,15 @@ if not exist %FBINST_EXEC% ( @goto :EOF ::===SET_VERBOSE_IF_DEBUG_ON================ + ::===================================== :SET_VERBOSE @set VERBOSE=1 +@echo on @goto :EOF ::===SET_VERBOSE================ + ::===================================== :UNSET_VERBOSE :: Unset VERBOSE before exiting each sub-routine. @@ -651,50 +782,131 @@ goto :EOF :ISS_ERROR @echo. @echo InnoSetup ErrorCode %1 from calling %~2 %~3 %~4 %~5 %~6 %~7 %~8 %~9 -@echo Definition of Innosetup errorcode %1 is to be added later +@echo. +@if "%1"=="1" ( + @echo Setup failed to initialize. + @echo. + @goto :EOF +) + +@if "%1"=="2" ( + @echo The user clicked Cancel in the wizard before the actual installation + @echo started, or chose 'No' on the opening 'This will install...' message box. + @echo. + @goto :EOF +) + +@if "%1" == "3" ( + @echo A fatal error occurred while preparing to move to the next + @echo installation phase (for example, from displaying the pre-installation + @echo wizard pages to the actual installation process). This should never + @echo happen except under the most unusual of circumstances, such as + @echo running out of memory or Windows resources. + @echo. + @goto :EOF +) + +@if "%1" == "4" ( + @echo A fatal error occurred during the actual installation process. + @echo. + @goto :EOF +) + +@if "%1" == "5" ( + @echo The user clicked Cancel during the actual installation process, + @echo or chose Abort at an Abort-Retry-Ignore box. + @echo. + @goto :EOF +) + +@if "%1" == "6" ( + @echo The Setup process was forcefully terminated by the debugger + @echo (Run | Terminate was used in the Compiler IDE). + @echo. + @goto :EOF +) + +@if "%1" == "7" ( + @echo The Preparing to Install stage determined that Setup cannot proceed + @echo with installation. (First introduced in Inno Setup 5.4.1.) + @echo. + @goto :EOF +) + +@if "%1" == "8" ( + @echo The Preparing to Install stage determined that Setup cannot proceed + @echo with installation, and that the system needs to be restarted in + @echo order to correct the problem. (First introduced in Inno Setup 5.4.1.) + @echo. + @goto :EOF +) @echo. @goto :EOF ::====================================== -::================================================================================================================ -:: GENERIC SUPPORT ROUTINES FROM HERE TO END ====================================================================== -::================================================================================================================ +::============================================================================== +:: GENERIC SUPPORT ROUTINES FROM HERE TO END ================================== +::============================================================================== + + +::===================================== +:CHECKSTRING +@if defined DEBUG @echo Entering %0 +@call :SET_VERBOSE_IF_DEBUG_ON +::@if defined VERBOSE echo %0 - %* +for %%v in ( %* ) do ( + if /I "%%v"=="" ( @goto :EOF ) +) +:: === NOTE ==== +:: We use the /x flag here for an exact match! +@if defined VERBOSE echo calling findstr /x /c:%1 /i %2 +:: findstr /x /c:"servermode = superclassic" /i Firebird_4_0\firebird.conf +findstr /x /c:%1 /i %2 + +@call :RESET_ERRORLEVEL +@if defined DEBUG @echo Leaving %0 +@call :UNSET_VERBOSE +@goto :EOF +::===CHECKSTRING=================== + + ::===================================== :GET_DATE -if NOT defined DEBUG @echo off -echo. | date | FIND "(mm" > NUL -if errorlevel 1 ((set MD=0) & call :ParseDate DD MM) else ((set MD=1) & call :ParseDate MM DD) +@if NOT defined DEBUG @echo off +@echo. | date | FIND "(mm" > NUL +@if errorlevel 1 ((set MD=0) & call :ParseDate DD MM) else ((set MD=1) & call :ParseDate MM DD) @goto :EOF :ParseDate -for /f "tokens=1-3 delims=/.- " %%a in ("%DATE%") do ( -set %1=%%a -set %2=%%b -set YYYY=%%c +@for /f "tokens=1-3 delims=/.- " %%a in ("%DATE%") do ( +@set %1=%%a +@set %2=%%b +@set YYYY=%%c @goto:EOF) ::===================================== ::===================================== :GET_TIME -if NOT defined DEBUG @echo off -for /f "tokens=1-4 delims=:. " %%a in ("%TIME%") do ( -set hh=%%a -set nn=%%b -set ss=%%c -set ms=%%d +@if NOT defined DEBUG @echo off +@for /f "tokens=1-4 delims=:. " %%a in ("%TIME%") do ( +@set hh=%%a +@set nn=%%b +@set ss=%%c +@set ms=%%d @goto :EOF) ::===================================== ::===================================== :CLEAR_DT_VARS -if NOT defined DEBUG @echo off -set MM= -set DD= -set YYYY= -set hh= -set nn= -set ss= -set ms= +@if NOT defined DEBUG @echo off +@set MM= +@set DD= +@set YYYY= +@set hh= +@set nn= +@set ss= +@set ms= +@if defined DEBUG @echo Leaving CLEAR_DT_VARS @goto :EOF ::===================================== @@ -703,8 +915,8 @@ set ms= @call :GET_DATE @call :GET_TIME @set TIMESTAMP=%YYYY%%MM%%DD%%hh%%nn%%ss% -if defined DEBUG (@echo Timestamp set to %TIMESTAMP%) -call :CLEAR_DT_VARS +@if defined DEBUG (@echo Timestamp set to %TIMESTAMP%) +@call :CLEAR_DT_VARS @goto :EOF ::===================================== @@ -724,25 +936,22 @@ call :CLEAR_DT_VARS :: sometimes it is useful to just tidy up! @if /I "%1"=="clean" ( - @call :RUN_UNINSTALLER SCRIPTED CLEAN - @goto :EOF + @set NOUNINSTALL= + call :RUN_UNINSTALLER SCRIPTED CLEAN + goto :EOF ) @call :RUN_INSTALLER %* +@if defined _err ( echo _err is %_err% - quitting. && @goto :EOF) + @call :RUN_UNINSTALLER %* + @if defined DEBUG @echo Leaving %0 @call :UNSET_VERBOSE @goto :EOF ::===MAIN============================== -:: CONTINUE with -:: - look at setting up different test recipes and naming them. -:: - Integrate innosetup exit codes and look at error handling -:: - Tidy up the relationship between DEBUG and VERBOSE. -:: - DEBUG should turn on VERBOSE? -:: - VERBOSE should be routine specific.? -:: - DEBUG should persist for entire script run? :: NOTHING BEYOND THIS POINT=========== diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index 61a503f578..4cec7d682b 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -41,9 +41,8 @@ # # String # ------ -# Strings are also what they sound like, strings. Examples: +# Strings are also what they sound like, strings. Example: # RemoteServiceName = gds_db -# RemotePipeName = pipe47 # # Scopes # ------ @@ -612,6 +611,18 @@ #ConnectionIdleTimeout = 0 +# ---------------------------- +# +# Set number of seconds after which ON DISCONNECT trigger execution will be +# automatically cancelled by the engine. Zero means no timeout is set. +# +# Per-database configurable. +# +# Type: integer +# +#OnDisconnectTriggerTimeout = 180 + + # ---------------------------- # # How often the pages are flushed on disk @@ -1020,6 +1031,19 @@ #GCPolicy = combined +# ---------------------------- +# Maximum statement cache size +# +# The maximum amount of RAM used to cache unused DSQL compiled statements. +# If set to 0 (zero), statement cache is disabled. +# +# Per-database configurable. +# +# Type: integer +# +#MaxStatementCacheSize = 2M + + # ---------------------------- # Security database # @@ -1083,18 +1107,6 @@ # #IpcName = FIREBIRD -# -# The name of the pipe used as a transport channel in NetBEUI protocol. -# Has the same meaning as a port number for TCP/IP. The default value is -# compatible with IB/FB1. -# -# Per-connection configurable. -# -# Type: string -# -#RemotePipeName = interbas - - # ============================ # Settings for Unix/Linux platforms # ============================ diff --git a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj index c75d4835fa..416a03d2c3 100644 --- a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj +++ b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj @@ -4881,16 +4881,6 @@ path = old_proto.h; refType = 4; }; - F616C66F0200B0CF01EF0ADE = { - isa = PBXFileReference; - path = opt.cpp; - refType = 4; - }; - F616C6700200B0CF01EF0ADE = { - isa = PBXFileReference; - path = opt_proto.h; - refType = 4; - }; F616C6710200B0CF01EF0ADE = { children = ( F616C6720200B0CF01EF0ADE, @@ -5133,11 +5123,6 @@ path = rse.cpp; refType = 4; }; - F616C6A70200B0D001EF0ADE = { - isa = PBXFileReference; - path = rse.h; - refType = 4; - }; F616C6A80200B0D001EF0ADE = { isa = PBXFileReference; path = rse_proto.h; @@ -6767,16 +6752,6 @@ path = winvx.cpp; refType = 4; }; - F616C8C00200B0D001EF0ADE = { - isa = PBXFileReference; - path = wnet.cpp; - refType = 4; - }; - F616C8C10200B0D001EF0ADE = { - isa = PBXFileReference; - path = wnet_proto.h; - refType = 4; - }; F616C8C20200B0D001EF0ADE = { isa = PBXFileReference; path = xdr.cpp; diff --git a/builds/posix/Makefile.in.examples b/builds/posix/Makefile.in.examples index 9f8177b121..d0f8973830 100644 --- a/builds/posix/Makefile.in.examples +++ b/builds/posix/Makefile.in.examples @@ -124,6 +124,9 @@ $(EXAMPLES_FB)/README: $(CP) $(ROOT)/examples/stat/*.* $(EXAMPLES_FB)/stat/ $(CP) $(ROOT)/examples/udf/*.* $(EXAMPLES_FB)/udf/ $(CP) $(ROOT)/examples/udr/*.* $(EXAMPLES_FB)/udr/ + $(CP) $(ROOT)/examples/object_pascal/*.* $(EXAMPLES_FB)/object_pascal/ + $(CP) $(ROOT)/examples/object_pascal/[mM]ake* $(EXAMPLES_FB)/object_pascal/ + $(CP) $(ROOT)/examples/object_pascal/common/*.* $(EXAMPLES_FB)/object_pascal/common/ # $(CP) intlemp.fdb $(EXAMPLES_FB)/empbuild/ $(CP) $(ROOT)/examples/readme $(EXAMPLES_FB)/README $(CP) $(ROOT)/examples/empbuild/employe2.sql $(EXAMPLES_FB)/empbuild/ diff --git a/builds/posix/fbintl.vers b/builds/posix/fbintl.vers index a91d69b660..ffadaaff23 100644 --- a/builds/posix/fbintl.vers +++ b/builds/posix/fbintl.vers @@ -7,25 +7,26 @@ # you may not use this file except in compliance with the # License. You may obtain a copy of the License at # http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. -# +# # Software distributed under the License is distributed AS IS, # WITHOUT WARRANTY OF ANY KIND, either express or implied. # See the License for the specific language governing rights # and limitations under the License. -# +# # The Original Code was created by Nickolay Samofatov # for the Firebird Open Source RDBMS project. -# +# # Copyright (c) 2004 Nickolay Samofatov # and all contributors signed below. -# +# # All Rights Reserved. # Contributor(s): ______________________________________. # Adriano dos Santos Fernandes -# +# # LD_lookup_charset LD_lookup_texttype +LD_lookup_texttype_with_status LD_setup_attributes LD_version diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables index fe286cda7e..c5d555b1ad 100644 --- a/builds/posix/make.shared.variables +++ b/builds/posix/make.shared.variables @@ -77,7 +77,7 @@ AllObjects += $(Chacha_Objects) # Engine Engine_Objects:= $(call dirObjects,jrd) $(call dirObjects,dsql) $(call dirObjects,jrd/extds) \ - $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) $(call dirObjects,jrd/trace) \ + $(call dirObjects,jrd/optimizer) $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) $(call dirObjects,jrd/trace) \ $(call makeObjects,lock,lock.cpp) AllObjects += $(Engine_Objects) diff --git a/builds/posix/prefix.darwin_aarch64 b/builds/posix/prefix.darwin_aarch64 new file mode 100644 index 0000000000..e9e9ae180f --- /dev/null +++ b/builds/posix/prefix.darwin_aarch64 @@ -0,0 +1,49 @@ +# The contents of this file are subject to the Interbase Public +# License Version 1.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy +# of the License at http://www.Inprise.com/IPL.html +# +# Software distributed under the License is distributed on an +# "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express +# or implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code was created by Inprise Corporation +# and its predecessors. Portions created by Inprise Corporation are +# +# Copyright (C) 2000 Inprise Corporation +# All Rights Reserved. +# Contributor(s): ______________________________________. +# Start of file prefix.darwin: $(VERSION) @PLATFORM@ +# 2 Oct 2002, Nickolay Samofatov - Major Cleanup +# +# Default build from 10.9 using Clang +# +# Build instructions +# set CFLAGS='-I (ICUDIR)/icu/source/common' (ucnv.h) +# set LDFLAGS='-L(ICUDIR)/icu/source/lib' (-licuuc) +# set CXXFLAGS='-I (ICUDIR)/icu/source/common -I ICUDIR/icu/source/i18n' +# where ICUDIR is where you installed ICU +# configure using --with-builtin-tommath +# or add the relevant -I, -L for an installed version of libtommath + +#DYLD_PRINT_ENV=1 +#export DYLD_PRINT_ENV + +#DYLD_PRINT_LIBRARIES=1 +#export DYLD_PRINT_LIBRARIES + +MACOSX_DEPLOYMENT_TARGET=12.0 +export MACOSX_DEPLOYMENT_TARGET + +PROD_FLAGS=-DDARWIN -DARM64 -pipe -O2 -MMD -fPIC -fno-common -mmacosx-version-min=12.0 +DEV_FLAGS=-ggdb -DDARWIN -DARM64 -pipe -MMD -fPIC -fno-omit-frame-pointer -fno-common -Wall -fno-optimize-sibling-calls -mmacosx-version-min=12.0 -Wno-non-virtual-dtor +CXXFLAGS:=$(CXXFLAGS) -fvisibility-inlines-hidden -fvisibility=hidden -stdlib=libc++ + +UNDEF_PLATFORM= + +LINK_LIBS+=-liconv +#MATHLIB=$(ROOT)/extern/libtommath/.libs/libtommath.a +SO_LINK_LIBS+=-liconv + +include $(ROOT)/gen/darwin.defaults diff --git a/builds/win32/msvc15/common.vcxproj b/builds/win32/msvc15/common.vcxproj index 90a062e04d..86baeb4be8 100644 --- a/builds/win32/msvc15/common.vcxproj +++ b/builds/win32/msvc15/common.vcxproj @@ -124,6 +124,7 @@ + diff --git a/builds/win32/msvc15/common.vcxproj.filters b/builds/win32/msvc15/common.vcxproj.filters index 8f41a256cf..700b1d4543 100644 --- a/builds/win32/msvc15/common.vcxproj.filters +++ b/builds/win32/msvc15/common.vcxproj.filters @@ -392,6 +392,9 @@ headers + + headers + headers diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj index 89a580cde6..4bc3e762f6 100644 --- a/builds/win32/msvc15/engine.vcxproj +++ b/builds/win32/msvc15/engine.vcxproj @@ -41,6 +41,9 @@ + + + @@ -90,7 +93,6 @@ - @@ -100,8 +102,9 @@ - - + + + @@ -152,6 +155,7 @@ + @@ -186,6 +190,9 @@ + + + @@ -275,7 +282,6 @@ - @@ -296,8 +302,7 @@ - - + @@ -317,7 +322,6 @@ - @@ -327,6 +331,7 @@ + diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters index aa5308dc3e..80fe264b9f 100644 --- a/builds/win32/msvc15/engine.vcxproj.filters +++ b/builds/win32/msvc15/engine.vcxproj.filters @@ -43,6 +43,9 @@ {686cba10-1e4f-44f7-b044-972ef8327197} + + {4acc7c23-ac25-4311-a075-813ed730cb40} + @@ -138,6 +141,15 @@ DSQL + + DSQL + + + DSQL + + + DSQL + DSQL @@ -288,9 +300,6 @@ JRD files - - JRD files - JRD files @@ -312,12 +321,6 @@ JRD files - - JRD files - - - JRD files - JRD files @@ -363,6 +366,9 @@ JRD files + + JRD files + JRD files @@ -507,6 +513,15 @@ JRD files + + Optimizer + + + Optimizer + + + Optimizer + @@ -545,6 +560,15 @@ Header files + + Header files + + + Header files + + + Header files + Header files @@ -827,9 +851,6 @@ Header files - - Header files - Header files @@ -878,10 +899,7 @@ Header files - - Header files - - + Header files @@ -932,9 +950,6 @@ Header files - - Header files - Header files @@ -962,6 +977,9 @@ Header files + + Header files + Header files diff --git a/builds/win32/msvc15/remote.vcxproj b/builds/win32/msvc15/remote.vcxproj index 41d0aa0e9e..691b114bdd 100644 --- a/builds/win32/msvc15/remote.vcxproj +++ b/builds/win32/msvc15/remote.vcxproj @@ -157,7 +157,6 @@ - @@ -165,7 +164,6 @@ - diff --git a/builds/win32/msvc15/remote.vcxproj.filters b/builds/win32/msvc15/remote.vcxproj.filters index 391b076fc8..5ebb99e644 100644 --- a/builds/win32/msvc15/remote.vcxproj.filters +++ b/builds/win32/msvc15/remote.vcxproj.filters @@ -29,9 +29,6 @@ REMOTE files - - REMOTE files - AUTH files @@ -64,9 +61,6 @@ Header files - - Header files - Header files diff --git a/configure.ac b/configure.ac index beebfbb57d..f6aa655f98 100644 --- a/configure.ac +++ b/configure.ac @@ -86,6 +86,20 @@ dnl Test for special ar options? AR_OPT_CHECK=false case "$build" in + aarch64-*-darwin*) + MAKEFILE_PREFIX=darwin_aarch64 + MAKEFILE_POSTFIX=darwin + PLATFORM=DARWIN + INSTALL_PREFIX=darwin + AC_DEFINE(DARWIN, 1, [Define this if OS is DARWIN]) + XE_APPEND(-framework CoreFoundation,LIBS) + EDITLINE_FLG=Y + SHRLIB_EXT=dylib + CPU_TYPE=ARM64 + EXPORT_SYMBOLS_STYLE=darwin + RAW_DEVICES_FLG=N + ;; + x*64-*-darwin*) MAKEFILE_PREFIX=darwin_x86_64 MAKEFILE_POSTFIX=darwin @@ -1049,7 +1063,7 @@ AC_MSG_CHECKING(for working sem_init()) AC_RUN_IFELSE([AC_LANG_SOURCE([[#include main () { sem_t s; - exit(sem_init(&s,0,0)); + return sem_init(&s,0,0); } ]])],[AC_DEFINE(WORKING_SEM_INIT,1,[Define this if sem_init() works on the platform]) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) @@ -1089,7 +1103,7 @@ if test "$ac_cv_sys_file_offset_bits" = "no"; then AC_MSG_CHECKING(for native large file support) AC_RUN_IFELSE([AC_LANG_SOURCE([[#include main () { - exit(!(sizeof(off_t) == 8)); + return !(sizeof(off_t) == 8); }]])],[ac_cv_sys_file_offset_bits=64; AC_DEFINE(_FILE_OFFSET_BITS,64) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)],[]) fi @@ -1140,7 +1154,7 @@ main () { char a; union { long long x; sem_t y; } b; }; - exit((int)&((struct s*)0)->b); + return (int)&((struct s*)0)->b; }]])],[ac_cv_c_alignment=$ac_status],[ac_cv_c_alignment=$ac_status],[]) AC_MSG_RESULT($ac_cv_c_alignment) AC_DEFINE_UNQUOTED(FB_ALIGNMENT, $ac_cv_c_alignment, [Alignment of long]) @@ -1151,7 +1165,7 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[main () { char a; double b; }; - exit((int)&((struct s*)0)->b); + return (int)&((struct s*)0)->b; }]])],[ac_cv_c_double_align=$ac_status],[ac_cv_c_double_align=$ac_status],[]) AC_MSG_RESULT($ac_cv_c_double_align) AC_DEFINE_UNQUOTED(FB_DOUBLE_ALIGN, $ac_cv_c_double_align, [Alignment of double]) @@ -1168,7 +1182,7 @@ static int abs64Compare(SINT64 n1, SINT64 n2) { return n1 == n2 ? 0 : n1 < n2 ? 1 : -1; } int main() { - exit (abs64Compare(-9223372036854775808, 3652058) == 1 ? 0 : 1); + return abs64Compare(-9223372036854775808, 3652058) == 1 ? 0 : 1; }]])],[ac_cv_compare_failed=0 ac_cv_compare_result=success]) CFLAGS="$savedflags" @@ -1222,15 +1236,6 @@ case "$PLATFORM" in ;; win32) - FB_PIPE_NAME=interbas - AC_ARG_WITH(pipe-name, - [ --with-pipe-name specify wnet pipe name (default=interbas)], - [FB_PIPE_NAME=${withval}]) - AH_VERBATIM(FB_PIPE_NAME, -[/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas"]) - AC_DEFINE_UNQUOTED(FB_PIPE_NAME,"$FB_PIPE_NAME") - AC_SUBST(FB_PIPE_NAME) XE_PREPEND( -mthreads -lmpr -lversion -lws2_32 -lole32,LIBS) ;; @@ -1294,6 +1299,8 @@ dnl # output mkdir -p gen/\$fb_tgt/firebird/examples/extauth mkdir -p gen/\$fb_tgt/firebird/examples/include mkdir -p gen/\$fb_tgt/firebird/examples/interfaces + mkdir -p gen/\$fb_tgt/firebird/examples/object_pascal + mkdir -p gen/\$fb_tgt/firebird/examples/object_pascal/common mkdir -p gen/\$fb_tgt/firebird/examples/package mkdir -p gen/\$fb_tgt/firebird/examples/stat mkdir -p gen/\$fb_tgt/firebird/examples/udf @@ -1503,10 +1510,6 @@ esac echo " Service name : $FB_SERVICE_NAME" echo " Service port : $FB_SERVICE_PORT" -case "$PLATFORM" in - win32) echo " Pipe name : $FB_PIPE_NAME";; -esac - echo " GPRE modules : c_cxx.cpp$GPRE_LANGUAGE_MODULES" echo diff --git a/doc/Firebird_conf.txt b/doc/Firebird_conf.txt index 99a485e21f..06871c017c 100644 --- a/doc/Firebird_conf.txt +++ b/doc/Firebird_conf.txt @@ -94,9 +94,8 @@ you only use 0/1 String ------ -Strings are also what they sound like, strings. Examples: +Strings are also what they sound like, strings. Example: RootDirectory = /opt/firebird -RemotePipeName = "pipe47" Configuration options @@ -144,8 +143,7 @@ DeadThreadsCollection integer default 50 PriorityBoost integer default 5 RemoteServiceName string default gds_db RemoteServicePort integer default 3050 (TCP port number) -RemotePipeName string default "interbas" (Windows only?) -IpcName string default "FirebirdIPI" (Windows only) +IpcName string default "FIREBIRD" (Windows only) MaxUnflushedWrites integer # of writes before file writes are forcibly synched. diff --git a/doc/README.connection_strings b/doc/README.connection_strings index a43908808d..39fe8b538a 100644 --- a/doc/README.connection_strings +++ b/doc/README.connection_strings @@ -10,10 +10,6 @@ For TCP (aka INET) protocol: [ / ] : -For named pipes (aka NetBEUI, aka WNET) protocol: - - \\ [ @ ] \ - For local connections as simple as: @@ -55,11 +51,6 @@ Examples: myserver/fb_db:mydb localhost/fb_db:mydb - Connect via named pipes: - - \\myserver\C:\db\mydb.fdb - \\myserver@fb_db\C:\db\mydb.fdb - Local connection: /db/mydb.fdb @@ -71,8 +62,7 @@ connection strings: [ : // [ [ : ] ] ] / -Where protocol is one of: INET (means TCP), WNET (means named pipes) or XNET -(means shared memory). +Where protocol is one of: INET (means TCP) or XNET (means shared memory). Examples: @@ -114,22 +104,12 @@ Examples: inet4://myserver/mydb inet6://myserver/mydb - Connect via named pipes: - - wnet://myserver/C:\db\mydb.fdb - wnet://myserver:fb_db/C:\db\mydb.fdb - Loopback connection via TCP: inet:///db/mydb.fdb inet://C:\db\mydb.fdb inet://mydb - Loopback connection via named pipes: - - wnet://C:\db\mydb.fdb - wnet://mydb - Local connection via shared memory: xnet://C:\db\mydb.fdb @@ -155,9 +135,7 @@ to connect locally using a specific transport protocol, please specify: inet:// or - wnet:// - or xnet:// -Note: WNET (named pipes) and XNET (shared memory) protocols are available on Windows only. +Note: XNET (shared memory) protocol is available on Windows only. diff --git a/doc/Using_OO_API.html b/doc/Using_OO_API.html index ba269991f9..25b16da5e5 100644 --- a/doc/Using_OO_API.html +++ b/doc/Using_OO_API.html @@ -1673,7 +1673,7 @@ only for some specific OS you can make this place a bit simpler. In minimum case the function should register module and all factories in plugin manager:

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

{

IPluginManager* diff --git a/doc/sql.extensions/README.context_variables2 b/doc/sql.extensions/README.context_variables2 index 5ef0fff719..88dff5daf1 100644 --- a/doc/sql.extensions/README.context_variables2 +++ b/doc/sql.extensions/README.context_variables2 @@ -48,7 +48,7 @@ Usage: Variable name Value ------------------------------------------------------------------------------ NETWORK_PROTOCOL | The network protocol used by client to connect. Currently - | used values: "TCPv4", "TCPv6", "WNET", "XNET" and NULL. + | used values: "TCPv4", "TCPv6", "XNET" and NULL. | WIRE_COMPRESSED | Compression status of current connection. | If connection is compressed - returns "TRUE", if it is diff --git a/doc/sql.extensions/README.subroutines.txt b/doc/sql.extensions/README.subroutines.txt index 5de6db5494..0caeabcabe 100644 --- a/doc/sql.extensions/README.subroutines.txt +++ b/doc/sql.extensions/README.subroutines.txt @@ -11,7 +11,7 @@ Description: Syntax: ::= - DECLARE [VARIABLE] [ := ]; + DECLARE [VARIABLE] [ = ]; | DECLARE [VARIABLE] CURSOR FOR (); | @@ -46,8 +46,11 @@ Syntax: Limitations: 1) Subroutines may not be nested in another subroutine. They are only supported in the main routine. - 2) Currently, a subroutine may not directly access or use variables or cursors of the - main statements. This may be allowed in the future. + 2) Currently, a subroutine may not directly access cursors of the main routine/block. + This may be allowed in the future. + 3) Since FB 5 subroutines may use variables and parameters from the main routine/block. + 4) Variables and parameters that are accessed by subroutines may have a small performance + penalty (even in the main routine) when being read. Notes: 1) Starting in FB 4, subroutines may be recursive or call others subroutines. diff --git a/examples/dbcrypt/CryptKeyHolder.cpp b/examples/dbcrypt/CryptKeyHolder.cpp index bc79942238..efa57df60f 100644 --- a/examples/dbcrypt/CryptKeyHolder.cpp +++ b/examples/dbcrypt/CryptKeyHolder.cpp @@ -298,7 +298,7 @@ Factory factory; } // anonymous namespace -extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* m) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* m) { master = m; IPluginManager* pluginManager = master->getPluginManager(); diff --git a/examples/dbcrypt/DbCrypt.cpp b/examples/dbcrypt/DbCrypt.cpp index fcfc1ba3f4..6b0260eb27 100644 --- a/examples/dbcrypt/DbCrypt.cpp +++ b/examples/dbcrypt/DbCrypt.cpp @@ -266,7 +266,7 @@ Factory factory; } // anonymous namespace -extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { IPluginManager* pluginManager = master->getPluginManager(); diff --git a/examples/extauth/ExtAuth.cpp b/examples/extauth/ExtAuth.cpp index 5c3c5efd4d..56926575f6 100644 --- a/examples/extauth/ExtAuth.cpp +++ b/examples/extauth/ExtAuth.cpp @@ -435,13 +435,7 @@ Factory serverFactory; } // anonymous namespace -#if defined(_WIN32) -#define FB_DLL_EXPORT __declspec(dllexport) -#else -#define FB_DLL_EXPORT -#endif - -extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* m) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* m) { master = m; IPluginManager* pluginManager = master->getPluginManager(); diff --git a/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj b/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj index f7c55009a5..d63b18cfa2 100644 --- a/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj +++ b/examples/extauth/msvc/ExtAuth_MSVC15.vcxproj @@ -105,9 +105,9 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -121,9 +121,9 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -139,9 +139,9 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -159,9 +159,9 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -182,4 +182,4 @@ - + \ No newline at end of file diff --git a/examples/extauth/msvc/KeyGen_MSVC15.vcxproj b/examples/extauth/msvc/KeyGen_MSVC15.vcxproj index 09268aed3a..6439e904a5 100644 --- a/examples/extauth/msvc/KeyGen_MSVC15.vcxproj +++ b/examples/extauth/msvc/KeyGen_MSVC15.vcxproj @@ -83,31 +83,31 @@ ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen ..\..\prebuilt\$(Platform)\$(Configuration)\bin\ ..\..\..\temp\$(PlatformName)\$(Configuration)\examples\$(ProjectName)\ - fbSampleExtAuthKeygen + fbSampleExtAuthKeygen Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -120,9 +120,9 @@ Level3 Disabled true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -137,9 +137,9 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console @@ -156,9 +156,9 @@ true true true - true + false ..\..\..\src\include\;..\..\..\extern\libtomcrypt\src\headers\ - _CRT_SECURE_NO_WARNINGS;LTM_DESC + _CRT_SECURE_NO_WARNINGS;LTM_DESC;WIN32 Console diff --git a/examples/interfaces/12.batch_isc.cpp b/examples/interfaces/12.batch_isc.cpp index db1ddd197a..09c5e90f98 100644 --- a/examples/interfaces/12.batch_isc.cpp +++ b/examples/interfaces/12.batch_isc.cpp @@ -145,7 +145,7 @@ int main() /*IProvider* prov = master->getDispatcher(); IAttachment* att = NULL;*/ IUtil* utl = master->getUtilInterface(); - IStatement* statemt = NULL; + IStatement* statement = NULL; ITransaction* tra = NULL; IBatch* batch = NULL; IBatchCompletionState* cs = NULL; @@ -185,7 +185,7 @@ int main() if (isc_dsql_prepare(st, &tr, &stmt, 0, sqlStmt1, 3, NULL)) raiseError(status, st); // and get it's interface - if (fb_get_statement_interface(st, &statemt, &stmt)) + if (fb_get_statement_interface(st, &statement, &stmt)) raiseError(status, st); // Message to store in a table @@ -202,7 +202,7 @@ int main() pb->insertInt(&status, IBatch::TAG_RECORD_COUNTS, 1); // create batch - batch = statemt->createBatch(&status, meta, + batch = statement->createBatch(&status, meta, pb->getBufferLength(&status), pb->getBuffer(&status)); // fill batch with data record by record @@ -223,8 +223,8 @@ int main() batch = NULL; // unprepare statement - statemt->release(); - statemt = NULL; + statement->release(); + statement = NULL; if (isc_dsql_free_statement(st, &stmt, DSQL_unprepare)) raiseError(status, st); @@ -237,7 +237,7 @@ int main() if (isc_dsql_prepare(st, &tr, &stmt, 0, sqlStmt2, 3, NULL)) raiseError(status, st); // and get it's interface - if (fb_get_statement_interface(st, &statemt, &stmt)) + if (fb_get_statement_interface(st, &statement, &stmt)) raiseError(status, st); // Message to store in a table @@ -255,7 +255,7 @@ int main() pb->insertInt(&status, IBatch::TAG_BLOB_POLICY, IBatch::BLOB_ID_ENGINE); // create batch - batch = statemt->createBatch(&status, meta, + batch = statement->createBatch(&status, meta, pb->getBufferLength(&status), pb->getBuffer(&status)); // create blob @@ -283,8 +283,8 @@ int main() batch = NULL; // unprepare statement - statemt->release(); - statemt = NULL; + statement->release(); + statement = NULL; if (isc_dsql_free_statement(st, &stmt, DSQL_drop)) raiseError(status, st); @@ -312,8 +312,8 @@ int main() batch->release(); if (tra) tra->release(); - if (statemt) - statemt->release(); + if (statement) + statement->release(); // close handles if not closed if (blb) diff --git a/examples/interfaces/ifaceExamples.h b/examples/interfaces/ifaceExamples.h index da349264dc..cfcb1f9dca 100644 --- a/examples/interfaces/ifaceExamples.h +++ b/examples/interfaces/ifaceExamples.h @@ -37,14 +37,6 @@ typedef int FbSampleAtomic; #include -#if defined(_WIN32) -#define FB_DLL_EXPORT __declspec(dllexport) -#elif defined(__APPLE__) -#define FB_DLL_EXPORT __attribute__((visibility("default"))) -#else -#define FB_DLL_EXPORT -#endif - using namespace Firebird; #define SAMPLES_DIALECT SQL_DIALECT_V6 diff --git a/examples/object_pascal/Readme.md b/examples/object_pascal/Readme.md index 6c30083d98..8d8cbf73c0 100644 --- a/examples/object_pascal/Readme.md +++ b/examples/object_pascal/Readme.md @@ -40,9 +40,10 @@ you would do the following to create a project from 03.select.pas: - Open select.lpr as a project - When prompted choose 'Simple Program' as the project template - Go into Project options and add the following paths: - /opt/firebird/include/Firebird +``` + /usr/include/Firebird common - +``` You can then compile and run the example through the debugger. diff --git a/examples/replication/fbSampleReplicator.cpp b/examples/replication/fbSampleReplicator.cpp index fa8f63232b..fcf59537b2 100644 --- a/examples/replication/fbSampleReplicator.cpp +++ b/examples/replication/fbSampleReplicator.cpp @@ -100,14 +100,7 @@ public: extern "C" { -#if defined(__WIN32__) - void __declspec(dllexport) FB_PLUGIN_ENTRY_POINT(IMaster* m); -#else - void FB_PLUGIN_ENTRY_POINT(IMaster* m) - __attribute__((visibility("default"))); -#endif // __WIN32__ - - void FB_PLUGIN_ENTRY_POINT(IMaster* m) + FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* m) { master = m; IPluginManager* pm = m->getPluginManager(); diff --git a/examples/udr/Triggers.cpp b/examples/udr/Triggers.cpp index 4e9bcca834..f397b9a373 100644 --- a/examples/udr/Triggers.cpp +++ b/examples/udr/Triggers.cpp @@ -234,8 +234,6 @@ FB_UDR_BEGIN_TRIGGER(replicate_persons) "select data_source from replicate_config where name = ?", SQL_DIALECT_CURRENT, NULL), status, statusVector); - const char* table = metadata->getTriggerTable(status); - // Skip the first exclamation point, separating the module name and entry point. const char* info = strchr(metadata->getEntryPoint(status), '!'); diff --git a/extern/icu/tzdata/be.zip b/extern/icu/tzdata/be.zip index 6632977cac..0044e7b97a 100644 Binary files a/extern/icu/tzdata/be.zip and b/extern/icu/tzdata/be.zip differ diff --git a/extern/icu/tzdata/le.zip b/extern/icu/tzdata/le.zip index d10fcddfc2..168e562878 100644 Binary files a/extern/icu/tzdata/le.zip and b/extern/icu/tzdata/le.zip differ diff --git a/extern/icu/tzdata/version.txt b/extern/icu/tzdata/version.txt index dac017a1e4..ca002de23b 100644 --- a/extern/icu/tzdata/version.txt +++ b/extern/icu/tzdata/version.txt @@ -1 +1 @@ -2021a4 +2022a diff --git a/extern/zlib/Readme.txt b/extern/zlib/Readme.txt index 16f388c16e..6ba7036a7a 100644 --- a/extern/zlib/Readme.txt +++ b/extern/zlib/Readme.txt @@ -4,7 +4,7 @@ architectures. The source code of zlib library was downloaded from - http://zlib.net/zlib1211.zip + http://zlib.net/zlib1212.zip It was built with MSVC17 compilers using commands specified at win32/Makefile.msc: diff --git a/extern/zlib/zlib.exe b/extern/zlib/zlib.exe index cfe5367e63..d0d734e26e 100644 Binary files a/extern/zlib/zlib.exe and b/extern/zlib/zlib.exe differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ca816e3395..fffc3d0d7b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -448,6 +448,7 @@ file(GLOB engine_src "dsql/*.cpp" "jrd/*.cpp" "jrd/extds/*.cpp" + "jrd/optimizer/*.cpp" "jrd/recsrc/*.cpp" "jrd/replication/*.cpp" "jrd/trace/*.cpp" diff --git a/src/alice/alice.h b/src/alice/alice.h index 0648f297ac..b6b87033a2 100644 --- a/src/alice/alice.h +++ b/src/alice/alice.h @@ -98,30 +98,21 @@ struct user_action -// String block: used to store a string of constant length. - -class alice_str : public pool_alloc_rpt -{ -public: - USHORT str_length; - UCHAR str_data[2]; -}; - // Transaction block: used to store info about a multi-database transaction. // Transaction Description Record struct tdr : public pool_alloc { - tdr* tdr_next; // next sub-transaction - TraNumber tdr_id; // database-specific transaction id - alice_str* tdr_fullpath; // full (possibly) remote pathname - const TEXT* tdr_filename; // filename within full pathname - alice_str* tdr_host_site; // host for transaction - alice_str* tdr_remote_site; // site for remote transaction - FB_API_HANDLE tdr_handle; // reconnected transaction handle - FB_API_HANDLE tdr_db_handle; // re-attached database handle - USHORT tdr_db_caps; // capabilities of database - USHORT tdr_state; // see flags below + tdr* tdr_next; // next sub-transaction + TraNumber tdr_id; // database-specific transaction id + Firebird::string tdr_fullpath; // full (possibly) remote pathname + Firebird::string tdr_filename; // filename + Firebird::string tdr_host_site; // host for transaction + Firebird::string tdr_remote_site; // site for remote transaction + FB_API_HANDLE tdr_handle; // reconnected transaction handle + FB_API_HANDLE tdr_db_handle; // re-attached database handle + USHORT tdr_db_caps; // capabilities of database + USHORT tdr_state; // see flags below }; // CVC: This information should match Transaction Description Record constants in acl.h diff --git a/src/alice/alice_meta.epp b/src/alice/alice_meta.epp index fc99727e19..651602fcc6 100644 --- a/src/alice/alice_meta.epp +++ b/src/alice/alice_meta.epp @@ -39,6 +39,7 @@ #include "../common/classes/UserBlob.h" #include "../alice/alice_proto.h" #include "../common/utils_proto.h" +#include "../common/isc_f_proto.h" #include @@ -54,7 +55,7 @@ DATABASE DB = STATIC FILENAME "yachts.lnk"; typedef Firebird::HalfStaticArray TextBuffer; -static alice_str* alloc_string(const TEXT**); +static void get_string(const TEXT**, Firebird::string&); static USHORT get_capabilities(ISC_STATUS*); static tdr* get_description(ISC_QUAD*); static void parse_fullpath(tdr*); @@ -242,19 +243,15 @@ void MET_set_capabilities(ISC_STATUS* user_status, tdr* trans) * Eat a string with a byte-encoded length. */ -static alice_str* alloc_string(const TEXT** ptr) +static void get_string(const TEXT** ptr, Firebird::string& str) { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); const TEXT* p = *ptr; - const USHORT length = (USHORT) *p++; - alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str; - memcpy(string->str_data, p, length); - string->str_data[length] = 0; *ptr = p + length; - return string; + str.assign(p, length); } @@ -319,8 +316,8 @@ static tdr* get_description(ISC_QUAD* blob_id) return NULL; tdr* trans = NULL; - alice_str* host_site = NULL; - alice_str* database_path = NULL; + Firebird::string host_site; + Firebird::string database_path; const TEXT* p = buffer.begin(); @@ -336,11 +333,11 @@ static tdr* get_description(ISC_QUAD* blob_id) switch (*p++) { case TDR_HOST_SITE: - host_site = alloc_string(&p); + get_string(&p, host_site); break; case TDR_DATABASE_PATH: - database_path = alloc_string(&p); + get_string(&p, database_path); break; case TDR_TRANSACTION_ID: @@ -359,7 +356,7 @@ static tdr* get_description(ISC_QUAD* blob_id) ptr->tdr_fullpath = database_path; parse_fullpath(ptr); ptr->tdr_id = id; - database_path = NULL; + database_path.clear(); break; default: @@ -382,73 +379,17 @@ static tdr* get_description(ISC_QUAD* blob_id) static void parse_fullpath(tdr* trans) { - AliceGlobals* tdgbl = AliceGlobals::getSpecific(); + Firebird::PathName filename = trans->tdr_fullpath.c_str(); + Firebird::PathName hostname; - // start at the end of the full pathname + // Find the last remote node in the path - const TEXT* p = (TEXT*) trans->tdr_fullpath->str_data; - const TEXT* const start = p; - while (*p) - p++; - const TEXT* const end = p; + while (ISC_analyze_tcp(filename, hostname)) + trans->tdr_remote_site = hostname.c_str(); - // Check for a named pipes name - \\node\path\db or //node/path/db - while (p > start && !(*p == '/' && p[-1] == '/') && !(*p == '\\' && p[-1] == '\\')) - { - --p; - } + // At this point the filename is clear from any remote nodes - if (p > start) - { - // Increment p past slash, & search forward for end of node name - p = p + 1; - const TEXT* q = p; - - while (*q && *q != '/' && *q != '\\') - q++; - if (*q) - { - trans->tdr_filename = q + 1; - - trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->getDefaultPool(), q - p + 1) alice_str; - fb_utils::copy_terminate((char*) trans->tdr_remote_site->str_data, (char*) p, q - p + 1); - } - } - else - { - p = end; - - // If not named pipes, check the other protocols - // work backwards until we find a remote protocol specifier - - - while (p >= start && (*p != '^' && *p != ':' && *p != '@')) - p--; - // dimitr: make sure that the remote path is parsed correctly - // for win32 servers, i.e. the drive separator is taken into account - if ((p - 2 >= start) && p[-2] == ':' && (p[0] == ':')) - p -= 2; - trans->tdr_filename = p + 1; - - // now find the last remote node in the chain - - while (p > start && (*p == ':' || *p == '^' || *p == '@')) - p--; - - USHORT length = 0; - for (; p >= start && (*p != '^' && *p != ':' && *p != '@'); ++length) - --p; - ++p; - - if (length) - { - trans->tdr_remote_site = FB_NEW_RPT(*tdgbl->getDefaultPool(), length + 1) alice_str; - TEXT* q = (TEXT *) trans->tdr_remote_site->str_data; - while (length--) - *q++ = *p++; - *q = 0; - } - } + trans->tdr_filename = filename.c_str(); } diff --git a/src/alice/tdr.cpp b/src/alice/tdr.cpp index 29762496d6..f7b1e89b0b 100644 --- a/src/alice/tdr.cpp +++ b/src/alice/tdr.cpp @@ -472,7 +472,7 @@ bool TDR_reconnect_multiple(FB_API_HANDLE handle, TraNumber id, const TEXT* name { if (ptr->tdr_state == TRA_limbo) { - reconnect(ptr->tdr_db_handle, ptr->tdr_id, ptr->tdr_filename, switches); + reconnect(ptr->tdr_db_handle, ptr->tdr_id, ptr->tdr_filename.c_str(), switches); } } } @@ -504,28 +504,23 @@ static void print_description(const tdr* trans) AliceGlobals* tdgbl = AliceGlobals::getSpecific(); if (!trans) - { return; - } if (!tdgbl->uSvc->isService()) - { ALICE_print(92); // msg 92: Multidatabase transaction: - } bool prepared_seen = false; for (const tdr* ptr = trans; ptr; ptr = ptr->tdr_next) { - if (ptr->tdr_host_site) + const auto host_site = ptr->tdr_host_site.nullStr(); + if (host_site) { - const char* pszHostSize = reinterpret_cast(ptr->tdr_host_site->str_data); - if (!tdgbl->uSvc->isService()) { // msg 93: Host Site: %s - ALICE_print(93, SafeArg() << pszHostSize); + ALICE_print(93, SafeArg() << host_site); } - tdgbl->uSvc->putLine(isc_spb_tra_host_site, pszHostSize); + tdgbl->uSvc->putLine(isc_spb_tra_host_site, host_site); } if (ptr->tdr_id) @@ -586,28 +581,26 @@ static void print_description(const tdr* trans) break; } - if (ptr->tdr_remote_site) + const auto remote_site = ptr->tdr_remote_site.nullStr(); + if (remote_site) { - const char* pszRemoteSite = reinterpret_cast(ptr->tdr_remote_site->str_data); - if (!tdgbl->uSvc->isService()) { // msg 101: Remote Site: %s - ALICE_print(101, SafeArg() << pszRemoteSite); + ALICE_print(101, SafeArg() << remote_site); } - tdgbl->uSvc->putLine(isc_spb_tra_remote_site, pszRemoteSite); + tdgbl->uSvc->putLine(isc_spb_tra_remote_site, remote_site); } - if (ptr->tdr_fullpath) + const auto fullpath = ptr->tdr_fullpath.nullStr(); + if (fullpath) { - const char* pszFullpath = reinterpret_cast(ptr->tdr_fullpath->str_data); - if (!tdgbl->uSvc->isService()) { // msg 102: Database Path: %s - ALICE_print(102, SafeArg() << pszFullpath); + ALICE_print(102, SafeArg() << fullpath); } - tdgbl->uSvc->putLine(isc_spb_tra_db_path, pszFullpath); + tdgbl->uSvc->putLine(isc_spb_tra_db_path, fullpath); } } @@ -649,10 +642,9 @@ static void print_description(const tdr* trans) static SINT64 ask() { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); + if (tdgbl->uSvc->isService()) - { return ~SINT64(0); - } char response[32]; SINT64 switches = 0; @@ -694,79 +686,56 @@ static SINT64 ask() static void reattach_database(tdr* trans) { ISC_STATUS_ARRAY status_vector; - char buffer[1024]; + char buffer[BUFFER_LARGE]; // sizeof(buffer) - 1 => leave space for the terminator. const char* const end = buffer + sizeof(buffer) - 1; AliceGlobals* tdgbl = AliceGlobals::getSpecific(); - ISC_get_host(buffer, sizeof(buffer)); - - if (trans->tdr_fullpath) + if (trans->tdr_fullpath.hasData()) { + Firebird::string hostname; + ISC_get_host(hostname); + // if this is being run from the same host, // try to reconnect using the same pathname - if (!strcmp(buffer, reinterpret_cast(trans->tdr_host_site->str_data))) + if (trans->tdr_host_site == hostname) { - if (TDR_attach_database(status_vector, trans, - reinterpret_cast(trans->tdr_fullpath->str_data))) - { + if (TDR_attach_database(status_vector, trans, trans->tdr_fullpath.c_str())) return; - } } - else if (trans->tdr_host_site) + else if (trans->tdr_host_site.hasData()) { // try going through the previous host with all available // protocols, using chaining to try the same method of // attachment originally used from that host - char* p = buffer; - const UCHAR* q = trans->tdr_host_site->str_data; - while (*q && p < end) - *p++ = *q++; - *p++ = ':'; - q = trans->tdr_fullpath->str_data; - while (*q && p < end) - *p++ = *q++; - *p = 0; - if (TDR_attach_database(status_vector, trans, buffer)) - { + const Firebird::string pathname = trans->tdr_host_site + ':' + trans->tdr_fullpath; + if (TDR_attach_database(status_vector, trans, pathname.c_str())) return; - } } // attaching using the old method didn't work; // try attaching to the remote node directly - if (trans->tdr_remote_site) + if (trans->tdr_remote_site.hasData()) { - char* p = buffer; - const UCHAR* q = trans->tdr_remote_site->str_data; - while (*q && p < end) - *p++ = *q++; - *p++ = ':'; - q = reinterpret_cast(trans->tdr_filename); - while (*q && p < end) - *p++ = *q++; - *p = 0; - if (TDR_attach_database (status_vector, trans, buffer)) - { + const Firebird::string pathname = trans->tdr_remote_site + ':' + trans->tdr_filename; + if (TDR_attach_database(status_vector, trans, pathname.c_str())) return; - } } } + // we have failed to reattach; notify the user // and let them try to succeed where we have failed ALICE_print(86, SafeArg() << trans->tdr_id); // msg 86: Could not reattach to database for transaction %ld. - ALICE_print(87, SafeArg() << (trans->tdr_fullpath ? (char*)(trans->tdr_fullpath->str_data) : "is unknown")); + ALICE_print(87, SafeArg() << (trans->tdr_fullpath.hasData() ? trans->tdr_fullpath.c_str() : "unknown")); // msg 87: Original path: %s if (tdgbl->uSvc->isService()) - { ALICE_exit(FINI_ERROR, tdgbl); - } for (;;) { @@ -782,12 +751,8 @@ static void reattach_database(tdr* trans) ++p; if (TDR_attach_database(status_vector, trans, p)) { - const size_t p_len = strlen(p); - alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), p_len + 1) alice_str; - strcpy(reinterpret_cast(string->str_data), p); - string->str_length = static_cast(p_len); - trans->tdr_fullpath = string; - trans->tdr_filename = (TEXT *) string->str_data; + trans->tdr_fullpath.assign(p); + trans->tdr_filename = trans->tdr_fullpath; return; } ALICE_print(89); // msg 89: Attach unsuccessful. diff --git a/src/auth/AuthDbg.cpp b/src/auth/AuthDbg.cpp index 6f2110e629..386f9f5e23 100644 --- a/src/auth/AuthDbg.cpp +++ b/src/auth/AuthDbg.cpp @@ -38,7 +38,7 @@ static Firebird::SimpleFactory clientFactory; static Firebird::SimpleFactory serverFactory; -extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); diff --git a/src/auth/AuthDbg.h b/src/auth/AuthDbg.h index ab3c49e233..b556bd537f 100644 --- a/src/auth/AuthDbg.h +++ b/src/auth/AuthDbg.h @@ -46,7 +46,7 @@ namespace Auth { // The idea of debug plugin is to send some data from server to client, // modify them on client and return result (which becomes login name) to the server -class DebugServer FB_FINAL : +class DebugServer final : public Firebird::StdPlugin > { public: @@ -61,7 +61,7 @@ private: Firebird::RefPtr config; }; -class DebugClient FB_FINAL : +class DebugClient final : public Firebird::StdPlugin > { public: diff --git a/src/auth/SecDbCache.h b/src/auth/SecDbCache.h index c29037f463..1937e4a7ca 100644 --- a/src/auth/SecDbCache.h +++ b/src/auth/SecDbCache.h @@ -55,7 +55,7 @@ public: class PluginDatabases; -class CachedSecurityDatabase FB_FINAL +class CachedSecurityDatabase final : public Firebird::RefCntIface > { public: diff --git a/src/auth/SecureRemotePassword/client/SrpClient.cpp b/src/auth/SecureRemotePassword/client/SrpClient.cpp index 10da02f5ff..3d88446c51 100644 --- a/src/auth/SecureRemotePassword/client/SrpClient.cpp +++ b/src/auth/SecureRemotePassword/client/SrpClient.cpp @@ -59,7 +59,7 @@ protected: virtual RemotePassword* remotePasswordFactory() = 0; }; -template class SrpClientImpl FB_FINAL : public SrpClient +template class SrpClientImpl final : public SrpClient { public: explicit SrpClientImpl(IPluginConfig* ipc) diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index b2150ac155..db795d0312 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -38,14 +38,6 @@ #include "../common/classes/auto.h" #include "../common/classes/ParsedList.h" -#ifndef FB_EXPORTED -#if defined(DARWIN) -#define FB_EXPORTED __attribute__((visibility("default"))) -#else -#define FB_EXPORTED -#endif // OS choice (DARWIN) -#endif // FB_EXPORTED - namespace { const unsigned int SZ_LOGIN = 31; @@ -60,7 +52,7 @@ Firebird::GlobalPtr keys; namespace Auth { -class SrpManagement FB_FINAL : public Firebird::StdPlugin > +class SrpManagement final : public Firebird::StdPlugin > { public: explicit SrpManagement(Firebird::IPluginConfig* par) @@ -540,12 +532,9 @@ public: assignField(active, user->active()); setField(login, user->userName()); - int count = 0; - checkCount(status, stmt, &count, isc_info_req_update_count); stmt->execute(status, mainTra, up.getMetadata(), up.getBuffer(), NULL, NULL); check(status); - - if (!checkCount(status, stmt, &count, isc_info_req_update_count)) + if (recordsCount(status, stmt, isc_info_req_update_count) != 1) { stmt->release(); return GsecMsg22; @@ -581,12 +570,9 @@ public: Varfield login(dl); setField(login, user->userName()); - int count = 0; - checkCount(status, stmt, &count, isc_info_req_delete_count); stmt->execute(status, mainTra, dl.getMetadata(), dl.getBuffer(), NULL, NULL); check(status); - - if (!checkCount(status, stmt, &count, isc_info_req_delete_count)) + if (recordsCount(status, stmt, isc_info_req_delete_count) != 1) { stmt->release(); return GsecMsg22; @@ -739,7 +725,7 @@ private: RemotePasswordImpl server; - bool checkCount(Firebird::CheckStatusWrapper* status, Firebird::IStatement* stmt, int* count, UCHAR item) + int recordsCount(Firebird::CheckStatusWrapper* status, Firebird::IStatement* stmt, UCHAR item) { UCHAR buffer[33]; const UCHAR count_info[] = { isc_info_sql_records }; @@ -755,17 +741,12 @@ private: const SSHORT len = gds__vax_integer(p, 2); p += 2; if (count_is == item) - { - int newCount = gds__vax_integer(p, len); - int oldCount = *count; - *count = newCount; - return newCount == oldCount + 1; - } + return gds__vax_integer(p, len); p += len; } } - return false; + return 0; } static void check(Firebird::CheckStatusWrapper* status) @@ -986,7 +967,7 @@ static Firebird::SimpleFactory factory; } // namespace Auth -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); Firebird::PluginManagerInterfacePtr()->registerPluginFactory(Firebird::IPluginManager::TYPE_AUTH_USER_MANAGEMENT, Auth::RemotePassword::plugName, &Auth::factory); diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp index f8a7474f0b..3aafc985c3 100644 --- a/src/auth/SecureRemotePassword/server/SrpServer.cpp +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -244,7 +244,7 @@ private: }; -template class SrpServerImpl FB_FINAL : public SrpServer +template class SrpServerImpl final : public SrpServer { public: explicit SrpServerImpl(IPluginConfig* ipc) diff --git a/src/auth/SecurityDatabase/LegacyClient.h b/src/auth/SecurityDatabase/LegacyClient.h index 24c5843b27..c271f94520 100644 --- a/src/auth/SecurityDatabase/LegacyClient.h +++ b/src/auth/SecurityDatabase/LegacyClient.h @@ -35,7 +35,7 @@ namespace Auth { // Required to stop analyzing rest of plugins before first roundtrip to server // if legacy login is present in DPB -class SecurityDatabaseClient FB_FINAL : +class SecurityDatabaseClient final : public Firebird::StdPlugin > { public: diff --git a/src/auth/SecurityDatabase/LegacyManagement.epp b/src/auth/SecurityDatabase/LegacyManagement.epp index b60aeed564..80cbc070dc 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.epp +++ b/src/auth/SecurityDatabase/LegacyManagement.epp @@ -762,7 +762,7 @@ int SecurityDatabaseManagement::execute(Firebird::CheckStatusWrapper* st, Firebi // register plugin static Firebird::SimpleFactory factory; -extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); Firebird::PluginManagerInterfacePtr()->registerPluginFactory( diff --git a/src/auth/SecurityDatabase/LegacyManagement.h b/src/auth/SecurityDatabase/LegacyManagement.h index 5d2a309f7a..65136edb6a 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.h +++ b/src/auth/SecurityDatabase/LegacyManagement.h @@ -33,7 +33,7 @@ namespace Auth { -class SecurityDatabaseManagement FB_FINAL : +class SecurityDatabaseManagement final : public Firebird::StdPlugin > { public: diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 23f9b471c2..03715e9615 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -125,7 +125,7 @@ namespace Auth { GlobalPtr instances; -class SecurityDatabaseServer FB_FINAL : +class SecurityDatabaseServer final : public StdPlugin > { public: @@ -411,7 +411,7 @@ void registerLegacyServer(IPluginManager* iPlugin) #ifdef PLUG_MODULE -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { CachedMasterInterface::set(master); diff --git a/src/burp/backup.epp b/src/burp/backup.epp index e2bf9603fd..72421bc4d7 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -1140,7 +1140,7 @@ void put_asciz( const att_type attribute, const TEXT* string) // We can't honor operating systems that allow longer file names. if (len >= MAX_FILE_NAME_SIZE) { - BURP_print(true, 343, SafeArg() << int(attribute) << "put_asciz()" << (MAX_FILE_NAME_SIZE - 1)); + BURP_print(false, 343, SafeArg() << int(attribute) << "put_asciz()" << (MAX_FILE_NAME_SIZE - 1)); // msg 343: text for attribute @1 is too large in @2, truncating to @3 bytes len = MAX_FILE_NAME_SIZE - 1; } @@ -1961,6 +1961,7 @@ void put_boolean(att_type attribute, const FB_BOOLEAN value) BurpGlobals* tdgbl = BurpGlobals::getSpecific(); put(tdgbl, attribute); + put(tdgbl, (UCHAR) 1); put(tdgbl, value ? 1u : 0u); } diff --git a/src/burp/burp.h b/src/burp/burp.h index eb2ea3ac15..11b2581d4f 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -252,8 +252,9 @@ enum att_type { att_SQL_dialect, // SQL dialect that it speaks att_db_read_only, // Is the database ReadOnly? att_database_linger, // Disconnection timeout - att_database_sql_security,// default sql security value + att_database_sql_security_deprecated, // can be removed later att_replica_mode, // replica mode + att_database_sql_security, // default sql security value // Relation attributes @@ -275,6 +276,7 @@ enum att_type { att_relation_flags, att_relation_ext_file_name, // name of file for external tables att_relation_type, + att_relation_sql_security_deprecated, // can be removed later att_relation_sql_security, // Field attributes (used for both global and local fields) @@ -409,6 +411,7 @@ enum att_type { att_trig_engine_name, att_trig_entrypoint, att_trig_type2, + att_trig_sql_security_deprecated, // can be removed later att_trig_sql_security, // Function attributes @@ -433,6 +436,7 @@ enum att_type { att_function_owner_name, att_function_legacy_flag, att_function_deterministic_flag, + att_function_sql_security_deprecated, // can be removed later att_function_sql_security, // Function argument attributes @@ -529,6 +533,7 @@ enum att_type { att_procedure_entrypoint, att_procedure_package_name, att_procedure_private_flag, + att_procedure_sql_security_deprecated, // can be removed later att_procedure_sql_security, // Stored procedure parameter attributes @@ -630,6 +635,7 @@ enum att_type { att_package_security_class, att_package_owner_name, att_package_description, + att_package_sql_security_deprecated, // can be removed later att_package_sql_security, // Database creators diff --git a/src/burp/mvol.cpp b/src/burp/mvol.cpp index a89744b001..7557d40bd8 100644 --- a/src/burp/mvol.cpp +++ b/src/burp/mvol.cpp @@ -134,7 +134,7 @@ static ULONG unzip_read_block(BurpGlobals*, UCHAR*, FB_SIZE_T); // Portion of data passed to crypt plugin const ULONG CRYPT_STEP = 256; -class DbInfo FB_FINAL : public Firebird::RefCntIface > +class DbInfo final : public Firebird::RefCntIface > { public: DbInfo(BurpGlobals* bg) diff --git a/src/burp/restore.epp b/src/burp/restore.epp index b09b9e3018..b43140d076 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -192,8 +192,13 @@ static inline UCHAR get(BurpGlobals* tdgbl) return tdgbl->get(); } -static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl) +static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl, bool deprecated) { + if (!deprecated) + { + const UCHAR length = get(tdgbl); + fb_assert(length == 1); + } return get(tdgbl) ? FB_TRUE : FB_FALSE; } @@ -5142,10 +5147,11 @@ bool get_function(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 89); break; + case att_function_sql_security_deprecated: case att_function_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_function_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -5274,9 +5280,10 @@ bool get_function(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 89); break; + case att_function_sql_security_deprecated: case att_function_sql_security: if (tdgbl->RESTORE_format >= 11) - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_function_sql_security_deprecated); else bad_attribute(scan_next_attr, attribute, 89); break; @@ -7417,10 +7424,11 @@ bool get_package(BurpGlobals* tdgbl) X.RDB$DESCRIPTION.NULL = FALSE; break; + case att_package_sql_security_deprecated: case att_package_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_package_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -7634,10 +7642,11 @@ bool get_procedure(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 290); break; + case att_procedure_sql_security_deprecated: case att_procedure_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_procedure_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -7759,9 +7768,10 @@ bool get_procedure(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 290); break; + case att_procedure_sql_security_deprecated: case att_procedure_sql_security: if (tdgbl->RESTORE_format >= 11) - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_procedure_sql_security_deprecated); else bad_attribute(scan_next_attr, attribute, 290); break; @@ -8443,9 +8453,10 @@ bool get_relation(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 111); break; + case att_relation_sql_security_deprecated: case att_relation_sql_security: sql_security_null = false; - sql_security = get_boolean(tdgbl); + sql_security = get_boolean(tdgbl, attribute == att_relation_sql_security_deprecated); break; default: @@ -9784,10 +9795,11 @@ bool get_trigger(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 134); break; + case att_trig_sql_security_deprecated: case att_trig_sql_security: if (tdgbl->RESTORE_format >= 11) { - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_trig_sql_security_deprecated); X.RDB$SQL_SECURITY.NULL = FALSE; } else @@ -9930,9 +9942,10 @@ bool get_trigger(BurpGlobals* tdgbl) bad_attribute(scan_next_attr, attribute, 134); break; + case att_trig_sql_security_deprecated: case att_trig_sql_security: if (tdgbl->RESTORE_format >= 11) - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_trig_sql_security_deprecated); else bad_attribute(scan_next_attr, attribute, 134); break; @@ -10226,6 +10239,8 @@ bool get_user_privilege(BurpGlobals* tdgbl) case att_priv_obj_type: flags |= USER_PRIV_OBJECT_TYPE; object_type = (USHORT) get_int32(tdgbl); + if ( (tdgbl->RESTORE_format < 11) && (object_type > 19) ) // FB 4 has a shift :( + object_type++; break; default: @@ -10297,9 +10312,6 @@ bool get_user_privilege(BurpGlobals* tdgbl) } break; - case obj_database: - break; - default: exists = true; break; @@ -11009,6 +11021,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file } break; + case att_database_sql_security_deprecated: case att_database_sql_security: if (tdgbl->RESTORE_format >= 11) { @@ -11017,7 +11030,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file FOR (REQUEST_HANDLE req_handle5) X IN RDB$DATABASE MODIFY X USING - X.RDB$SQL_SECURITY = get_boolean(tdgbl); + X.RDB$SQL_SECURITY = get_boolean(tdgbl, attribute == att_database_sql_security_deprecated); END_MODIFY; ON_ERROR general_on_error(); @@ -11028,7 +11041,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file END_ERROR; } else - get_boolean(tdgbl); + get_boolean(tdgbl, attribute == att_database_sql_security_deprecated); } else { diff --git a/src/common/IntlUtil.cpp b/src/common/IntlUtil.cpp index cbe32c5a04..f1207c22fd 100644 --- a/src/common/IntlUtil.cpp +++ b/src/common/IntlUtil.cpp @@ -664,7 +664,7 @@ bool IntlUtil::readOneChar(Jrd::CharSet* cs, const UCHAR** s, const UCHAR* end, } -// Transform ICU-VERSION attribute (given by the user) in COLL-VERSION (to be stored). +// Add COLL-VERSION attribute. bool IntlUtil::setupIcuAttributes(charset* cs, const string& specificAttributes, const string& configInfo, string& newSpecificAttributes) { @@ -681,10 +681,19 @@ bool IntlUtil::setupIcuAttributes(charset* cs, const string& specificAttributes, map.get("ICU-VERSION", icuVersion); string collVersion; - if (!UnicodeUtil::getCollVersion(icuVersion, configInfo, collVersion)) + auto icu = UnicodeUtil::getCollVersion(icuVersion, configInfo, collVersion); + + if (!icu) return false; - map.remove("ICU-VERSION"); + if (icuVersion.isEmpty()) + { + int majorVersion, minorVersion; + UnicodeUtil::getICUVersion(icu, majorVersion, minorVersion); + icuVersion.printf("%d.%d", majorVersion, minorVersion); + map.put("ICU-VERSION", icuVersion); + } + map.remove("COLL-VERSION"); if (collVersion.hasData()) @@ -781,16 +790,6 @@ bool IntlUtil::readAttributeChar(Jrd::CharSet* cs, const UCHAR** s, const UCHAR* } -void IntlUtil::getDefaultCollationAttributes(UCharBuffer& collAttributes, charset& cs) -{ - string attributes("ICU-VERSION="); - attributes += Jrd::UnicodeUtil::getDefaultIcuVersion(); - setupIcuAttributes(&cs, attributes, "", attributes); - - collAttributes.push(reinterpret_cast(attributes.c_str()), attributes.length()); -} - - static void unicodeDestroy(texttype* tt) { delete[] const_cast(tt->texttype_name); diff --git a/src/common/IntlUtil.h b/src/common/IntlUtil.h index d7b010ef01..a8471dc13e 100644 --- a/src/common/IntlUtil.h +++ b/src/common/IntlUtil.h @@ -94,7 +94,6 @@ public: static bool setupIcuAttributes(charset* cs, const string& specificAttributes, const string& configInfo, string& newSpecificAttributes); - static void getDefaultCollationAttributes(UCharBuffer& collAttributes, charset& cs); private: static string escapeAttribute(Jrd::CharSet* cs, const string& s); diff --git a/src/common/MsgMetadata.h b/src/common/MsgMetadata.h index b194d6c433..f39307764e 100644 --- a/src/common/MsgMetadata.h +++ b/src/common/MsgMetadata.h @@ -310,7 +310,7 @@ public: RefPtr attachment; }; -class MetadataBuilder FB_FINAL : +class MetadataBuilder final : public RefCntIface > { public: diff --git a/src/common/TimeZones.h b/src/common/TimeZones.h index e7d4353b83..3754b8d4d6 100644 --- a/src/common/TimeZones.h +++ b/src/common/TimeZones.h @@ -1,6 +1,6 @@ // The content of this file is generated with help of update-ids utility Do not edit. -static const char* BUILTIN_TIME_ZONE_VERSION = "2021a4"; +static const char* BUILTIN_TIME_ZONE_VERSION = "2022a"; // Do not change order of items in this array! The index corresponds to a TimeZone ID, which must be fixed! static const char* BUILTIN_TIME_ZONE_LIST[] = { diff --git a/src/common/classes/BatchCompletionState.h b/src/common/classes/BatchCompletionState.h index ab29db0959..7a6a51a953 100644 --- a/src/common/classes/BatchCompletionState.h +++ b/src/common/classes/BatchCompletionState.h @@ -34,7 +34,7 @@ namespace Firebird { virtual void transliterate(IStatus* status) = 0; }; - class BatchCompletionState FB_FINAL : + class BatchCompletionState final : public DisposeIface > { public: diff --git a/src/common/classes/DoublyLinkedList.h b/src/common/classes/DoublyLinkedList.h new file mode 100644 index 0000000000..18bd8897c7 --- /dev/null +++ b/src/common/classes/DoublyLinkedList.h @@ -0,0 +1,149 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2022 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef CLASSES_DOUBLY_LINKED_LIST_H +#define CLASSES_DOUBLY_LINKED_LIST_H + +#include "../common/classes/alloc.h" +#include +#include + + +namespace Firebird +{ + +template +class DoublyLinkedList +{ +private: + using StdList = std::list>; + +public: + using Iterator = typename StdList::iterator; + using ConstIterator = typename StdList::const_iterator; + +public: + explicit DoublyLinkedList(MemoryPool& p) + : stdList(p) + { + } + +public: + constexpr T& front() noexcept + { + return stdList.front(); + } + + constexpr const T& front() const noexcept + { + return stdList.front(); + } + + constexpr T& back() noexcept + { + return stdList.back(); + } + + constexpr const T& back() const noexcept + { + return stdList.back(); + } + + constexpr Iterator begin() noexcept + { + return stdList.begin(); + } + + constexpr ConstIterator begin() const noexcept + { + return stdList.begin(); + } + + constexpr ConstIterator cbegin() const noexcept + { + return stdList.cbegin(); + } + + constexpr Iterator end() noexcept + { + return stdList.end(); + } + + constexpr ConstIterator end() const noexcept + { + return stdList.end(); + } + + constexpr ConstIterator cend() const noexcept + { + return stdList.cend(); + } + + constexpr bool isEmpty() const noexcept + { + return stdList.empty(); + } + + constexpr void clear() noexcept + { + stdList.clear(); + } + + constexpr void erase(Iterator pos) + { + stdList.erase(pos); + } + + constexpr void erase(ConstIterator pos) + { + stdList.erase(pos); + } + + constexpr void pushBack(const T& value) + { + stdList.push_back(value); + } + + constexpr void pushBack(T&& value) + { + stdList.push_back(std::move(value)); + } + + constexpr void splice(ConstIterator pos, DoublyLinkedList& other, ConstIterator it) + { + fb_assert(stdList.get_allocator() == other.stdList.get_allocator()); + stdList.splice(pos, other.stdList, it); + } + + constexpr void splice(ConstIterator pos, DoublyLinkedList&& other, ConstIterator it) + { + fb_assert(stdList.get_allocator() == other.stdList.get_allocator()); + stdList.splice(pos, std::move(other.stdList), it); + } + +private: + StdList stdList; +}; + +} // namespace Firebird + +#endif // CLASSES_DOUBLY_LINKED_LIST_H diff --git a/src/common/classes/Hash.h b/src/common/classes/Hash.h index a4fa774643..dbd22f02dd 100644 --- a/src/common/classes/Hash.h +++ b/src/common/classes/Hash.h @@ -355,7 +355,7 @@ namespace Firebird virtual void finish(dsc& result) = 0; }; - class WeakHashContext FB_FINAL : public HashContext + class WeakHashContext final : public HashContext { public: virtual void update(const void* data, FB_SIZE_T length); @@ -389,31 +389,31 @@ namespace Firebird UCharBuffer buffer; }; - class Md5HashContext FB_FINAL : public LibTomCryptHashContext + class Md5HashContext final : public LibTomCryptHashContext { public: Md5HashContext(MemoryPool& pool); }; - class Sha1HashContext FB_FINAL : public LibTomCryptHashContext + class Sha1HashContext final : public LibTomCryptHashContext { public: Sha1HashContext(MemoryPool& pool); }; - class Sha256HashContext FB_FINAL : public LibTomCryptHashContext + class Sha256HashContext final : public LibTomCryptHashContext { public: Sha256HashContext(MemoryPool& pool); }; - class Sha512HashContext FB_FINAL : public LibTomCryptHashContext + class Sha512HashContext final : public LibTomCryptHashContext { public: Sha512HashContext(MemoryPool& pool); }; - class Crc32HashContext FB_FINAL : public HashContext + class Crc32HashContext final : public HashContext { public: Crc32HashContext(MemoryPool& pool); diff --git a/src/common/classes/ImplementHelper.h b/src/common/classes/ImplementHelper.h index 474abc918f..5da017d1b8 100644 --- a/src/common/classes/ImplementHelper.h +++ b/src/common/classes/ImplementHelper.h @@ -293,7 +293,7 @@ public: // when yvalve is starting fb_shutdown(). This causes almost unavoidable segfault. // To avoid it this class is added - it detects spontaneous (not by PluginManager) // module unload and notifies PluginManager about this said fact. -class UnloadDetectorHelper FB_FINAL : +class UnloadDetectorHelper final : public VersionedIface > { public: diff --git a/src/common/classes/alloc.cpp b/src/common/classes/alloc.cpp index 86c5a64ec9..efe4b49b2a 100644 --- a/src/common/classes/alloc.cpp +++ b/src/common/classes/alloc.cpp @@ -1850,6 +1850,11 @@ public: // Create memory pool instance static MemPool* createPool(MemPool* parent, MemoryStats& stats); + MemoryStats& getStatsGroup() noexcept + { + return *stats; + } + // Set statistics group for pool. Usage counters will be decremented from // previously set group and added to new void setStatsGroup(MemoryStats& stats) noexcept; @@ -2262,6 +2267,11 @@ void MemPool::setStatsGroup(MemoryStats& newStats) noexcept stats->increment_usage(sav_used_memory); } +MemoryStats& MemoryPool::getStatsGroup() noexcept +{ + return pool->getStatsGroup(); +} + void MemoryPool::setStatsGroup(MemoryStats& newStats) noexcept { pool->setStatsGroup(newStats); diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h index cf52648855..bc25095170 100644 --- a/src/common/classes/alloc.h +++ b/src/common/classes/alloc.h @@ -57,6 +57,7 @@ #endif #include +#include #ifdef DEBUG_GDS_ALLOC #define FB_NEW new(*getDefaultMemoryPool(), __FILE__, __LINE__) @@ -212,6 +213,8 @@ public: // Get context pool for current thread of execution static MemoryPool* getContextPool(); + MemoryStats& getStatsGroup() noexcept; + // Set statistics group for pool. Usage counters will be decremented from // previously set group and added to new void setStatsGroup(MemoryStats& stats) noexcept; @@ -456,7 +459,166 @@ namespace Firebird typedef AutoPtr AutoMemoryPool; + template + class PoolAllocator + { + template friend class PoolAllocator; + + public: + using value_type = T; + using size_type = size_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using void_pointer = void* ; + using const_void_pointer = const void*; + using difference_type = std::ptrdiff_t; + using is_always_equal = std::true_type; + + template + struct rebind + { + typedef PoolAllocator other; + }; + + public: + PoolAllocator(MemoryPool& aPool) noexcept + : pool(aPool) + {} + + PoolAllocator(const PoolAllocator& o) noexcept + : pool(o.pool) + {} + + template + PoolAllocator(const PoolAllocator& o) noexcept + : pool(o.pool) + {} + + ~PoolAllocator() noexcept + {} + + public: + constexpr pointer allocate(size_type n, const void* hint = nullptr) + { + return static_cast(pool.allocate(n * sizeof(T) ALLOC_ARGS)); + } + + constexpr void deallocate(pointer p, size_type n) + { + pool.deallocate(p); + } + + constexpr size_type max_size() const noexcept + { + return size_t(-1) / sizeof(T); + } + + /* C++17 + template + constexpr void construct(U* ptr, Args&&... args) + { + if constexpr (std::is_constructible::value) + new ((void*) ptr) U(pool, std::forward(args)...); + else + new ((void*) ptr) U(std::forward(args)...); + } + */ + + template < + typename U, + typename... Args, + std::enable_if_t::value, bool> = true + > + constexpr void construct(U* ptr, Args&&... args) + { + new ((void*) ptr) U(pool, std::forward(args)...); + } + + template < + typename U, + typename... Args, + std::enable_if_t::value, bool> = true + > + constexpr void construct(U* ptr, Args&&... args) + { + new ((void*) ptr) U(std::forward(args)...); + } + + template + constexpr void destroy(U* ptr) + { + ptr->~U(); + } + + constexpr bool operator==(const PoolAllocator& o) const noexcept + { + return &pool == &o.pool; + } + + constexpr bool operator!=(const PoolAllocator& o) const noexcept + { + return &pool != &o.pool; + } + + private: + MemoryPool& pool; + }; } // namespace Firebird +template +struct std::allocator_traits> +{ + using Alloc = Firebird::PoolAllocator; + + using allocator_type = Alloc; + using value_type = typename Alloc::value_type; + using pointer = typename Alloc::pointer; + using const_pointer = typename Alloc::const_pointer; + using void_pointer = typename Alloc::void_pointer; + using const_void_pointer = typename Alloc::const_void_pointer; + using size_type = typename Alloc::size_type; + using difference_type = typename Alloc::difference_type; + using reference = value_type&; + using const_reference = const value_type&; + + using is_always_equal = typename Alloc::is_always_equal; + + template + using rebind_alloc = typename Alloc::template rebind::other; + + template + using rebind_traits = allocator_traits>; + + static constexpr pointer allocate(Alloc& alloc, size_type size) + { + return alloc.allocate(size); + } + + static constexpr void deallocate(Alloc& alloc, pointer ptr, size_type size) + { + alloc.deallocate(ptr, size); + } + + template + static constexpr void construct(Alloc& alloc, T* ptr, Args&&... args) + { + alloc.construct(ptr, std::forward(args)...); + } + + template + static constexpr void destroy(Alloc& alloc, T* ptr) + { + alloc.destroy(ptr); + } + + static constexpr size_type max_size(const Alloc& alloc) noexcept + { + return alloc.max_size(); + } +}; + + #endif // CLASSES_ALLOC_H diff --git a/src/common/classes/array.h b/src/common/classes/array.h index 5121bde6ed..b9e95730e5 100644 --- a/src/common/classes/array.h +++ b/src/common/classes/array.h @@ -110,6 +110,12 @@ public: ensureCapacity(InitialCapacity); } + Array(const T* items, const size_type itemsCount) + : Storage(), count(0), capacity(this->getStorageSize()), data(this->getStorage()) + { + add(items, itemsCount); + } + Array(const Array& source) : Storage(), count(0), capacity(this->getStorageSize()), data(this->getStorage()) { diff --git a/src/common/classes/objects_array.h b/src/common/classes/objects_array.h index d946b87976..ed9f27c050 100644 --- a/src/common/classes/objects_array.h +++ b/src/common/classes/objects_array.h @@ -483,6 +483,13 @@ namespace Firebird ObjectCmp> >() { } + explicit SortedObjectsArray(MemoryPool& p, const SortedObjectsArray& o) : + ObjectsArray >(p, o) + { + } + bool find(const ObjectKey& item, size_type& pos) const { const ObjectKey* const pItem = &item; diff --git a/src/common/common.h b/src/common/common.h index c5d3079fc1..f58b065dcb 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -230,6 +230,10 @@ #ifdef ARM #define FB_CPU CpuArm #endif /* ARM */ +#ifdef ARM64 +#define DARWIN64 +#define FB_CPU CpuArm64 +#endif /* ARM64 */ #ifdef __ppc__ #define powerpc #define FB_CPU CpuPowerPc @@ -252,8 +256,6 @@ #define API_ROUTINE __attribute__((visibility("default"))) #define API_ROUTINE_VARARG API_ROUTINE -#define INTERNAL_API_ROUTINE API_ROUTINE -#define FB_EXPORTED __attribute__((visibility("default"))) #define O_DIRECT F_NOCACHE #endif /* Darwin Platforms */ @@ -603,10 +605,6 @@ extern "C" int remove(const char* path); #define CLIB_ROUTINE #endif -#ifndef FB_EXPORTED -#define FB_EXPORTED -#endif - #ifdef HAS_NOEXCEPT #define NOEXCEPT noexcept #define NOEXCEPT_ARG(X) noexcept((X)) @@ -886,21 +884,6 @@ void GDS_breakpoint(int); #define FB_CONST64(a) (a##LL) #endif -// Check for "final" keyword support -#ifdef CPP_11 -#define FB_FINAL final -#else -#ifdef __GNUC__ -#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || (__GNUC__ >= 5)) -#define FB_FINAL __final -#endif -#endif -// Please add support for other compilers here -#ifndef FB_FINAL -#define FB_FINAL -#endif -#endif - #define FB_UNUSED(value) do { if (value) {} } while (false) #define FB_UNUSED_VAR(value) (void) value diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 6e17cf9b50..1e43ea4769 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -410,6 +410,8 @@ void Config::checkValues() checkIntForHiBound(KEY_TIP_CACHE_BLOCK_SIZE, MAX_ULONG, true); checkIntForLoBound(KEY_INLINE_SORT_THRESHOLD, 0, true); + + checkIntForLoBound(KEY_MAX_STATEMENT_CACHE_SIZE, 0, true); } diff --git a/src/common/config/config.h b/src/common/config/config.h index 2550cb9b55..f874213c32 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -65,9 +65,9 @@ type getParameterName() const; form, for world-wide (global) parameters static type getParameterName(); - should be used. Also, for world-wide parameters, values of default + should be used. Also, for world-wide parameters, values of default config instance (see getDefaultConfig()) should be used. - 5. Macros CONFIG_GET_GLOBAL_XXX and CONFIG_GET_PER_DB_XXX helps to + 5. Macros CONFIG_GET_GLOBAL_XXX and CONFIG_GET_PER_DB_XXX helps to declare and implement trivial getXXX functions and to enforce rule (4). **/ @@ -137,7 +137,6 @@ enum ConfigKey KEY_DEADLOCK_TIMEOUT, KEY_REMOTE_SERVICE_NAME, KEY_REMOTE_SERVICE_PORT, - KEY_REMOTE_PIPE_NAME, KEY_IPC_NAME, KEY_MAX_UNFLUSHED_WRITES, KEY_MAX_UNFLUSHED_WRITE_TIME, @@ -177,6 +176,7 @@ enum ConfigKey KEY_ENCRYPT_SECURITY_DATABASE, KEY_STMT_TIMEOUT, KEY_CONN_IDLE_TIMEOUT, + KEY_ON_DISCONNECT_TRIG_TIMEOUT, KEY_CLIENT_BATCH_BUFFER, KEY_OUTPUT_REDIRECTION_FILE, KEY_EXT_CONN_POOL_SIZE, @@ -189,6 +189,7 @@ enum ConfigKey KEY_USE_FILESYSTEM_CACHE, KEY_INLINE_SORT_THRESHOLD, KEY_TEMP_PAGESPACE_DIR, + KEY_MAX_STATEMENT_CACHE_SIZE, MAX_CONFIG_KEY // keep it last }; @@ -232,7 +233,6 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_INTEGER, "DeadlockTimeout", false, 10}, // seconds {TYPE_STRING, "RemoteServiceName", false, FB_SERVICE_NAME}, {TYPE_INTEGER, "RemoteServicePort", false, 0}, - {TYPE_STRING, "RemotePipeName", false, FB_PIPE_NAME}, {TYPE_STRING, "IpcName", false, FB_IPC_NAME}, #ifdef WIN_NT {TYPE_INTEGER, "MaxUnflushedWrites", false, 100}, @@ -285,6 +285,7 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_BOOLEAN, "AllowEncryptedSecurityDatabase", false, false}, {TYPE_INTEGER, "StatementTimeout", false, 0}, {TYPE_INTEGER, "ConnectionIdleTimeout", false, 0}, + {TYPE_INTEGER, "OnDisconnectTriggerTimeout", false, 180}, {TYPE_INTEGER, "ClientBatchBuffer", false, 128 * 1024}, #ifdef DEV_BUILD {TYPE_STRING, "OutputRedirectionFile", true, "-"}, @@ -304,7 +305,8 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_STRING, "DataTypeCompatibility", false, nullptr}, {TYPE_BOOLEAN, "UseFileSystemCache", false, true}, {TYPE_INTEGER, "InlineSortThreshold", false, 1000}, // bytes - {TYPE_STRING, "TempTableDirectory", false, ""} + {TYPE_STRING, "TempTableDirectory", false, ""}, + {TYPE_INTEGER, "MaxStatementCacheSize", false, 2 * 1048576} // bytes }; @@ -434,7 +436,7 @@ public: // CONFIG_GET_GLOBAL_XXX (CONFIG_GET_PER_DB_XXX) set of macros helps to - // create trivial static (non-static) getXXX functions. + // create trivial static (non-static) getXXX functions. // Correctness of declaration and implementation is enforced with help // of entries[XXX].is_global. @@ -522,9 +524,6 @@ public: // Service port for INET CONFIG_GET_PER_DB_KEY(unsigned short, getRemoteServicePort, KEY_REMOTE_SERVICE_PORT, getInt); - // Pipe name for WNET - CONFIG_GET_PER_DB_STR(getRemotePipeName, KEY_REMOTE_PIPE_NAME); - // Name for IPC-related objects CONFIG_GET_PER_DB_STR(getIpcName, KEY_IPC_NAME); @@ -606,6 +605,9 @@ public: // set in minutes CONFIG_GET_PER_DB_KEY(unsigned int, getConnIdleTimeout, KEY_CONN_IDLE_TIMEOUT, getInt); + // set in seconds + CONFIG_GET_PER_DB_KEY(unsigned int, getOnDisconnectTrigTimeout, KEY_ON_DISCONNECT_TRIG_TIMEOUT, getInt); + CONFIG_GET_PER_DB_KEY(unsigned int, getClientBatchBuffer, KEY_CLIENT_BATCH_BUFFER, getInt); CONFIG_GET_GLOBAL_STR(getOutputRedirectionFile, KEY_OUTPUT_REDIRECTION_FILE); @@ -629,10 +631,12 @@ public: CONFIG_GET_PER_DB_KEY(ULONG, getInlineSortThreshold, KEY_INLINE_SORT_THRESHOLD, getInt); CONFIG_GET_PER_DB_STR(getTempPageSpaceDirectory, KEY_TEMP_PAGESPACE_DIR); + + CONFIG_GET_PER_DB_INT(getMaxStatementCacheSize, KEY_MAX_STATEMENT_CACHE_SIZE); }; // Implementation of interface to access master configuration file -class FirebirdConf FB_FINAL : +class FirebirdConf final : public RefCntIface > { public: diff --git a/src/common/config/config_file.h b/src/common/config/config_file.h index 4699ff7a5a..51b3836a59 100644 --- a/src/common/config/config_file.h +++ b/src/common/config/config_file.h @@ -81,7 +81,7 @@ public: sub(par.sub), line(par.line) { } Parameter() - : AutoStorage(), name(getPool()), value(getPool()), sub(0), line(0) + : AutoStorage(), name(getPool()), value(getPool()), line(0) { } SINT64 asInteger() const; diff --git a/src/common/intlobj_new.h b/src/common/intlobj_new.h index dee96f0705..859f1e4d57 100644 --- a/src/common/intlobj_new.h +++ b/src/common/intlobj_new.h @@ -57,9 +57,15 @@ typedef USHORT (*pfn_INTL_keylength) (texttype* tt, USHORT len); /* Types of the keys which may be returned by str2key routine */ -#define INTL_KEY_SORT 0 /* Full sort key */ -#define INTL_KEY_PARTIAL 1 /* Starting portion of sort key for equality class */ -#define INTL_KEY_UNIQUE 2 /* Full key for the equality class of the string */ +#define INTL_KEY_SORT 0 /* Full sort key */ +#define INTL_KEY_PARTIAL 1 /* Starting portion of sort key for equality class */ +#define INTL_KEY_UNIQUE 2 /* Full key for the equality class of the string */ +#define INTL_KEY_MULTI_STARTING 3 /* Multiple starting keys */ + +/* INTL_KEY_MULTI_STARTING format: + key ::= { }... + key_length ::= +*/ /* Returned value of INTL_BAD_KEY_LENGTH means that key error happened during key construction. When partial key is requested returned string should @@ -130,6 +136,8 @@ typedef void (*pfn_INTL_tt_destroy) (texttype* tt); (char, case, accent) which is case-insensitive, but accent-sensitive */ +#define TEXTTYPE_MULTI_STARTING_KEY 8 /* Supports INTL_KEY_MULTI_STARTING */ + struct texttype { @@ -346,6 +354,20 @@ typedef INTL_BOOL (*pfn_INTL_lookup_texttype) ( const ASCII* config_info ); +/* typedef for texttype lookup entry-point - with status buffer */ +typedef INTL_BOOL (*pfn_INTL_lookup_texttype_with_status) ( + char* status_buffer, + ULONG status_buffer_length, + texttype* tt, + const ASCII* texttype_name, + const ASCII* charset_name, + USHORT attributes, + const UCHAR* specific_attributes, + ULONG specific_attributes_length, + INTL_BOOL ignore_attributes, + const ASCII* config_info +); + /* typedef for charset lookup entry-point */ typedef INTL_BOOL (*pfn_INTL_lookup_charset) ( charset* cs, @@ -371,6 +393,7 @@ typedef ULONG (*pfn_INTL_setup_attributes) ( #define TEXTTYPE_ENTRYPOINT LD_lookup_texttype +#define TEXTTYPE_WITH_STATUS_ENTRYPOINT LD_lookup_texttype_with_status #define CHARSET_ENTRYPOINT LD_lookup_charset #define INTL_VERSION_ENTRYPOINT LD_version #define INTL_SETUP_ATTRIBUTES_ENTRYPOINT LD_setup_attributes diff --git a/src/common/isc_f_proto.h b/src/common/isc_f_proto.h index 021092be4d..718f8f8208 100644 --- a/src/common/isc_f_proto.h +++ b/src/common/isc_f_proto.h @@ -30,13 +30,15 @@ #include "../common/classes/fb_string.h" #include "../common/common.h" -enum iscProtocol {ISC_PROTOCOL_LOCAL, ISC_PROTOCOL_TCPIP, ISC_PROTOCOL_WLAN}; +enum iscProtocol {ISC_PROTOCOL_LOCAL, ISC_PROTOCOL_TCPIP}; #ifndef NO_NFS bool ISC_analyze_nfs(Firebird::PathName&, Firebird::PathName&); #endif -bool ISC_analyze_protocol(const char*, Firebird::PathName&, Firebird::PathName&, const char*, bool needFile); +#ifdef WIN_NT bool ISC_analyze_pclan(Firebird::PathName&, Firebird::PathName&); +#endif +bool ISC_analyze_protocol(const char*, Firebird::PathName&, Firebird::PathName&, const char*, bool needFile); bool ISC_analyze_tcp(Firebird::PathName&, Firebird::PathName&, bool = true); bool ISC_check_if_remote(const Firebird::PathName&, bool); iscProtocol ISC_extract_host(Firebird::PathName&, Firebird::PathName&, bool); diff --git a/src/common/isc_file.cpp b/src/common/isc_file.cpp index fae006bcc6..10222ac6b6 100644 --- a/src/common/isc_file.cpp +++ b/src/common/isc_file.cpp @@ -346,6 +346,47 @@ bool ISC_analyze_nfs(tstring& expanded_filename, tstring& node_name) #endif +#if defined(WIN_NT) +bool ISC_analyze_pclan(tstring& expanded_name, tstring& node_name) +{ +/************************************** + * + * I S C _ a n a l y z e _ p c l a n + * + ************************************** + * + * Functional description + * Check a file name for a SMB mount point. If so, + * decompose into node name and remote file name. + * + **************************************/ + ISC_expand_share(expanded_name); + + if (expanded_name.length() < 2 || + (expanded_name[0] != '\\' && expanded_name[0] != '/') || + (expanded_name[1] != '\\' && expanded_name[1] != '/')) + { + return false; + } + + const size p = expanded_name.find_first_of("\\/", 2); + if (p == npos) + return false; + + if (Config::getRemoteFileOpenAbility()) + { + if (expanded_name.find(':', p + 1) == npos) + return false; + } + + node_name = expanded_name.substr(2, p - 2); + expanded_name.erase(0, p + 1); + + return true; +} +#endif + + bool ISC_analyze_protocol(const char* protocol, tstring& expanded_name, tstring& node_name, const char* separator, bool need_file) { @@ -403,57 +444,6 @@ bool ISC_analyze_protocol(const char* protocol, tstring& expanded_name, tstring& } -#if defined(WIN_NT) -bool ISC_analyze_pclan(tstring& expanded_name, tstring& node_name) -{ -/************************************** - * - * I S C _ a n a l y z e _ p c l a n - * - ************************************** - * - * Functional description - * Analyze a filename for a named pipe node name on the front. - * If one is found, extract the node name, compute the residual - * file name, and return true. Otherwise return false. - * - **************************************/ - node_name.erase(); - if (expanded_name.length() < 2 || - (expanded_name[0] != '\\' && expanded_name[0] != '/') || - (expanded_name[1] != '\\' && expanded_name[1] != '/')) - { - return false; - } - - const size p = expanded_name.find_first_of("\\/", 2); - if (p == npos) - return false; - - if (Config::getRemoteFileOpenAbility()) - { - if (expanded_name.find(':', p + 1) == npos) - return false; - } - - node_name = "\\\\"; - node_name += expanded_name.substr(2, p - 2); - - // If this is a loopback, substitute "." for the host name. Otherwise, - // the CreateFile on the pipe will fail. - TEXT localhost[MAXHOSTLEN]; - ISC_get_host(localhost, sizeof(localhost)); - if (node_name.substr(2, npos) == localhost) - { - node_name.replace(2, npos, "."); - } - - expanded_name.erase(0, p + 1); - return true; -} -#endif // WIN_NT - - bool ISC_analyze_tcp(tstring& file_name, tstring& node_name, bool need_file) { /************************************** @@ -564,46 +554,22 @@ iscProtocol ISC_extract_host(Firebird::PathName& file_name, // Always check for an explicit TCP node name if (ISC_analyze_tcp(file_name, host_name)) - { return ISC_PROTOCOL_TCPIP; - } -#ifndef NO_NFS + if (implicit_flag) { - // Check for a file on an NFS mounted device + // Check for a file on a network mount - if (ISC_analyze_nfs(file_name, host_name)) - { +#ifdef WIN_NT + if (ISC_analyze_pclan(file_name, host_name)) return ISC_PROTOCOL_TCPIP; - } - } #endif -#if defined(WIN_NT) - // Check for an explicit named pipe node name - - if (ISC_analyze_pclan(file_name, host_name)) - { - return ISC_PROTOCOL_WLAN; - } - - if (implicit_flag) - { - // Check for a file on a shared drive. First try to expand - // the path. Then check the expanded path for a TCP or named pipe. - - ISC_expand_share(file_name); - if (ISC_analyze_tcp(file_name, host_name)) - { +#ifndef NO_NFS + if (ISC_analyze_nfs(file_name, host_name)) return ISC_PROTOCOL_TCPIP; - } - if (ISC_analyze_pclan(file_name, host_name)) - { - return ISC_PROTOCOL_WLAN; - } - +#endif } -#endif // WIN_NT return ISC_PROTOCOL_LOCAL; } diff --git a/src/common/os/mod_loader.h b/src/common/os/mod_loader.h index 933a4c896e..1c27966d91 100644 --- a/src/common/os/mod_loader.h +++ b/src/common/os/mod_loader.h @@ -73,7 +73,7 @@ public: const Firebird::PathName fileName; #ifdef LINUX - virtual bool getRealPath(Firebird::PathName& realPath) = 0; + virtual bool getRealPath(Firebird::PathName& path) = 0; #endif protected: diff --git a/src/common/os/posix/mod_loader.cpp b/src/common/os/posix/mod_loader.cpp index 9f87818e4a..ac0d61445c 100644 --- a/src/common/os/posix/mod_loader.cpp +++ b/src/common/os/posix/mod_loader.cpp @@ -46,18 +46,16 @@ class DlfcnModule : public ModuleLoader::Module { public: - DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, void* m) - : ModuleLoader::Module(pool, aFileName), - module(m) - {} - + DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, void* m); ~DlfcnModule(); - void* findSymbol(ISC_STATUS*, const Firebird::string&); - bool getRealPath(Firebird::PathName& realPath); + void* findSymbol(ISC_STATUS*, const Firebird::string&) override; + + bool getRealPath(Firebird::PathName& path) override; private: void* module; + Firebird::PathName realPath; }; static void makeErrorStatus(ISC_STATUS* status, const char* text) @@ -154,6 +152,47 @@ ModuleLoader::Module* ModuleLoader::loadModule(ISC_STATUS* status, const Firebir return FB_NEW_POOL(*getDefaultMemoryPool()) DlfcnModule(*getDefaultMemoryPool(), linkPath, module); } +DlfcnModule::DlfcnModule(MemoryPool& pool, const Firebird::PathName& aFileName, void* m) + : ModuleLoader::Module(pool, aFileName), + module(m), + realPath(pool) +{ +#ifdef HAVE_DLINFO + char b[PATH_MAX]; + +#ifdef HAVE_RTLD_DI_ORIGIN + if (dlinfo(module, RTLD_DI_ORIGIN, b) == 0) + { + realPath = b; + realPath += '/'; + realPath += fileName; + + if (realpath(realPath.c_str(), b)) + { + realPath = b; + return; + } + } +#endif + +#ifdef HAVE_RTLD_DI_LINKMAP + struct link_map* lm; + if (dlinfo(module, RTLD_DI_LINKMAP, &lm) == 0) + { + if (realpath(lm->l_name, b)) + { + realPath = b; + return; + } + } +#endif + +#endif + + // Error getting real path. + realPath.clear(); +} + DlfcnModule::~DlfcnModule() { if (module) @@ -165,7 +204,7 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam void* result = dlsym(module, symName.c_str()); if (!result) { - Firebird::string newSym ='_' + symName; + Firebird::string newSym = '_' + symName; result = dlsym(module, newSym.c_str()); } @@ -183,20 +222,28 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam return NULL; } + const auto& libraryPath = realPath.isEmpty() ? fileName : realPath; + + char symbolPathBuffer[PATH_MAX]; + const char* symbolPath = symbolPathBuffer; + + if (!realpath(info.dli_fname, symbolPathBuffer)) + symbolPath = info.dli_fname; + const char* errText = "Actual module name does not match requested"; - if (PathUtils::isRelative(fileName) || PathUtils::isRelative(info.dli_fname)) + if (PathUtils::isRelative(libraryPath) || PathUtils::isRelative(symbolPath)) { // check only name (not path) of the library Firebird::PathName dummyDir, nm1, nm2; - PathUtils::splitLastComponent(dummyDir, nm1, fileName); - PathUtils::splitLastComponent(dummyDir, nm2, info.dli_fname); + PathUtils::splitLastComponent(dummyDir, nm1, libraryPath); + PathUtils::splitLastComponent(dummyDir, nm2, symbolPath); if (nm1 != nm2) { makeErrorStatus(status, errText); return NULL; } } - else if (fileName != info.dli_fname) + else if (libraryPath != symbolPath) { makeErrorStatus(status, errText); return NULL; @@ -206,38 +253,11 @@ void* DlfcnModule::findSymbol(ISC_STATUS* status, const Firebird::string& symNam return result; } -bool DlfcnModule::getRealPath(Firebird::PathName& realPath) +bool DlfcnModule::getRealPath(Firebird::PathName& path) { -#ifdef HAVE_DLINFO - char b[PATH_MAX]; + if (realPath.isEmpty()) + return false; -#ifdef HAVE_RTLD_DI_ORIGIN - if (dlinfo(module, RTLD_DI_ORIGIN, b) == 0) - { - realPath = b; - realPath += '/'; - realPath += fileName; - - if (realpath(realPath.c_str(), b)) - { - realPath = b; - return true; - } - } -#endif - -#ifdef HAVE_RTLD_DI_LINKMAP - struct link_map* lm; - if (dlinfo(module, RTLD_DI_LINKMAP, &lm) == 0) - { - if (realpath(lm->l_name, b)) - { - realPath = b; - return true; - } - } -#endif - -#endif - return false; + path = realPath; + return true; } diff --git a/src/common/os/win32/mod_loader.cpp b/src/common/os/win32/mod_loader.cpp index 4276fccdb0..5173b00daa 100644 --- a/src/common/os/win32/mod_loader.cpp +++ b/src/common/os/win32/mod_loader.cpp @@ -87,26 +87,34 @@ public: memset(&ackd, 0, sizeof(ackd)); ackd.cbSize = sizeof(ackd); + const char* crtDll = +#if _MSC_VER == 1400 + "msvcr80.dll"; +#elif _MSC_VER == 1500 + "msvcr90.dll"; +#elif _MSC_VER == 1600 + "msvcr100.dll"; +#elif _MSC_VER == 1700 + "msvcr110.dll"; +#elif _MSC_VER == 1800 + "msvcr120.dll"; +#elif _MSC_VER >= 1900 && _MSC_VER < 2000 + "vcruntime140.dll"; +#else + ""; + +#define TO_STR(x) #x +#define ERRSTR(x) "Unknown " #x " value: " TO_STR(x) ". Specify CRT DLL name here !" + + static_assert(false, ERRSTR(_MSC_VER)); +// #error Specify CRT DLL name here ! +#endif + // if CRT already present in some activation context then nothing to do if ((*mFindActCtxSectionString) (0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, -#if _MSC_VER == 1400 - "msvcr80.dll", -#elif _MSC_VER == 1500 - "msvcr90.dll", -#elif _MSC_VER == 1600 - "msvcr100.dll", -#elif _MSC_VER == 1700 - "msvcr110.dll", -#elif _MSC_VER == 1800 - "msvcr120.dll", -#elif _MSC_VER >= 1900 && _MSC_VER <= 1930 - "vcruntime140.dll", -#else - #error Specify CRT DLL name here ! -#endif - &ackd)) + crtDll, &ackd)) { return; } diff --git a/src/common/security.h b/src/common/security.h index 33ad1e943a..c9ed3ca65c 100644 --- a/src/common/security.h +++ b/src/common/security.h @@ -232,7 +232,7 @@ public: IntField u, g; }; -class StackUserData FB_FINAL : public UserData +class StackUserData final : public UserData { public: void* operator new(size_t, void* memory) throw() @@ -241,7 +241,7 @@ public: } }; -class DynamicUserData FB_FINAL : public UserData +class DynamicUserData final : public UserData { public: #ifdef DEBUG_GDS_ALLOC diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 3c2a17e914..9d257c0615 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -92,22 +92,24 @@ public: // System-wide ICU have no version number at entries names if (!majorVersion) { + fb_assert(false); // ASF: I don't think this code path is correct. + if (module->findSymbol(NULL, name, ptr)) return; } else { // ICU has several schemas for entries names - const char* patterns[] = + const char* const patterns[] = { - "%s_%d", "%s_%d_%d", "%s_%d%d", "%s", NULL + "%s_%d", "%s_%d_%d", "%s_%d%d", "%s" }; string symbol; - for (const char** p = patterns; *p; ++p) + for (auto pattern : patterns) { - symbol.printf(*p, name, majorVersion, minorVersion); + symbol.printf(pattern, name, majorVersion, minorVersion); if (module->findSymbol(NULL, symbol, ptr)) return; } @@ -272,8 +274,10 @@ public: USet* (U_EXPORT2 *usetOpen)(UChar32 start, UChar32 end); void (U_EXPORT2 *ucolClose)(UCollator* coll); - int32_t (U_EXPORT2 *ucolGetContractions)(const UCollator* coll, USet* conts, UErrorCode* status); + int32_t (U_EXPORT2 *ucolGetContractionsAndExpansions)(const UCollator* coll, USet* contractions, USet* expansions, + UBool addPrefixes, UErrorCode* status); const UChar* (U_EXPORT2 *ucolGetRules)(const UCollator* coll, int32_t* length); + int32_t (U_EXPORT2 *ucolGetSortKey)(const UCollator* coll, const UChar* source, int32_t sourceLength, uint8_t* result, int32_t resultLength); UCollator* (U_EXPORT2 *ucolOpen)(const char* loc, UErrorCode* status); @@ -321,6 +325,7 @@ private: getEntryPoint("ucnv_open", module, ucnv_open); getEntryPoint("ucnv_close", module, ucnv_close); getEntryPoint("ucnv_fromUChars", module, ucnv_fromUChars); + getEntryPoint("u_getVersion", module, u_getVersion); getEntryPoint("u_tolower", module, u_tolower); getEntryPoint("u_toupper", module, u_toupper); getEntryPoint("u_strCompare", module, u_strCompare); @@ -379,8 +384,11 @@ public: if (o) { - o->vMajor = majorVersion; - o->vMinor = minorVersion; + UVersionInfo versionInfo; + o->u_getVersion(versionInfo); + + o->vMajor = versionInfo[0]; + o->vMinor = versionInfo[1]; } return o; @@ -507,15 +515,20 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, else { // ICU has several schemas for placing version into file name - const char* patterns[] = + const char* const patterns[] = { - "%d_%d", "%d%d", NULL +#ifdef WIN_NT + "%d", +#endif + "%d_%d", + "%d.%d", + "%d%d" }; PathName s, filename; - for (const char** p = patterns; *p; ++p) + for (auto pattern : patterns) { - s.printf(*p, majorVersion, minorVersion); + s.printf(pattern, majorVersion, minorVersion); filename.printf(templateName, s.c_str()); module = ModuleLoader::fixAndLoadModule(NULL, filename); @@ -523,7 +536,9 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, break; } +#ifndef WIN_NT // There is no sence to try pattern "%d" for different minor versions + // ASF: In Windows ICU 63.1 libraries use 63.dll suffix. This is handled in 'patterns' above. if (!module && minorVersion == 0) { s.printf("%d", majorVersion); @@ -531,6 +546,7 @@ static ModuleLoader::Module* formatAndLoad(const char* templateName, module = ModuleLoader::fixAndLoadModule(NULL, filename); } +#endif } return module; @@ -1169,7 +1185,7 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c getVersions(configInfo, versions); if (versions.isEmpty()) - gds__log("No versions"); + gds__log("No ICU versions specified"); string version = icuVersion.isEmpty() ? versions[0] : icuVersion; if (version == "default") @@ -1241,7 +1257,8 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c icu->getEntryPoint("uset_open", icu->ucModule, icu->usetOpen); icu->getEntryPoint("ucol_close", icu->inModule, icu->ucolClose); - icu->getEntryPoint("ucol_getContractions", icu->inModule, icu->ucolGetContractions); + icu->getEntryPoint("ucol_getContractionsAndExpansions", icu->inModule, + icu->ucolGetContractionsAndExpansions); icu->getEntryPoint("ucol_getRules", icu->inModule, icu->ucolGetRules); icu->getEntryPoint("ucol_getSortKey", icu->inModule, icu->ucolGetSortKey); icu->getEntryPoint("ucol_open", icu->inModule, icu->ucolOpen); @@ -1295,6 +1312,13 @@ UnicodeUtil::ICU* UnicodeUtil::loadICU(const string& icuVersion, const string& c } +void UnicodeUtil::getICUVersion(ICU* icu, int& majorVersion, int& minorVersion) +{ + majorVersion = icu->majorVersion; + minorVersion = icu->minorVersion; +} + + UnicodeUtil::ConversionICU& UnicodeUtil::getConversionICU() { if (convIcu) @@ -1311,7 +1335,7 @@ UnicodeUtil::ConversionICU& UnicodeUtil::getConversionICU() // Try "favorite" (distributed on windows) version first const int favMaj = 63; - const int favMin = 0; + const int favMin = 1; try { if ((convIcu = ImplementConversionICU::create(favMaj, favMin))) @@ -1408,13 +1432,13 @@ string UnicodeUtil::getDefaultIcuVersion() } -bool UnicodeUtil::getCollVersion(const Firebird::string& icuVersion, +UnicodeUtil::ICU* UnicodeUtil::getCollVersion(const Firebird::string& icuVersion, const Firebird::string& configInfo, Firebird::string& collVersion) { ICU* icu = loadICU(icuVersion, configInfo); if (!icu) - return false; + return nullptr; char version[U_MAX_VERSION_STRING_LENGTH]; icu->uVersionToString(icu->collVersion, version); @@ -1424,7 +1448,7 @@ bool UnicodeUtil::getCollVersion(const Firebird::string& icuVersion, else collVersion = version; - return true; + return icu; } UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( @@ -1499,12 +1523,11 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( tt->texttype_pad_option = (attributes & TEXTTYPE_ATTR_PAD_SPACE) ? true : false; - ICU* icu = loadICU(collVersion, locale, configInfo); - if (!icu) - { - gds__log("loadICU failed"); - return NULL; - } + string icuVersion; + if (specificAttributes.get(IntlUtil::convertAsciiToUtf16("ICU-VERSION"), icuVersion)) + icuVersion = IntlUtil::convertUtf16ToAscii(icuVersion, &error); + + const auto icu = loadICU(icuVersion, collVersion, locale, configInfo); UErrorCode status = U_ZERO_ERROR; HalfStaticArray rulesBuffer; @@ -1617,35 +1640,145 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create( USet* contractions = icu->usetOpen(1, 0); // status not verified here. - icu->ucolGetContractions(partialCollator, contractions, &status); + icu->ucolGetContractionsAndExpansions(partialCollator, contractions, nullptr, false, &status); int contractionsCount = icu->usetGetItemCount(contractions); for (int contractionIndex = 0; contractionIndex < contractionsCount; ++contractionIndex) { - UChar str[10]; + UChar strChars[10]; UChar32 start, end; status = U_ZERO_ERROR; - int len = icu->usetGetItem(contractions, contractionIndex, &start, &end, str, sizeof(str), &status); + int len = icu->usetGetItem(contractions, contractionIndex, &start, &end, strChars, sizeof(strChars), &status); if (len >= 2) { obj->maxContractionsPrefixLength = len - 1 > obj->maxContractionsPrefixLength ? len - 1 : obj->maxContractionsPrefixLength; - for (int currentLen = 1; currentLen < len; ++currentLen) - { - string s(reinterpret_cast(str), currentLen * 2); + UCHAR key[100]; + int keyLen = icu->ucolGetSortKey(partialCollator, strChars, len, key, sizeof(key)); - if (!obj->contractionsPrefix.exist(s)) - obj->contractionsPrefix.push(s); + for (int prefixLen = 1; prefixLen < len; ++prefixLen) + { + const Array str(reinterpret_cast(strChars), prefixLen); + auto keySet = obj->contractionsPrefix.get(str); + + if (!keySet) + { + keySet = obj->contractionsPrefix.put(str); + + UCHAR prefixKey[100]; + int prefixKeyLen = icu->ucolGetSortKey(partialCollator, + strChars, prefixLen, prefixKey, sizeof(prefixKey)); + + keySet->add(Array(prefixKey, prefixKeyLen)); + } + + keySet->add(Array(key, keyLen)); } } } icu->usetClose(contractions); + ContractionsPrefixMap::Accessor accessor(&obj->contractionsPrefix); + + for (bool found = accessor.getFirst(); found; found = accessor.getNext()) + { + auto& keySet = accessor.current()->second; + + if (keySet.getCount() <= 1) + continue; + + fb_assert(accessor.current()->first.hasData()); + USHORT ch = accessor.current()->first[0]; + + if (ch >= 0xFDD0 && ch <= 0xFDEF) + { + keySet.clear(); + keySet.add(Array()); + continue; + } + + auto firstKeyIt = keySet.begin(); + auto lastKeyIt = --keySet.end(); + + const UCHAR* firstKeyDataIt = firstKeyIt->begin(); + const UCHAR* lastKeyDataIt = lastKeyIt->begin(); + const UCHAR* firstKeyDataEnd = firstKeyIt->end(); + const UCHAR* lastKeyDataEnd = lastKeyIt->end(); + + if (*firstKeyDataIt == *lastKeyDataIt) + { + unsigned common = 0; + + do + { + ++common; + } while (++firstKeyDataIt != firstKeyDataEnd && ++lastKeyDataIt != lastKeyDataEnd && + *firstKeyDataIt == *lastKeyDataIt); + + Array commonKey(firstKeyIt->begin(), common); + keySet.clear(); + keySet.add(commonKey); + } + else + { + auto secondKeyIt = ++keySet.begin(); + const UCHAR* secondKeyDataIt = secondKeyIt->begin(); + const UCHAR* secondKeyDataEnd = secondKeyIt->end(); + + ObjectsArray > commonKeys; + commonKeys.add(*firstKeyIt); + + while (secondKeyIt != keySet.end()) + { + unsigned common = 0; + + while (firstKeyDataIt != firstKeyDataEnd && secondKeyDataIt != secondKeyDataEnd && + *firstKeyDataIt == *secondKeyDataIt) + { + ++common; + ++firstKeyDataIt; + ++secondKeyDataIt; + } + + unsigned backSize = commonKeys.back()->getCount(); + + if (common > backSize) + commonKeys.back()->append(secondKeyIt->begin() + backSize, common - backSize); + else if (common < backSize) + { + if (common == 0) + commonKeys.push(*secondKeyIt); + else + commonKeys.back()->resize(common); + } + + if (++secondKeyIt != keySet.end()) + { + ++firstKeyIt; + + firstKeyDataIt = firstKeyIt->begin(); + secondKeyDataIt = secondKeyIt->begin(); + + firstKeyDataEnd = firstKeyIt->end(); + secondKeyDataEnd = secondKeyIt->end(); + } + } + + keySet.clear(); + + for (auto ck : commonKeys) + keySet.add(ck); + } + } + + if (obj->maxContractionsPrefixLength) + tt->texttype_flags |= TEXTTYPE_MULTI_STARTING_KEY; + return obj; } @@ -1696,41 +1829,17 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src srcLenLong = pad - src + 1; } + if (srcLenLong == 0) + return 0; + HalfStaticArray buffer; const UCollator* coll = NULL; switch (key_type) { case INTL_KEY_PARTIAL: + case INTL_KEY_MULTI_STARTING: coll = partialCollator; - - // Remove last bytes of key if they are start of a contraction - // to correctly find in the index. - - for (int i = MIN(maxContractionsPrefixLength, srcLenLong); i > 0; --i) - { - if (contractionsPrefix.exist(string(reinterpret_cast(src + srcLenLong - i), i * 2))) - { - srcLenLong -= i; - break; - } - } - - if (numericSort) - { - // ASF: Wee need to remove trailing numbers to return sub key that - // matches full key. Example: "abc1" becomes "abc" to match "abc10". - const USHORT* p = src + srcLenLong - 1; - - for (; p >= src; --p) - { - if (!(*p >= '0' && *p <= '9')) - break; - } - - srcLenLong = p - src + 1; - } - break; case INTL_KEY_UNIQUE: @@ -1749,11 +1858,100 @@ USHORT UnicodeUtil::Utf16Collation::stringToKey(USHORT srcLen, const USHORT* src return INTL_BAD_KEY_LENGTH; } - if (srcLenLong == 0) - return 0; + if (key_type == INTL_KEY_MULTI_STARTING) + { + bool trailingNumbersRemoved = false; - return icu->ucolGetSortKey(coll, + if (numericSort) + { + // ASF: Wee need to remove trailing numbers to return sub key that + // matches full key. Example: "abc1" becomes "abc" to match "abc10". + const USHORT* p = src + srcLenLong - 1; + + for (; p >= src; --p) + { + if (!(*p >= '0' && *p <= '9')) + break; + + trailingNumbersRemoved = true; + } + + srcLenLong = p - src + 1; + } + + if (!trailingNumbersRemoved) + { + for (int i = MIN(maxContractionsPrefixLength, srcLenLong); i > 0; --i) + { + auto keys = contractionsPrefix.get(Array(src + srcLenLong - i, i)); + + if (keys) + { + const UCHAR* dstStart = dst; + ULONG prefixLen; + + srcLenLong -= i; + + if (srcLenLong != 0) + { + prefixLen = icu->ucolGetSortKey(coll, + reinterpret_cast(src), srcLenLong, dst + 2, dstLen - 2); + + if (prefixLen == 0 || prefixLen > dstLen - 2 || prefixLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + fb_assert(dst[2 + prefixLen - 1] == '\0'); + --prefixLen; + dstLen -= 2 + prefixLen; + } + else + prefixLen = 0; + + for (const auto& keyIt : *keys) + { + const ULONG keyLen = prefixLen + keyIt.getCount(); + + if (keyLen > dstLen - 2 || keyLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + dst[0] = UCHAR(keyLen & 0xFF); + dst[1] = UCHAR(keyLen >> 8); + + if (dst != dstStart) + memcpy(dst + 2, dstStart + 2, prefixLen); + + memcpy(dst + 2 + prefixLen, keyIt.begin(), keyIt.getCount()); + dst += 2 + keyLen; + dstLen -= 2 + keyLen; + } + + return dst - dstStart; + } + } + } + + ULONG keyLen = icu->ucolGetSortKey(coll, + reinterpret_cast(src), srcLenLong, dst + 2, dstLen - 3); + + if (keyLen == 0 || keyLen > dstLen - 3 || keyLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + fb_assert(dst[2 + keyLen - 1] == '\0'); + --keyLen; + + dst[0] = UCHAR(keyLen & 0xFF); + dst[1] = UCHAR(keyLen >> 8); + + return keyLen + 2; + } + + const ULONG keyLen = icu->ucolGetSortKey(coll, reinterpret_cast(src), srcLenLong, dst, dstLen); + + if (keyLen == 0 || keyLen > dstLen || keyLen > MAX_USHORT) + return INTL_BAD_KEY_LENGTH; + + return keyLen; } @@ -1822,8 +2020,8 @@ ULONG UnicodeUtil::Utf16Collation::canonical(ULONG srcLen, const USHORT* src, UL UnicodeUtil::ICU* UnicodeUtil::Utf16Collation::loadICU( - const Firebird::string& collVersion, const Firebird::string& locale, - const Firebird::string& configInfo) + const string& icuVersion, const string& collVersion, + const string& locale, const string& configInfo) { ObjectsArray versions; getVersions(configInfo, versions); @@ -1866,7 +2064,26 @@ UnicodeUtil::ICU* UnicodeUtil::Utf16Collation::loadICU( return icu; } - return NULL; + string errorMsg; + + if (icuVersion.isEmpty()) + { + errorMsg.printf( + "An ICU library with collation version %s is required but was not found. " + "You may try to install another ICU version with this collation version " + "or look for 'gfix -icu' in Firebird documentation.", + collVersion.c_str()); + } + else + { + errorMsg.printf( + "An ICU library with collation version %s is required but was not found. " + "You may try to install ICU version %s, used to register the collation in this database " + "or look for 'gfix -icu' in Firebird documentation.", + collVersion.c_str(), icuVersion.c_str()); + } + + (Arg::Gds(isc_random) << errorMsg).raise(); } diff --git a/src/common/unicode_util.h b/src/common/unicode_util.h index 59bf4ade39..22c00ba2c1 100644 --- a/src/common/unicode_util.h +++ b/src/common/unicode_util.h @@ -30,7 +30,9 @@ #include "intlobj_new.h" #include "../common/IntlUtil.h" #include "../common/os/mod_loader.h" +#include "../common/classes/array.h" #include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" #include "../common/classes/objects_array.h" #include #include @@ -56,6 +58,7 @@ public: const UChar *src, int32_t srcLength, UErrorCode *pErrorCode); + void (U_EXPORT2* u_getVersion) (UVersionInfo versionArray); UChar32 (U_EXPORT2* u_tolower) (UChar32 c); UChar32 (U_EXPORT2* u_toupper) (UChar32 c); int32_t (U_EXPORT2* u_strCompare) (const UChar* s1, int32_t length1, @@ -175,7 +178,8 @@ public: static ConversionICU& getConversionICU(); static ICU* loadICU(const Firebird::string& icuVersion, const Firebird::string& configInfo); - static bool getCollVersion(const Firebird::string& icuVersion, + static void getICUVersion(ICU* icu, int& majorVersion, int& minorVersion); + static ICU* getCollVersion(const Firebird::string& icuVersion, const Firebird::string& configInfo, Firebird::string& collVersion); class Utf16Collation @@ -200,8 +204,47 @@ public: ULONG canonical(ULONG srcLen, const USHORT* src, ULONG dstLen, ULONG* dst, const ULONG* exceptions); private: - static ICU* loadICU(const Firebird::string& collVersion, const Firebird::string& locale, - const Firebird::string& configInfo); + template + class ArrayComparator + { + public: + static bool greaterThan(const Firebird::Array& i1, const Firebird::Array& i2) + { + FB_SIZE_T minCount = MIN(i1.getCount(), i2.getCount()); + int cmp = memcmp(i1.begin(), i2.begin(), minCount * sizeof(T)); + + if (cmp != 0) + return cmp > 0; + + return i1.getCount() > i2.getCount(); + } + + static bool greaterThan(const Firebird::Array* i1, const Firebird::Array* i2) + { + return greaterThan(*i1, *i2); + } + }; + + typedef Firebird::SortedObjectsArray< + Firebird::Array, + Firebird::InlineStorage*, 3>, + Firebird::Array, + Firebird::DefaultKeyValue*>, + ArrayComparator + > SortKeyArray; + + typedef Firebird::GenericMap< + Firebird::Pair< + Firebird::Full< + Firebird::Array, // UTF-16 string + SortKeyArray // sort keys + > + >, + ArrayComparator + > ContractionsPrefixMap; + + static ICU* loadICU(const Firebird::string& icuVersion, const Firebird::string& collVersion, + const Firebird::string& locale, const Firebird::string& configInfo); void normalize(ULONG* strLen, const USHORT** str, bool forNumericSort, Firebird::HalfStaticArray& buffer) const; @@ -212,7 +255,7 @@ public: UCollator* compareCollator; UCollator* partialCollator; UCollator* sortCollator; - Firebird::SortedObjectsArray contractionsPrefix; // UTF-16 string + ContractionsPrefixMap contractionsPrefix; unsigned maxContractionsPrefixLength; // number of characters bool numericSort; }; diff --git a/src/common/utils.cpp b/src/common/utils.cpp index c1076713c5..aee24aae0b 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1143,7 +1143,7 @@ Firebird::PathName getPrefix(unsigned int prefType, const char* name) if (s.hasData() && name[0]) { - s += '/'; + s += PathUtils::dir_sep; } s += name; gds__prefix(tmp, s.c_str()); @@ -1613,6 +1613,13 @@ bool containsErrorCode(const ISC_STATUS* v, ISC_STATUS code) return false; } +inline bool sqlSymbolChar(char c, bool first) +{ + if (c & 0x80) + return false; + return (isdigit(c) && !first) || isalpha(c) || c == '_' || c == '$'; +} + const char* dpbItemUpper(const char* s, FB_SIZE_T l, Firebird::string& buf) { if (l && (s[0] == '"' || s[0] == '\'')) @@ -1625,30 +1632,38 @@ const char* dpbItemUpper(const char* s, FB_SIZE_T l, Firebird::string& buf) { if (s[i] == end_quote) { - if (++i >= l || s[i] != end_quote) - break; // delimited quote, done processing + if (++i >= l) + { + if (ascii && s[0] == '\'') + buf.upper(); + + return buf.c_str(); + } + + if (s[i] != end_quote) + { + buf.assign(&s[i], l - i); + (Firebird::Arg::Gds(isc_quoted_str_bad) << buf).raise(); + } // skipped the escape quote, continue processing } - - if (s[i] & 0x80) + else if (!sqlSymbolChar(s[i], i == 1)) ascii = false; + buf += s[i]; } - if (ascii && s[0] == '\'') - buf.upper(); - - return buf.c_str(); + buf.assign(1, s[0]); + (Firebird::Arg::Gds(isc_quoted_str_miss) << buf).raise(); } // non-quoted string - try to uppercase for (FB_SIZE_T i = 0; i < l; ++i) { - if (!(s[i] & 0x80)) - buf += toupper(s[i]); - else + if (!sqlSymbolChar(s[i], i == 0)) return NULL; // contains non-ascii data + buf += toupper(s[i]); } return buf.c_str(); diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index af5c0e9b93..0e9a9d3dd8 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -356,7 +356,7 @@ AggNode* AggNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void AggNode::aggInit(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); impure->vlux_count = 0; @@ -378,7 +378,7 @@ void AggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -bool AggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool AggNode::aggPass(thread_db* tdbb, Request* request) const { dsc* desc = NULL; @@ -435,7 +435,7 @@ bool AggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void AggNode::aggFinish(thread_db* /*tdbb*/, jrd_req* request) const +void AggNode::aggFinish(thread_db* /*tdbb*/, Request* request) const { if (asb) { @@ -445,7 +445,7 @@ void AggNode::aggFinish(thread_db* /*tdbb*/, jrd_req* request) const } } -dsc* AggNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* AggNode::execute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -685,7 +685,7 @@ string AvgAggNode::internalPrint(NodePrinter& printer) const return "AvgAggNode"; } -void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void AvgAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -704,7 +704,7 @@ void AvgAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -void AvgAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void AvgAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); if (impure->vlux_count++ == 0) // first call to aggPass() @@ -720,7 +720,7 @@ void AvgAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const ArithmeticNode::add2(tdbb, desc, impure, this, blr_add); } -dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* AvgAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -848,7 +848,7 @@ string ListAggNode::internalPrint(NodePrinter& printer) const return "ListAggNode"; } -void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void ListAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -859,7 +859,7 @@ void ListAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->vlu_desc.dsc_dtype = 0; } -void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void ListAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -897,7 +897,7 @@ void ListAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const impure->vlu_blob->BLB_put_data(tdbb, temp, len); } -dsc* ListAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* ListAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1000,7 +1000,7 @@ string CountAggNode::internalPrint(NodePrinter& printer) const } //// TODO: Improve count(*) in local tables. -void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void CountAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1008,7 +1008,7 @@ void CountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0); } -void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const +void CountAggNode::aggPass(thread_db* /*tdbb*/, Request* request, dsc* /*desc*/) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1018,7 +1018,7 @@ void CountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) ++impure->vlu_misc.vlu_int64; } -dsc* CountAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* CountAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1253,7 +1253,7 @@ string SumAggNode::internalPrint(NodePrinter& printer) const return "SumAggNode"; } -void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void SumAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1269,7 +1269,7 @@ void SumAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -void SumAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void SumAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; @@ -1280,7 +1280,7 @@ void SumAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const ArithmeticNode::add2(tdbb, desc, impure, this, blr_add); } -dsc* SumAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* SumAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1345,7 +1345,7 @@ string MaxMinAggNode::internalPrint(NodePrinter& printer) const return "MaxMinAggNode"; } -void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void MaxMinAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1353,7 +1353,7 @@ void MaxMinAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->vlu_desc.dsc_dtype = 0; } -void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void MaxMinAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; @@ -1370,7 +1370,7 @@ void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const EVL_make_value(tdbb, desc, impure); } -dsc* MaxMinAggNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* MaxMinAggNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1472,7 +1472,7 @@ string StdDevAggNode::internalPrint(NodePrinter& printer) const return "StdDevAggNode"; } -void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void StdDevAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1491,7 +1491,7 @@ void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const +void StdDevAggNode::aggPass(thread_db* tdbb, Request* request, dsc* desc) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; @@ -1514,7 +1514,7 @@ void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const } } -dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* StdDevAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); StdDevImpure* impure2 = request->getImpure(impure2Offset); @@ -1681,7 +1681,7 @@ string CorrAggNode::internalPrint(NodePrinter& printer) const return "CorrAggNode"; } -void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void CorrAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1700,7 +1700,7 @@ void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool CorrAggNode::aggPass(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -1744,12 +1744,12 @@ bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void CorrAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void CorrAggNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { fb_assert(false); } -dsc* CorrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* CorrAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); CorrImpure* impure2 = request->getImpure(impure2Offset); @@ -1957,7 +1957,7 @@ string RegrAggNode::internalPrint(NodePrinter& printer) const return "RegrAggNode"; } -void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RegrAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -1976,7 +1976,7 @@ void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool RegrAggNode::aggPass(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -2021,12 +2021,12 @@ bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void RegrAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void RegrAggNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { fb_assert(false); } -dsc* RegrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* RegrAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -2226,7 +2226,7 @@ string RegrCountAggNode::internalPrint(NodePrinter& printer) const return "RegrCountAggNode"; } -void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RegrCountAggNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -2234,7 +2234,7 @@ void RegrCountAggNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0); } -bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const +bool RegrCountAggNode::aggPass(thread_db* tdbb, Request* request) const { EVL_expr(tdbb, request, arg); if (request->req_flags & req_null) @@ -2250,12 +2250,12 @@ bool RegrCountAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return true; } -void RegrCountAggNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void RegrCountAggNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { fb_assert(false); } -dsc* RegrCountAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* RegrCountAggNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); diff --git a/src/dsql/AggNodes.h b/src/dsql/AggNodes.h index 2d122c41d2..0c5f105a21 100644 --- a/src/dsql/AggNodes.h +++ b/src/dsql/AggNodes.h @@ -30,7 +30,7 @@ namespace Jrd { -class AvgAggNode : public AggNode +class AvgAggNode final : public AggNode { public: explicit AvgAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg = NULL); @@ -53,9 +53,9 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -65,7 +65,7 @@ private: ULONG tempImpure; }; -class ListAggNode : public AggNode +class ListAggNode final : public AggNode { public: explicit ListAggNode(MemoryPool& pool, bool aDistinct, ValueExprNode* aArg = NULL, @@ -91,9 +91,9 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -102,7 +102,7 @@ private: NestConst delimiter; }; -class CountAggNode : public AggNode +class CountAggNode final : public AggNode { public: explicit CountAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg = NULL); @@ -120,15 +120,15 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; -class SumAggNode : public AggNode +class SumAggNode final : public AggNode { public: explicit SumAggNode(MemoryPool& pool, bool aDistinct, bool aDialect1, ValueExprNode* aArg = NULL); @@ -150,15 +150,15 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; -class MaxMinAggNode : public AggNode +class MaxMinAggNode final : public AggNode { public: enum MaxMinType @@ -181,9 +181,9 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -192,7 +192,7 @@ public: const MaxMinType type; }; -class StdDevAggNode : public AggNode +class StdDevAggNode final : public AggNode { public: enum StdDevType @@ -230,9 +230,9 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -244,7 +244,7 @@ private: ULONG impure2Offset; }; -class CorrAggNode : public AggNode +class CorrAggNode final : public AggNode { public: enum CorrType @@ -288,10 +288,10 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual bool aggPass(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -304,7 +304,7 @@ private: ULONG impure2Offset; }; -class RegrAggNode : public AggNode +class RegrAggNode final : public AggNode { public: enum RegrType @@ -353,10 +353,10 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual bool aggPass(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -369,7 +369,7 @@ private: ULONG impure2Offset; }; -class RegrCountAggNode : public AggNode +class RegrCountAggNode final : public AggNode { public: explicit RegrCountAggNode(MemoryPool& pool, @@ -393,10 +393,10 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual bool aggPass(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 7c77793d8b..35cb750ae3 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -28,7 +28,7 @@ #include "../jrd/tra.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/recsrc/Cursor.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" @@ -170,7 +170,7 @@ BoolExprNode* BinaryBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } -bool BinaryBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool BinaryBoolNode::execute(thread_db* tdbb, Request* request) const { switch (blrOp) { @@ -185,7 +185,7 @@ bool BinaryBoolNode::execute(thread_db* tdbb, jrd_req* request) const return false; } -bool BinaryBoolNode::executeAnd(thread_db* tdbb, jrd_req* request) const +bool BinaryBoolNode::executeAnd(thread_db* tdbb, Request* request) const { // If either operand is false, then the result is false; // If both are true, the result is true; @@ -231,7 +231,7 @@ bool BinaryBoolNode::executeAnd(thread_db* tdbb, jrd_req* request) const return false; } -bool BinaryBoolNode::executeOr(thread_db* tdbb, jrd_req* request) const +bool BinaryBoolNode::executeOr(thread_db* tdbb, Request* request) const { // If either operand is true, then the result is true; // If both are false, the result is false; @@ -643,7 +643,7 @@ void ComparativeBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) } } -bool ComparativeBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool ComparativeBoolNode::execute(thread_db* tdbb, Request* request) const { dsc* desc[2] = {NULL, NULL}; bool computed_invariant = false; @@ -817,7 +817,7 @@ bool ComparativeBoolNode::execute(thread_db* tdbb, jrd_req* request) const } // Perform one of the complex string functions CONTAINING, MATCHES, or STARTS WITH. -bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, jrd_req* request, dsc* desc1, +bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request, dsc* desc1, dsc* desc2, bool computedInvariant) const { SET_TDBB(tdbb); @@ -1023,7 +1023,7 @@ bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, jrd_req* request, dsc* } // Execute SLEUTH operator. -bool ComparativeBoolNode::sleuth(thread_db* tdbb, jrd_req* request, const dsc* desc1, +bool ComparativeBoolNode::sleuth(thread_db* tdbb, Request* request, const dsc* desc1, const dsc* desc2) const { SET_TDBB(tdbb); @@ -1223,7 +1223,7 @@ void MissingBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) arg->getDesc(tdbb, csb, &descriptor_a); } -bool MissingBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool MissingBoolNode::execute(thread_db* tdbb, Request* request) const { EVL_expr(tdbb, request, arg); @@ -1298,7 +1298,7 @@ BoolExprNode* NotBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) return BoolExprNode::pass1(tdbb, csb); } -bool NotBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool NotBoolNode::execute(thread_db* tdbb, Request* request) const { bool value = arg->execute(tdbb, request); @@ -1612,7 +1612,7 @@ void RseBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(rsb, rse->rse_invariants); } -bool RseBoolNode::execute(thread_db* tdbb, jrd_req* request) const +bool RseBoolNode::execute(thread_db* tdbb, Request* request) const { USHORT* invariant_flags; impure_value* impure; diff --git a/src/dsql/BoolNodes.h b/src/dsql/BoolNodes.h index 9235082ef3..10ad9d72a2 100644 --- a/src/dsql/BoolNodes.h +++ b/src/dsql/BoolNodes.h @@ -31,7 +31,7 @@ namespace Jrd { class SubQuery; -class BinaryBoolNode : public TypedNode +class BinaryBoolNode final : public TypedNode { public: BinaryBoolNode(MemoryPool& pool, UCHAR aBlrOp, BoolExprNode* aArg1 = NULL, @@ -54,11 +54,11 @@ public: virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: - virtual bool executeAnd(thread_db* tdbb, jrd_req* request) const; - virtual bool executeOr(thread_db* tdbb, jrd_req* request) const; + virtual bool executeAnd(thread_db* tdbb, Request* request) const; + virtual bool executeOr(thread_db* tdbb, Request* request) const; public: UCHAR blrOp; @@ -67,7 +67,7 @@ public: }; -class ComparativeBoolNode : public TypedNode +class ComparativeBoolNode final : public TypedNode { public: enum DsqlFlag : UCHAR @@ -106,12 +106,12 @@ public: virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean2(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: - bool stringBoolean(thread_db* tdbb, jrd_req* request, dsc* desc1, dsc* desc2, + bool stringBoolean(thread_db* tdbb, Request* request, dsc* desc1, dsc* desc2, bool computedInvariant) const; - bool sleuth(thread_db* tdbb, jrd_req* request, const dsc* desc1, const dsc* desc2) const; + bool sleuth(thread_db* tdbb, Request* request, const dsc* desc1, const dsc* desc2) const; BoolExprNode* createRseNode(DsqlCompilerScratch* dsqlScratch, UCHAR rseBlrOp); @@ -126,7 +126,7 @@ public: }; -class MissingBoolNode : public TypedNode +class MissingBoolNode final : public TypedNode { public: explicit MissingBoolNode(MemoryPool& pool, ValueExprNode* aArg = NULL, bool aDsqlUnknown = false); @@ -151,7 +151,7 @@ public: virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean2(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; public: bool dsqlUnknown; @@ -159,7 +159,7 @@ public: }; -class NotBoolNode : public TypedNode +class NotBoolNode final : public TypedNode { public: explicit NotBoolNode(MemoryPool& pool, BoolExprNode* aArg = NULL); @@ -183,7 +183,7 @@ public: virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: BoolExprNode* process(DsqlCompilerScratch* dsqlScratch, bool invert); @@ -193,7 +193,7 @@ public: }; -class RseBoolNode : public TypedNode +class RseBoolNode final : public TypedNode { public: RseBoolNode(MemoryPool& pool, UCHAR aBlrOp, RecordSourceNode* aDsqlRse = NULL); @@ -235,7 +235,7 @@ public: virtual BoolExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean1(thread_db* tdbb, CompilerScratch* csb); virtual void pass2Boolean2(thread_db* tdbb, CompilerScratch* csb); - virtual bool execute(thread_db* tdbb, jrd_req* request) const; + virtual bool execute(thread_db* tdbb, Request* request) const; private: BoolExprNode* convertNeqAllToNotAny(thread_db* tdbb, CompilerScratch* csb); diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index e8f6557f9d..6cb8316c99 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -985,7 +985,7 @@ void DdlNode::executeDdlTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc const MetaName& oldNewObjectName) { executeDdlTrigger(tdbb, transaction, when, action, objectName, oldNewObjectName, - *dsqlScratch->getStatement()->getSqlText()); + *dsqlScratch->getDsqlStatement()->getSqlText()); } void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, MetaName& name, @@ -1354,7 +1354,6 @@ void CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) switch (objType) { - case obj_schema: case obj_database: SCL_check_database(tdbb, SCL_alter); break; @@ -1577,12 +1576,6 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objNameStr); break; - case obj_schema: - tableClause = "rdb$schemas"; - columnClause = "rdb$schema_name"; - status << Arg::Gds(isc_dyn_schema_not_found) << Arg::Str(objNameStr); - break; - default: fb_assert(false); return; @@ -1741,7 +1734,7 @@ void CreateAlterFunctionNode::checkPermission(thread_db* tdbb, jrd_tra* transact return; } - SCL_check_create_access(tdbb, SCL_object_function); + SCL_check_create_access(tdbb, obj_functions); } void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -2342,6 +2335,8 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch->cursorNumber = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -2352,7 +2347,7 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(false); dsqlScratch->appendUChar(blr_end); @@ -2747,7 +2742,7 @@ void CreateAlterProcedureNode::checkPermission(thread_db* tdbb, jrd_tra* transac return; } - SCL_check_create_access(tdbb, SCL_object_procedure); + SCL_check_create_access(tdbb, obj_procedures); } void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -2877,7 +2872,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); bool modified = false; - DsqlCompiledStatement* statement = dsqlScratch->getStatement(); + DsqlStatement* statement = dsqlScratch->getDsqlStatement(); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) P IN RDB$PROCEDURES @@ -2971,7 +2966,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch P.RDB$PROCEDURE_TYPE.NULL = FALSE; P.RDB$PROCEDURE_TYPE = (USHORT) - (statement->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ? + (statement->getFlags() & DsqlStatement::FLAG_SELECTABLE ? prc_selectable : prc_executable); } } @@ -3235,6 +3230,8 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch->cursorNumber = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -3245,7 +3242,7 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* stmtNode->genBlr(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); dsqlScratch->appendUChar(blr_end); @@ -3751,6 +3748,8 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d dsqlScratch->scopeLevel++; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); // dimitr: I see no reason to deny EXIT command in triggers, @@ -3774,7 +3773,7 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d // The statement type may have been set incorrectly when parsing // the trigger actions, so reset it to reflect the fact that this // is a data definition statement; also reset the ddl node. - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); } invalid = false; @@ -3944,7 +3943,7 @@ string CreateCollationNode::internalPrint(NodePrinter& printer) const void CreateCollationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_collation); + SCL_check_create_access(tdbb, obj_collations); } void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -4312,7 +4311,7 @@ string CreateDomainNode::internalPrint(NodePrinter& printer) const void CreateDomainNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_domain); + SCL_check_create_access(tdbb, obj_domains); } void CreateDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -5450,7 +5449,7 @@ void CreateAlterExceptionNode::checkPermission(thread_db* tdbb, jrd_tra* transac return; } - SCL_check_create_access(tdbb, SCL_object_exception); + SCL_check_create_access(tdbb, obj_exceptions); } void CreateAlterExceptionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -5657,7 +5656,7 @@ void CreateAlterSequenceNode::checkPermission(thread_db* tdbb, jrd_tra* transact return; } - SCL_check_create_access(tdbb, SCL_object_generator); + SCL_check_create_access(tdbb, obj_generators); } void CreateAlterSequenceNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -5976,8 +5975,13 @@ void DropSequenceNode::deleteIdentity(thread_db* tdbb, jrd_tra* transaction, con WITH GEN.RDB$GENERATOR_NAME EQ name.c_str() { ERASE GEN; + + if (!GEN.RDB$SECURITY_CLASS.NULL) + deleteSecurityClass(tdbb, transaction, GEN.RDB$SECURITY_CLASS); } END_FOR + + deletePrivilegesByRelName(tdbb, transaction, name, obj_generator); } @@ -6746,6 +6750,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc definition.unique = constraint.type != Constraint::TYPE_FK; if (constraint.index->descending) definition.descending = true; + definition.inactive = false; definition.columns = constraint.columns; definition.refRelation = constraint.refRelation; definition.refColumns = constraint.refColumns; @@ -7389,7 +7394,7 @@ string CreateRelationNode::internalPrint(NodePrinter& printer) const void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_table); + SCL_check_create_access(tdbb, obj_relations); } void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -8669,7 +8674,7 @@ void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) return; } - SCL_check_create_access(tdbb, SCL_object_view); + SCL_check_create_access(tdbb, obj_views); } void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, @@ -9830,6 +9835,7 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, definition.relation = relation->dsqlName; definition.unique = unique; definition.descending = descending; + definition.inactive = false; if (columns) { @@ -10090,7 +10096,7 @@ string CreateFilterNode::internalPrint(NodePrinter& printer) const void CreateFilterNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_filter); + SCL_check_create_access(tdbb, obj_filters); } // Define a blob filter. @@ -10346,7 +10352,7 @@ string CreateAlterRoleNode::internalPrint(NodePrinter& printer) const void CreateAlterRoleNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { if (createFlag) - SCL_check_create_access(tdbb, SCL_object_role); + SCL_check_create_access(tdbb, obj_roles); else SCL_check_role(tdbb, name, SCL_alter); } @@ -10734,7 +10740,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd (Arg::Gds(isc_adm_task_denied) << Arg::Gds(isc_miss_prvlg) << "CHANGE_MAPPING_RULES").raise(); if (from) - fromUtf8 = from->toUtf8(dsqlScratch); + fromUtf8 = from->toUtf8(transaction); if (global) { @@ -11512,10 +11518,10 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G const GranteeClause* userNod, const char* privs, MetaName field, int options) { - SSHORT userType = userNod->first; + ObjectType userType = userNod->first; MetaName user(userNod->second); MetaName dummyName; - const SSHORT objType = object ? object->first : obj_type_MAX; + const ObjectType objType = object ? object->first : obj_type_MAX; const MetaName objName(object ? object->second : ""); bool crdb = false; @@ -11667,7 +11673,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G break; default: - fb_assert(object == NULL || objType >= obj_database); + fb_assert(object == NULL || isDdlObject(objType)); } if (options == 1) // with grant option @@ -11871,10 +11877,8 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G } default: - if (objType >= obj_database) - { + if (isDdlObject(objType)) checkGrantorCanGrantDdl(tdbb, transaction, currentUser.c_str(), priv, objName); - } else fb_assert(false); } @@ -12296,12 +12300,16 @@ void GrantRevokeNode::checkGrantorCanGrantDdl(thread_db* tdbb, jrd_tra* transact WITH ((PRV.RDB$USER = UPPERCASE(grantor.c_str()) AND PRV.RDB$USER_TYPE = obj_user) OR (PRV.RDB$USER_TYPE = obj_sql_role)) AND PRV.RDB$RELATION_NAME EQ objName.c_str() AND - PRV.RDB$OBJECT_TYPE >= obj_database AND + PRV.RDB$OBJECT_TYPE >= obj_database AND // it might be deleted but I believe it's more efficient PRV.RDB$PRIVILEGE EQ privilege { if ( (PRV.RDB$USER_TYPE == obj_sql_role) && !attachment->att_user->roleInUse(tdbb, PRV.RDB$USER)) continue; + // Double check if the object is DDL one. + if (!isDdlObject(PRV.RDB$OBJECT_TYPE)) + continue; + if (PRV.RDB$GRANT_OPTION == WITH_GRANT_OPTION) { grantable = true; diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 3c07b01ac2..4686f94eab 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1129,8 +1129,8 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType( - legacy ? DsqlCompiledStatement::TYPE_SET_GENERATOR : DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType( + legacy ? DsqlStatement::TYPE_SET_GENERATOR : DsqlStatement::TYPE_DDL); return this; } @@ -2279,7 +2279,7 @@ typedef RecreateNode > PrivilegeClause; -typedef Firebird::Pair > GranteeClause; +typedef Firebird::Pair > GranteeClause; class GrantRevokeNode : public PrivilegesNode, private ExecInSecurityDb { @@ -2417,8 +2417,8 @@ public: public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType( - create ? DsqlCompiledStatement::TYPE_CREATE_DB : DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType( + create ? DsqlStatement::TYPE_CREATE_DB : DsqlStatement::TYPE_DDL); return this; } diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp index 81329807d6..10fafc3655 100644 --- a/src/dsql/DsqlBatch.cpp +++ b/src/dsql/DsqlBatch.cpp @@ -60,15 +60,15 @@ namespace { }; } -DsqlBatch::DsqlBatch(dsql_req* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) - : m_request(req), +DsqlBatch::DsqlBatch(DsqlDmlRequest* req, const dsql_msg* /*message*/, IMessageMetadata* inMeta, ClumpletReader& pb) + : m_dsqlRequest(req), m_batch(NULL), m_meta(inMeta), - m_messages(m_request->getPool()), - m_blobs(m_request->getPool()), - m_blobMap(m_request->getPool()), - m_blobMeta(m_request->getPool()), - m_defaultBpb(m_request->getPool()), + m_messages(m_dsqlRequest->getPool()), + m_blobs(m_dsqlRequest->getPool()), + m_blobMap(m_dsqlRequest->getPool()), + m_blobMeta(m_dsqlRequest->getPool()), + m_defaultBpb(m_dsqlRequest->getPool()), m_messageSize(0), m_alignedMessage(0), m_alignment(0), @@ -168,13 +168,13 @@ DsqlBatch::~DsqlBatch() { if (m_batch) m_batch->resetHandle(); - if (m_request) - m_request->req_batch = NULL; + if (m_dsqlRequest) + m_dsqlRequest->req_batch = NULL; } Attachment* DsqlBatch::getAttachment() const { - return m_request->req_dbb->dbb_attachment; + return m_dsqlRequest->req_dbb->dbb_attachment; } void DsqlBatch::setInterfacePtr(JBatch* interfacePtr) throw() @@ -183,7 +183,7 @@ void DsqlBatch::setInterfacePtr(JBatch* interfacePtr) throw() m_batch = interfacePtr; } -DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inMetadata, +DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadata* inMetadata, unsigned parLength, const UCHAR* par) { SET_TDBB(tdbb); @@ -205,15 +205,15 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inM // Sanity checks before creating batch - if (!req->req_request) + if (!req->getRequest()) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << Arg::Gds(isc_unprepared_stmt)); } - const DsqlCompiledStatement* statement = req->getStatement(); + const auto statement = req->getDsqlStatement(); - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -221,11 +221,11 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, dsql_req* req, IMessageMetadata* inM switch (statement->getType()) { - case DsqlCompiledStatement::TYPE_INSERT: - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_INSERT: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_EXEC_BLOCK: break; default: @@ -451,7 +451,7 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb) jrd_tra* transaction = tdbb->getTransaction(); // execution timer - thread_db::TimerGuard timerGuard(tdbb, m_request->setupTimer(tdbb), true); + thread_db::TimerGuard timerGuard(tdbb, m_dsqlRequest->setupTimer(tdbb), true); // sync internal buffers m_messages.done(); @@ -651,20 +651,20 @@ private: } // execute request - m_request->req_transaction = transaction; - jrd_req* req = m_request->req_request; + m_dsqlRequest->req_transaction = transaction; + Request* req = m_dsqlRequest->getRequest(); fb_assert(req); // prepare completion interface AutoPtr completionState (FB_NEW BatchCompletionState(m_flags & (1 << IBatch::TAG_RECORD_COUNTS), m_detailed)); AutoSetRestore batchFlag(&req->req_batch_mode, true); - const dsql_msg* message = m_request->getStatement()->getSendMsg(); + const dsql_msg* message = m_dsqlRequest->getDsqlStatement()->getSendMsg(); bool startRequest = true; - bool isExecBlock = m_request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK; - const auto receiveMessage = isExecBlock ? m_request->getStatement()->getReceiveMsg() : nullptr; - auto receiveMsgBuffer = isExecBlock ? m_request->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr; + bool isExecBlock = m_dsqlRequest->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_BLOCK; + const auto receiveMessage = isExecBlock ? m_dsqlRequest->getDsqlStatement()->getReceiveMsg() : nullptr; + auto receiveMsgBuffer = isExecBlock ? m_dsqlRequest->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr; // process messages ULONG remains; @@ -720,11 +720,11 @@ private: } // map message to internal engine format - m_request->mapInOut(tdbb, false, message, m_meta, NULL, data); + m_dsqlRequest->mapInOut(tdbb, false, message, m_meta, NULL, data); data += m_messageSize; remains -= m_messageSize; - UCHAR* msgBuffer = m_request->req_msg_buffers[message->msg_buffer_number]; + UCHAR* msgBuffer = m_dsqlRequest->req_msg_buffers[message->msg_buffer_number]; try { // runsend data to request and collect stats diff --git a/src/dsql/DsqlBatch.h b/src/dsql/DsqlBatch.h index 187fc34e4b..b3ef18d9d4 100644 --- a/src/dsql/DsqlBatch.h +++ b/src/dsql/DsqlBatch.h @@ -41,7 +41,7 @@ class ClumpletReader; namespace Jrd { -class dsql_req; +class DsqlDmlRequest; class dsql_msg; class thread_db; class JBatch; @@ -50,7 +50,7 @@ class Attachment; class DsqlBatch { public: - DsqlBatch(dsql_req* req, const dsql_msg* message, Firebird::IMessageMetadata* inMetadata, + DsqlBatch(DsqlDmlRequest* req, const dsql_msg* message, Firebird::IMessageMetadata* inMetadata, Firebird::ClumpletReader& pb); ~DsqlBatch(); @@ -61,7 +61,7 @@ public: static const ULONG SIZEOF_BLOB_HEAD = sizeof(ISC_QUAD) + 2 * sizeof(ULONG); static const unsigned BLOB_STREAM_ALIGN = 4; - static DsqlBatch* open(thread_db* tdbb, dsql_req* req, Firebird::IMessageMetadata* inMetadata, + static DsqlBatch* open(thread_db* tdbb, DsqlDmlRequest* req, Firebird::IMessageMetadata* inMetadata, unsigned parLength, const UCHAR* par); Attachment* getAttachment() const; @@ -101,7 +101,7 @@ private: m_flags &= ~(1 << bit); } - dsql_req* const m_request; + DsqlDmlRequest* const m_dsqlRequest; JBatch* m_batch; Firebird::IMessageMetadata* m_meta; diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index f1550fe348..f3c21dbc06 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -345,30 +345,24 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE)) { // Check not implemented sub-functions. - - GenericMap >::ConstAccessor funcAccessor(&subFunctions); - - for (bool found = funcAccessor.getFirst(); found; found = funcAccessor.getNext()) + for (const auto& funcPair : subFunctions) { - if (!funcAccessor.current()->second->dsqlBlock) + if (!funcPair.second->dsqlBlock) { status_exception::raise( Arg::Gds(isc_subfunc_not_impl) << - funcAccessor.current()->first.c_str()); + funcPair.first.c_str()); } } // Check not implemented sub-procedures. - - GenericMap >::ConstAccessor procAccessor(&subProcedures); - - for (bool found = procAccessor.getFirst(); found; found = procAccessor.getNext()) + for (const auto& procPair : subProcedures) { - if (!procAccessor.current()->second->dsqlBlock) + if (!procPair.second->dsqlBlock) { status_exception::raise( Arg::Gds(isc_subproc_not_impl) << - procAccessor.current()->first.c_str()); + procPair.first.c_str()); } } } @@ -430,6 +424,31 @@ void DsqlCompilerScratch::putLocalVariable(dsql_var* variable, const DeclareVari ++hiddenVarsNumber; } +// Put maps in subroutines for outer variables/parameters usage. +void DsqlCompilerScratch::putOuterMaps() +{ + if (!outerMessagesMap.count() && !outerVarsMap.count()) + return; + + appendUChar(blr_outer_map); + + for (auto& pair : outerVarsMap) + { + appendUChar(blr_outer_map_variable); + appendUShort(pair.first); + appendUShort(pair.second); + } + + for (auto& pair : outerMessagesMap) + { + appendUChar(blr_outer_map_message); + appendUShort(pair.first); + appendUShort(pair.second); + } + + appendUChar(blr_end); +} + // Make a variable. dsql_var* DsqlCompilerScratch::makeVariable(dsql_fld* field, const char* name, const dsql_var::Type type, USHORT msgNumber, USHORT itemNumber, USHORT localNumber) diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index 632fbccb13..12e4fd76b3 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -24,6 +24,7 @@ #include "../jrd/jrd.h" #include "../dsql/dsql.h" +#include "../dsql/DsqlStatements.h" #include "../dsql/BlrDebugWriter.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" @@ -73,14 +74,13 @@ public: public: DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction, - DsqlCompiledStatement* aStatement, DsqlCompilerScratch* aMainScratch = NULL) + DsqlStatement* aDsqlStatement = nullptr, DsqlCompilerScratch* aMainScratch = nullptr) : BlrDebugWriter(p), dbb(aDbb), transaction(aTransaction), - statement(aStatement), + dsqlStatement(aDsqlStatement), flags(0), nestingLevel(0), - ports(p), relation(NULL), mainContext(p), context(&mainContext), @@ -117,10 +117,12 @@ public: outputVariables(p), returningClause(nullptr), currCteAlias(NULL), + mainScratch(aMainScratch), + outerMessagesMap(p), + outerVarsMap(p), ctes(p), cteAliases(p), psql(false), - mainScratch(aMainScratch), subFunctions(p), subProcedures(p) { @@ -142,7 +144,7 @@ public: public: virtual bool isVersion4() { - return statement->getBlrVersion() == 4; + return dsqlStatement->getBlrVersion() == 4; } MemoryPool& getPool() @@ -165,14 +167,14 @@ public: transaction = value; } - DsqlCompiledStatement* getStatement() + DsqlStatement* getDsqlStatement() const { - return statement; + return dsqlStatement; } - DsqlCompiledStatement* getStatement() const + void setDsqlStatement(DsqlStatement* aDsqlStatement) { - return statement; + dsqlStatement = aDsqlStatement; } void putBlrMarkers(ULONG marks); @@ -181,6 +183,7 @@ public: void putLocalVariables(CompoundStmtNode* parameters, USHORT locals); void putLocalVariable(dsql_var* variable, const DeclareVariableNode* hostParam, const MetaName& collationName); + void putOuterMaps(); dsql_var* makeVariable(dsql_fld*, const char*, const dsql_var::Type type, USHORT, USHORT, USHORT); dsql_var* resolveVariable(const MetaName& varName); @@ -269,12 +272,11 @@ private: dsql_dbb* dbb; // DSQL attachment jrd_tra* transaction; // Transaction - DsqlCompiledStatement* statement; // Compiled statement + DsqlStatement* dsqlStatement; // DSQL statement public: unsigned flags; // flags unsigned nestingLevel; // begin...end nesting level - Firebird::Array ports; // Port messages dsql_rel* relation; // relation created by this request (for DDL) DsqlContextStack mainContext; DsqlContextStack* context; @@ -313,14 +315,16 @@ public: Firebird::Array outputVariables; ReturningClause* returningClause; const Firebird::string* const* currCteAlias; + DsqlCompilerScratch* mainScratch; + Firebird::NonPooledMap outerMessagesMap; // + Firebird::NonPooledMap outerVarsMap; // private: Firebird::HalfStaticArray ctes; // common table expressions Firebird::HalfStaticArray cteAliases; // CTE aliases in recursive members bool psql; - DsqlCompilerScratch* mainScratch; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + Firebird::LeftPooledMap subFunctions; + Firebird::LeftPooledMap subProcedures; }; class PsqlChanger diff --git a/src/dsql/DsqlCursor.cpp b/src/dsql/DsqlCursor.cpp index ada2b4973b..5c64381724 100644 --- a/src/dsql/DsqlCursor.cpp +++ b/src/dsql/DsqlCursor.cpp @@ -35,13 +35,13 @@ using namespace Jrd; static const char* const SCRATCH = "fb_cursor_"; static const ULONG PREFETCH_SIZE = 65536; // 64 KB -DsqlCursor::DsqlCursor(dsql_req* req, ULONG flags) - : m_request(req), m_message(req->getStatement()->getReceiveMsg()), +DsqlCursor::DsqlCursor(DsqlDmlRequest* req, ULONG flags) + : m_dsqlRequest(req), m_message(req->getDsqlStatement()->getReceiveMsg()), m_resultSet(NULL), m_flags(flags), m_space(req->getPool(), SCRATCH), m_state(BOS), m_eof(false), m_position(0), m_cachedCount(0) { - TRA_link_cursor(m_request->req_transaction, this); + TRA_link_cursor(m_dsqlRequest->req_transaction, this); } DsqlCursor::~DsqlCursor() @@ -52,12 +52,12 @@ DsqlCursor::~DsqlCursor() jrd_tra* DsqlCursor::getTransaction() const { - return m_request->req_transaction; + return m_dsqlRequest->req_transaction; } Attachment* DsqlCursor::getAttachment() const { - return m_request->req_dbb->dbb_attachment; + return m_dsqlRequest->req_dbb->dbb_attachment; } void DsqlCursor::setInterfacePtr(JResultSet* interfacePtr) throw() @@ -71,35 +71,35 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) if (!cursor) return; - Jrd::Attachment* const attachment = cursor->getAttachment(); - dsql_req* const request = cursor->m_request; + const auto attachment = cursor->getAttachment(); + const auto dsqlRequest = cursor->m_dsqlRequest; - if (request->req_request) + if (dsqlRequest->getRequest()) { ThreadStatusGuard status_vector(tdbb); try { // Report some remaining fetches if any - if (request->req_fetch_baseline) + if (dsqlRequest->req_fetch_baseline) { - TraceDSQLFetch trace(attachment, request); + TraceDSQLFetch trace(attachment, dsqlRequest); trace.fetch(true, ITracePlugin::RESULT_SUCCESS); } - if (request->req_traced && TraceManager::need_dsql_free(attachment)) + if (dsqlRequest->req_traced && TraceManager::need_dsql_free(attachment)) { - TraceSQLStatementImpl stmt(request, NULL); + TraceSQLStatementImpl stmt(dsqlRequest, NULL); TraceManager::event_dsql_free(attachment, &stmt, DSQL_close); } - JRD_unwind_request(tdbb, request->req_request); + JRD_unwind_request(tdbb, dsqlRequest->getRequest()); } catch (Firebird::Exception&) {} // no-op } - request->req_cursor = NULL; - TRA_unlink_cursor(request->req_transaction, cursor); + dsqlRequest->req_cursor = NULL; + TRA_unlink_cursor(dsqlRequest->req_transaction, cursor); delete cursor; } @@ -107,7 +107,7 @@ int DsqlCursor::fetchNext(thread_db* tdbb, UCHAR* buffer) { if (!(m_flags & IStatement::CURSOR_TYPE_SCROLLABLE)) { - m_eof = !m_request->fetch(tdbb, buffer); + m_eof = !m_dsqlRequest->fetch(tdbb, buffer); if (m_eof) { @@ -300,13 +300,13 @@ int DsqlCursor::fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 positio fb_assert(position < m_cachedCount); - UCHAR* const msgBuffer = m_request->req_msg_buffers[m_message->msg_buffer_number]; + UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number]; const FB_UINT64 offset = position * m_message->msg_length; const FB_UINT64 readBytes = m_space.read(offset, msgBuffer, m_message->msg_length); fb_assert(readBytes == m_message->msg_length); - m_request->mapInOut(tdbb, true, m_message, NULL, buffer); + m_dsqlRequest->mapInOut(tdbb, true, m_message, NULL, buffer); m_position = position; m_state = POSITIONED; @@ -318,13 +318,13 @@ bool DsqlCursor::cacheInput(thread_db* tdbb, FB_UINT64 position) fb_assert(!m_eof); const ULONG prefetchCount = MAX(PREFETCH_SIZE / m_message->msg_length, 1); - const UCHAR* const msgBuffer = m_request->req_msg_buffers[m_message->msg_buffer_number]; + const UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number]; while (position >= m_cachedCount) { for (ULONG count = 0; count < prefetchCount; count++) { - if (!m_request->fetch(tdbb, NULL)) + if (!m_dsqlRequest->fetch(tdbb, NULL)) { m_eof = true; break; diff --git a/src/dsql/DsqlCursor.h b/src/dsql/DsqlCursor.h index 05bc28694c..6bd652a6aa 100644 --- a/src/dsql/DsqlCursor.h +++ b/src/dsql/DsqlCursor.h @@ -27,7 +27,7 @@ namespace Jrd { -class dsql_req; +class DsqlDmlRequest; class JResultSet; class DsqlCursor @@ -35,7 +35,7 @@ class DsqlCursor enum State { BOS, POSITIONED, EOS }; public: - DsqlCursor(dsql_req* req, ULONG flags); + DsqlCursor(DsqlDmlRequest* req, ULONG flags); ~DsqlCursor(); jrd_tra* getTransaction() const; @@ -69,7 +69,7 @@ private: int fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position); bool cacheInput(thread_db* tdbb, FB_UINT64 position = MAX_UINT64); - dsql_req* const m_request; + DsqlDmlRequest* const m_dsqlRequest; const dsql_msg* const m_message; JResultSet* m_resultSet; const ULONG m_flags; diff --git a/src/dsql/DsqlRequests.cpp b/src/dsql/DsqlRequests.cpp new file mode 100644 index 0000000000..baeea0a8df --- /dev/null +++ b/src/dsql/DsqlRequests.cpp @@ -0,0 +1,1125 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.cpp + */ + +#include "firebird.h" +#include "../dsql/DsqlRequests.h" +#include "../dsql/dsql.h" +#include "../dsql/DsqlBatch.h" +///#include "../dsql/DsqlStatementCache.h" +#include "../dsql/Nodes.h" +#include "../jrd/Statement.h" +#include "../jrd/req.h" +#include "../jrd/tra.h" +#include "../jrd/replication/Publisher.h" +#include "../jrd/trace/TraceDSQLHelpers.h" +#include "../jrd/trace/TraceObjects.h" +#include "../dsql/errd_proto.h" +#include "../dsql/movd_proto.h" +#include "../jrd/exe_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +static void checkD(IStatus* st); + + +// DsqlRequest + +DsqlRequest::DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aDsqlStatement) + : PermanentStorage(pool), + req_dbb(dbb), + dsqlStatement(aDsqlStatement) +{ +} + +DsqlRequest::~DsqlRequest() +{ +} + +void DsqlRequest::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); +} + +void DsqlRequest::setDelayedFormat(thread_db* /*tdbb*/, IMessageMetadata* /*metadata*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); +} + +bool DsqlRequest::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/) +{ + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); + + return false; // avoid warning +} + +unsigned int DsqlRequest::getTimeout() +{ + return req_timeout; +} + +unsigned int DsqlRequest::getActualTimeout() +{ + if (req_timer) + return req_timer->getValue(); + + return 0; +} + +void DsqlRequest::setTimeout(unsigned int timeOut) +{ + req_timeout = timeOut; +} + +TimeoutTimer* DsqlRequest::setupTimer(thread_db* tdbb) +{ + auto request = getRequest(); + + if (request) + { + if (request->hasInternalStatement()) + return req_timer; + + request->req_timeout = this->req_timeout; + + fb_assert(!request->req_caller); + if (request->req_caller) + { + if (req_timer) + req_timer->setup(0, 0); + return req_timer; + } + } + + Database* dbb = tdbb->getDatabase(); + Attachment* att = tdbb->getAttachment(); + + ISC_STATUS toutErr = isc_cfg_stmt_timeout; + unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000; + + if (req_timeout) + { + if (!timeOut || req_timeout < timeOut) + { + timeOut = req_timeout; + toutErr = isc_req_stmt_timeout; + } + } + else + { + const unsigned int attTout = att->getStatementTimeout(); + + if (!timeOut || attTout && attTout < timeOut) + { + timeOut = attTout; + toutErr = isc_att_stmt_timeout; + } + } + + if (!req_timer && timeOut) + { + req_timer = FB_NEW TimeoutTimer(); + fb_assert(request); + request->req_timer = this->req_timer; + } + + if (req_timer) + { + req_timer->setup(timeOut, toutErr); + req_timer->start(); + } + + return req_timer; +} + +// Release a dynamic request. +void DsqlRequest::destroy(thread_db* tdbb, DsqlRequest* dsqlRequest) +{ + SET_TDBB(tdbb); + + if (dsqlRequest->req_timer) + { + dsqlRequest->req_timer->stop(); + dsqlRequest->req_timer = nullptr; + } + + // If request is parent, orphan the children and release a portion of their requests + + for (auto childStatement : dsqlRequest->cursors) + { + childStatement->addFlags(DsqlStatement::FLAG_ORPHAN); + childStatement->setParentRequest(nullptr); + + // hvlad: lines below is commented out as + // - child is already unlinked from its parent request + // - we should not free child's sql text until its owner request is alive + // It seems to me we should destroy owner request here, not a child + // statement - as it always was before + + //Jrd::ContextPoolHolder context(tdbb, &childStatement->getPool()); + //releaseStatement(childStatement); + } + + // If the request had an open cursor, close it + + if (dsqlRequest->req_cursor) + DsqlCursor::close(tdbb, dsqlRequest->req_cursor); + + if (dsqlRequest->req_batch) + { + delete dsqlRequest->req_batch; + dsqlRequest->req_batch = nullptr; + } + + Jrd::Attachment* att = dsqlRequest->req_dbb->dbb_attachment; + const bool need_trace_free = dsqlRequest->req_traced && TraceManager::need_dsql_free(att); + if (need_trace_free) + { + TraceSQLStatementImpl stmt(dsqlRequest, NULL); + TraceManager::event_dsql_free(att, &stmt, DSQL_drop); + } + + if (dsqlRequest->req_cursor_name.hasData()) + dsqlRequest->req_dbb->dbb_cursors.remove(dsqlRequest->req_cursor_name); + + // If a request has been compiled, release it now + if (dsqlRequest->getRequest()) + EXE_release(tdbb, dsqlRequest->getRequest()); + + // Increase the statement refCount so its pool is not destroyed before the request is gone. + auto dsqlStatement = dsqlRequest->getDsqlStatement(); + + // Release the entire request + delete dsqlRequest; + + dsqlStatement = nullptr; +} + +// Parse the message of a request. +USHORT DsqlRequest::parseMetadata(IMessageMetadata* meta, const Array& parameters_list) +{ + HalfStaticArray parameters; + + for (FB_SIZE_T i = 0; i < parameters_list.getCount(); ++i) + { + dsql_par* param = parameters_list[i]; + + if (param->par_index) + { + if (param->par_index > parameters.getCount()) + parameters.grow(param->par_index); + fb_assert(!parameters[param->par_index - 1]); + parameters[param->par_index - 1] = param; + } + } + + // If there's no metadata, then the format of the current message buffer + // is identical to the format of the previous one. + + if (!meta) + return parameters.getCount(); + + FbLocalStatus st; + unsigned count = meta->getCount(&st); + checkD(&st); + + unsigned count2 = parameters.getCount(); + + if (count != count2) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_wrong_param_num) <getType(&st, index); + checkD(&st); + unsigned sqlLength = meta->getLength(&st, index); + checkD(&st); + + dsc desc; + desc.dsc_flags = 0; + + unsigned dataOffset, nullOffset, dtype, dlength; + offset = fb_utils::sqlTypeToDsc(offset, sqlType, sqlLength, + &dtype, &dlength, &dataOffset, &nullOffset); + desc.dsc_dtype = dtype; + desc.dsc_length = dlength; + + desc.dsc_scale = meta->getScale(&st, index); + checkD(&st); + desc.dsc_sub_type = meta->getSubType(&st, index); + checkD(&st); + unsigned textType = meta->getCharSet(&st, index); + checkD(&st); + desc.setTextType(textType); + desc.dsc_address = (UCHAR*)(IPTR) dataOffset; + + const dsql_par* const parameter = parameters[index]; + fb_assert(parameter); + + // ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been + // implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started + // failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using + // blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset + // here at the server as a way to make older (and not yet changed) client work + // correctly. + if (desc.isText() && desc.getTextType() == ttype_dynamic) + desc.setTextType(ttype_none); + + req_user_descs.put(parameter, desc); + + dsql_par* null = parameter->par_null; + if (null) + { + desc.clear(); + desc.dsc_dtype = dtype_short; + desc.dsc_scale = 0; + desc.dsc_length = sizeof(SSHORT); + desc.dsc_address = (UCHAR*)(IPTR) nullOffset; + + req_user_descs.put(null, desc); + } + } + + return count; +} + + +// DsqlDmlRequest + +DsqlDmlRequest::DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement) + : DsqlRequest(pool, dbb, aStatement), + req_msg_buffers(pool) +{ + // Create the messages buffers + for (auto message : aStatement->getPorts()) + { + // Allocate buffer for message + const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1; + UCHAR* msgBuffer = FB_NEW_POOL(getPool()) UCHAR[newLen]; + msgBuffer = FB_ALIGN(msgBuffer, FB_DOUBLE_ALIGN); + fb_assert(message->msg_buffer_number == req_msg_buffers.getCount()); + req_msg_buffers.add(msgBuffer); + } + + request = aStatement->getStatement()->findRequest(tdbb); + tdbb->getAttachment()->att_requests.add(request); +} + +Statement* DsqlDmlRequest::getStatement() const +{ + return request ? request->getStatement() : nullptr; +} + +// Provide backward-compatibility +void DsqlDmlRequest::setDelayedFormat(thread_db* tdbb, IMessageMetadata* metadata) +{ + if (!needDelayedFormat) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-804) << + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_req_sync)); + } + + needDelayedFormat = false; + delayedFormat = metadata; +} + +// Fetch next record from a dynamic SQL cursor. +bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + // if the cursor isn't open, we've got a problem + if (dsqlStatement->isCursorBased()) + { + if (!req_cursor) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_dsql_cursor_err) << + Arg::Gds(isc_dsql_cursor_not_open)); + } + } + + if (!request) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_unprepared_stmt)); + } + + dsql_msg* message = (dsql_msg*) dsqlStatement->getReceiveMsg(); + + if (delayedFormat && message) + { + parseMetadata(delayedFormat, message->msg_parameters); + delayedFormat = NULL; + } + + // Set up things for tracing this call + Jrd::Attachment* att = req_dbb->dbb_attachment; + TraceDSQLFetch trace(att, this); + + thread_db::TimerGuard timerGuard(tdbb, req_timer, false); + if (req_timer && req_timer->expired()) + tdbb->checkCancelState(); + + UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; + if (!firstRowFetched && needRestarts()) + { + // Note: tra_handle can't be changed by executeReceiveWithRestarts below + // and outMetadata and outMsg in not used there, so passing NULL's is safe. + jrd_tra* tra = req_transaction; + + executeReceiveWithRestarts(tdbb, &tra, NULL, NULL, false, false, true); + fb_assert(tra == req_transaction); + } + else + JRD_receive(tdbb, request, message->msg_number, message->msg_length, dsqlMsgBuffer); + + firstRowFetched = true; + + const dsql_par* const eof = dsqlStatement->getEof(); + const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; + const bool eofReached = eof && !(*eofPtr); + + if (eofReached) + { + if (req_timer) + req_timer->stop(); + + trace.fetch(true, ITracePlugin::RESULT_SUCCESS); + return false; + } + + if (msgBuffer) + mapInOut(tdbb, true, message, NULL, msgBuffer); + + trace.fetch(false, ITracePlugin::RESULT_SUCCESS); + return true; +} + +// Set a cursor name for a dynamic request. +void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + const size_t MAX_CURSOR_LENGTH = 132 - 1; + string cursor = name; + + if (cursor.hasData() && cursor[0] == '\"') + { + // Quoted cursor names eh? Strip'em. + // Note that "" will be replaced with ". + // The code is very strange, because it doesn't check for "" really + // and thus deletes one isolated " in the middle of the cursor. + for (string::iterator i = cursor.begin(); i < cursor.end(); ++i) + { + if (*i == '\"') + cursor.erase(i); + } + } + else // not quoted name + { + const string::size_type i = cursor.find(' '); + if (i != string::npos) + cursor.resize(i); + + cursor.upper(); + } + + USHORT length = (USHORT) fb_utils::name_length(cursor.c_str()); + + if (!length) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_invalid)); + } + + if (length > MAX_CURSOR_LENGTH) + length = MAX_CURSOR_LENGTH; + + cursor.resize(length); + + // If there already is a different cursor by the same name, bitch + + auto* const* symbol = req_dbb->dbb_cursors.get(cursor); + if (symbol) + { + if (this == *symbol) + return; + + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_redefined) << cursor); + } + + // If there already is a cursor and its name isn't the same, ditto. + // We already know there is no cursor by this name in the hash table + + if (req_cursor && req_cursor_name.hasData()) + { + fb_assert(!symbol); + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_decl_err) << + Arg::Gds(isc_dsql_cursor_redefined) << req_cursor_name); + } + + if (req_cursor_name.hasData()) + req_dbb->dbb_cursors.remove(req_cursor_name); + req_cursor_name = cursor; + req_dbb->dbb_cursors.put(cursor, this); +} + +// Open a dynamic SQL cursor. +DsqlCursor* DsqlDmlRequest::openCursor(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMeta, const UCHAR* inMsg, IMessageMetadata* outMeta, ULONG flags) +{ + SET_TDBB(tdbb); + + Jrd::ContextPoolHolder context(tdbb, &getPool()); + + if (dsqlStatement->getFlags() & DsqlStatement::FLAG_ORPHAN) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_bad_req_handle)); + } + + // Validate transaction handle + + if (!*traHandle) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << + Arg::Gds(isc_bad_trans_handle)); + } + + // Validate statement type + + if (!dsqlStatement->isCursorBased()) + Arg::Gds(isc_no_cursor).raise(); + + // Validate cursor or batch being not already open + + if (req_cursor) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)); + } + + if (req_batch) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_batch_open)); + } + + req_transaction = *traHandle; + execute(tdbb, traHandle, inMeta, inMsg, outMeta, NULL, false); + + req_cursor = FB_NEW_POOL(getPool()) DsqlCursor(this, flags); + + return req_cursor; +} + +bool DsqlDmlRequest::needRestarts() +{ + return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency)); +}; + +// Execute a dynamic SQL statement +void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + firstRowFetched = false; + const dsql_msg* message = dsqlStatement->getSendMsg(); + + if (!message) + JRD_start(tdbb, request, req_transaction); + else + { + UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; + JRD_start_and_send(tdbb, request, req_transaction, message->msg_number, + message->msg_length, msgBuffer); + } + + // Selectable execute block should get the "proc fetch" flag assigned, + // which ensures that the savepoint stack is preserved while suspending + if (dsqlStatement->getType() == DsqlStatement::TYPE_SELECT_BLOCK) + request->req_flags |= req_proc_fetch; + + // TYPE_EXEC_BLOCK has no outputs so there are no out_msg + // supplied from client side, but TYPE_EXEC_BLOCK requires + // 2-byte message for EOS synchronization + const bool isBlock = (dsqlStatement->getType() == DsqlStatement::TYPE_EXEC_BLOCK); + + message = dsqlStatement->getReceiveMsg(); + + if (outMetadata == DELAYED_OUT_FORMAT) + { + needDelayedFormat = true; + outMetadata = NULL; + } + + if (outMetadata && message) + parseMetadata(outMetadata, message->msg_parameters); + + if ((outMsg && message) || isBlock) + { + UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2]; + dsql_msg temp_msg(*getDefaultMemoryPool()); + + // Insure that the metadata for the message is parsed, regardless of + // whether anything is found by the call to receive. + + UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; + + if (!outMetadata && isBlock) + { + message = &temp_msg; + temp_msg.msg_number = 1; + temp_msg.msg_length = 2; + msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); + } + + JRD_receive(tdbb, request, message->msg_number, message->msg_length, msgBuffer); + + if (outMsg) + mapInOut(tdbb, true, message, NULL, outMsg); + + // if this is a singleton select, make sure there's in fact one record + + if (singleton) + { + USHORT counter; + + // Create a temp message buffer and try two more receives. + // If both succeed then the first is the next record and the + // second is either another record or the end of record message. + // In either case, there's more than one record. + + UCHAR* message_buffer = (UCHAR*) gds__alloc(message->msg_length); + + ISC_STATUS status = FB_SUCCESS; + FbLocalStatus localStatus; + + for (counter = 0; counter < 2 && !status; counter++) + { + localStatus->init(); + AutoSetRestore autoStatus(&tdbb->tdbb_status_vector, &localStatus); + + try + { + JRD_receive(tdbb, request, message->msg_number, + message->msg_length, message_buffer); + status = FB_SUCCESS; + } + catch (Exception&) + { + status = tdbb->tdbb_status_vector->getErrors()[1]; + } + } + + gds__free(message_buffer); + + // two successful receives means more than one record + // a req_sync error on the first pass above means no records + // a non-req_sync error on any of the passes above is an error + + if (!status) + status_exception::raise(Arg::Gds(isc_sing_select_err)); + else if (status == isc_req_sync && counter == 1) + status_exception::raise(Arg::Gds(isc_stream_eof)); + else if (status != isc_req_sync) + status_exception::raise(&localStatus); + } + } + + switch (dsqlStatement->getType()) + { + case DsqlStatement::TYPE_UPDATE_CURSOR: + if (!request->req_records_updated) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << + Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict)); + } + break; + + case DsqlStatement::TYPE_DELETE_CURSOR: + if (!request->req_records_deleted) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << + Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict)); + } + break; + } +} + +DsqlBatch* DsqlDmlRequest::openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) +{ + return DsqlBatch::open(tdbb, this, inMetadata, parLength, par); +} + +// Execute a dynamic SQL statement with tracing, restart and timeout handler +void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + if (!request) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_unprepared_stmt)); + } + + // If there is no data required, just start the request + + const dsql_msg* message = dsqlStatement->getSendMsg(); + if (message) + mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); + + // we need to mapInOut() before tracing of execution start to let trace + // manager know statement parameters values + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + + // Setup and start timeout timer + const bool have_cursor = dsqlStatement->isCursorBased() && !singleton; + + setupTimer(tdbb); + thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); + + if (needRestarts()) + executeReceiveWithRestarts(tdbb, traHandle, outMetadata, outMsg, singleton, true, false); + else { + doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); + } + + trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS); +} + +void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton, bool exec, bool fetch) +{ + request->req_flags &= ~req_update_conflict; + int numTries = 0; + const int MAX_RESTARTS = 10; + + while (true) + { + AutoSavePoint savePoint(tdbb, req_transaction); + + // Don't set req_restart_ready flag at last attempt to restart request. + // It allows to raise update conflict error (if any) as usual and + // handle error by PSQL handler. + const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; + AutoSetRestoreFlag restartReady(&request->req_flags, flag, true); + try + { + if (exec) + doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); + + if (fetch) + { + fb_assert(dsqlStatement->isCursorBased()); + + const dsql_msg* message = dsqlStatement->getReceiveMsg(); + + UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; + JRD_receive(tdbb, request, message->msg_number, message->msg_length, dsqlMsgBuffer); + } + } + catch (const status_exception&) + { + if (!(req_transaction->tra_flags & TRA_ex_restart)) + { + request->req_flags &= ~req_update_conflict; + throw; + } + } + + if (!(request->req_flags & req_update_conflict)) + { + fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); + req_transaction->tra_flags &= ~TRA_ex_restart; + +#ifdef DEV_BUILD + if (numTries > 0) + { + string s; + s.printf("restarts = %d", numTries); + + ERRD_post_warning(Arg::Warning(isc_random) << Arg::Str(s)); + } +#endif + savePoint.release(); // everything is ok + break; + } + + fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); + + request->req_flags &= ~req_update_conflict; + req_transaction->tra_flags &= ~TRA_ex_restart; + fb_utils::init_status(tdbb->tdbb_status_vector); + + // Undo current savepoint but preserve already taken locks. + // Savepoint will be restarted at the next loop iteration. + savePoint.rollback(true); + + numTries++; + if (numTries >= MAX_RESTARTS) + { + gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" + "\tafter %d attempts of restart.\n" + "\tQuery:\n%s\n", numTries, request->getStatement()->sqlText->c_str() ); + } + + // When restart we must execute query + exec = true; + } +} + +// Map data from external world into message or from message to external world. +void DsqlDmlRequest::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* message, + IMessageMetadata* meta, UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf) +{ + USHORT count = parseMetadata(meta, message->msg_parameters); + + // Sanity check + + if (count) + { + if (toExternal) + { + if (dsql_msg_buf == NULL) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_no_output_sqlda)); + } + } + else + { + if (in_dsql_msg_buf == NULL) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_no_input_sqlda)); + } + } + } + + USHORT count2 = 0; + + for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i) + { + dsql_par* parameter = message->msg_parameters[i]; + + if (parameter->par_index) + { + // Make sure the message given to us is long enough + + dsc desc; + if (!req_user_descs.get(parameter, desc)) + desc.clear(); + + /*** + ULONG length = (IPTR) desc.dsc_address + desc.dsc_length; + if (length > msg_length) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_random) << "Message buffer too short"); + } + ***/ + if (!desc.dsc_dtype) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_datatype_err) << + Arg::Gds(isc_dsql_sqlvar_index) << Arg::Num(parameter->par_index-1)); + } + + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + SSHORT* flag = NULL; + dsql_par* const null_ind = parameter->par_null; + if (null_ind != NULL) + { + dsc userNullDesc; + if (!req_user_descs.get(null_ind, userNullDesc)) + userNullDesc.clear(); + + const ULONG null_offset = (IPTR) userNullDesc.dsc_address; + + /*** + length = null_offset + sizeof(SSHORT); + if (length > msg_length) + { + ERRD_post(Arg::Gds(isc_dsql_sqlda_err) + << Arg::Gds(isc_random) << "Message buffer too short"); + } + ***/ + + dsc nullDesc = null_ind->par_desc; + nullDesc.dsc_address = msgBuffer + (IPTR) nullDesc.dsc_address; + + if (toExternal) + { + flag = reinterpret_cast(dsql_msg_buf + null_offset); + *flag = *reinterpret_cast(nullDesc.dsc_address); + } + else + { + flag = reinterpret_cast(nullDesc.dsc_address); + *flag = *reinterpret_cast(in_dsql_msg_buf + null_offset); + } + } + + const bool notNull = (!flag || *flag >= 0); + + dsc parDesc = parameter->par_desc; + parDesc.dsc_address = msgBuffer + (IPTR) parDesc.dsc_address; + + if (toExternal) + { + desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; + + if (notNull) + MOVD_move(tdbb, &parDesc, &desc); + else + memset(desc.dsc_address, 0, desc.dsc_length); + } + else if (notNull && !parDesc.isNull()) + { + // Safe cast because desc is used as source only. + desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; + MOVD_move(tdbb, &desc, &parDesc); + } + else + memset(parDesc.dsc_address, 0, parDesc.dsc_length); + + ++count2; + } + } + + if (count != count2) + { + ERRD_post( + Arg::Gds(isc_dsql_sqlda_err) << + Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && + (parameter = dsqlStatement->getDbKey())) + { + UCHAR* parentMsgBuffer = dsqlStatement->getParentRequest() ? + dsqlStatement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + fb_assert(parentMsgBuffer); + + dsc parentDesc = dbkey->par_desc; + parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; + + dsc desc = parameter->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + MOVD_move(tdbb, &parentDesc, &desc); + + dsql_par* null_ind = parameter->par_null; + if (null_ind != NULL) + { + desc = null_ind->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + SSHORT* flag = (SSHORT*) desc.dsc_address; + *flag = 0; + } + } + + const dsql_par* rec_version; + if (!toExternal && (rec_version = dsqlStatement->getParentRecVersion()) && + (parameter = dsqlStatement->getRecVersion())) + { + UCHAR* parentMsgBuffer = dsqlStatement->getParentRequest() ? + dsqlStatement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : + NULL; + UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; + + fb_assert(parentMsgBuffer); + + dsc parentDesc = rec_version->par_desc; + parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; + + dsc desc = parameter->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + MOVD_move(tdbb, &parentDesc, &desc); + + dsql_par* null_ind = parameter->par_null; + if (null_ind != NULL) + { + desc = null_ind->par_desc; + desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; + + SSHORT* flag = (SSHORT*) desc.dsc_address; + *flag = 0; + } + } +} + + +// DsqlDdlRequest + +DsqlDdlRequest::DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode) + : DsqlRequest(pool, dbb, aInternalScratch->getDsqlStatement()), + internalScratch(aInternalScratch), + node(aNode) +{ +} + +// Execute a dynamic SQL statement. +void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + + fb_utils::init_status(tdbb->tdbb_status_vector); + + // run all statements under savepoint control + { // scope + AutoSavePoint savePoint(tdbb, req_transaction); + + try + { + AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); + + //// Doing it in DFW_perform_work to avoid problems with DDL+DML in the same transaction. + ///req_dbb->dbb_attachment->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); + + node->executeDdl(tdbb, internalScratch, req_transaction); + + const bool isInternalRequest = + (internalScratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST); + + if (!isInternalRequest && node->mustBeReplicated()) + REPL_exec_sql(tdbb, req_transaction, getDsqlStatement()->getOrgText()); + } + catch (status_exception& ex) + { + DsqlStatement::rethrowDdlException(ex, true, node); + } + + savePoint.release(); // everything is ok + } + + JRD_autocommit_ddl(tdbb, req_transaction); + + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// DsqlTransactionRequest + +DsqlTransactionRequest::DsqlTransactionRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, TransactionNode* aNode) + : DsqlRequest(pool, dbb, aStatement), + node(aNode) +{ + // Don't trace anything except savepoint statements + req_traced = (aStatement->getType() == DsqlStatement::TYPE_SAVEPOINT); +} + +// Execute a dynamic SQL statement. +void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, + IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, + bool /*singleton*/) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + node->execute(tdbb, this, traHandle); + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// DsqlSessionManagementStatement + +DsqlSessionManagementStatement::~DsqlSessionManagementStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +void DsqlSessionManagementStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, + ntrace_result_t* /*traceResult*/) +{ + node = Node::doDsqlPass(scratch, node); + + this->scratch = scratch; +} + +DsqlSessionManagementRequest* DsqlSessionManagementStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlSessionManagementRequest(getPool(), dbb, this, node); +} + +// Execute a dynamic SQL statement. +void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + IMessageMetadata* inMetadata, const UCHAR* inMsg, + IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + TraceDSQLExecute trace(req_dbb->dbb_attachment, this); + node->execute(tdbb, this, traHandle); + trace.finish(false, ITracePlugin::RESULT_SUCCESS); +} + + +// Utility functions + +// raise error if one present +static void checkD(IStatus* st) +{ + if (st->getState() & IStatus::STATE_ERRORS) + ERRD_post(Arg::StatusVector(st)); +} diff --git a/src/dsql/DsqlRequests.h b/src/dsql/DsqlRequests.h new file mode 100644 index 0000000000..860f46433a --- /dev/null +++ b/src/dsql/DsqlRequests.h @@ -0,0 +1,261 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.h + */ + +#ifndef DSQL_REQUESTS_H +#define DSQL_REQUESTS_H + +#include "firebird/Interface.h" +#include "../common/StatusArg.h" +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/NestConst.h" +#include "../common/classes/RefCounted.h" +#include "../jrd/jrd.h" + +namespace Jrd { + + +class DdlNode; +class dsql_dbb; +class DsqlStatement; +class DsqlCompilerScratch; +class DsqlCursor; +class DsqlDmlStatement; +class dsql_par; +class Request; +class jrd_tra; +class Statement; +class SessionManagementNode; +class TransactionNode; + + +class DsqlRequest : public Firebird::PermanentStorage +{ +public: + DsqlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement); + virtual ~DsqlRequest(); + +public: + jrd_tra* getTransaction() + { + return req_transaction; + } + + Firebird::RefPtr getDsqlStatement() + { + return dsqlStatement; + } + + virtual Statement* getStatement() const + { + return nullptr; + } + + virtual Request* getRequest() const + { + return nullptr; + } + + virtual DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMeta, ULONG flags) + { + Firebird::Arg::Gds(isc_no_cursor).raise(); + } + + virtual DsqlBatch* openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) + { + (Firebird::Arg::Gds(isc_sqlerr) << + Firebird::Arg::Num(-504) << + Firebird::Arg::Gds(isc_unprepared_stmt) + ).raise(); + } + + virtual void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) = 0; + + virtual void setCursor(thread_db* tdbb, const TEXT* name); + + virtual bool fetch(thread_db* tdbb, UCHAR* buffer); + + virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); + + // Get session-level timeout, milliseconds + unsigned int getTimeout(); + + // Set session-level timeout, milliseconds + void setTimeout(unsigned int timeOut); + + // Get actual timeout, milliseconds + unsigned int getActualTimeout(); + + // Evaluate actual timeout value, consider config- and session-level timeout values, + // setup and start timer + TimeoutTimer* setupTimer(thread_db* tdbb); + + USHORT parseMetadata(Firebird::IMessageMetadata* meta, const Firebird::Array& parameters_list); + + static void destroy(thread_db* tdbb, DsqlRequest* request); + +public: + dsql_dbb* req_dbb; // DSQL attachment + Firebird::RefPtr dsqlStatement; + Firebird::Array cursors{getPool()}; // Cursor update statements + + jrd_tra* req_transaction = nullptr; // JRD transaction + + Firebird::string req_cursor_name{getPool()}; // Cursor name, if any + DsqlCursor* req_cursor = nullptr; // Open cursor, if any + DsqlBatch* req_batch = nullptr; // Active batch, if any + Firebird::NonPooledMap req_user_descs{getPool()}; // SQLDA data type + + Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time + SINT64 req_fetch_elapsed = 0; // Number of clock ticks spent while fetching rows for this request since we reported it last time + SINT64 req_fetch_rowcount = 0; // Total number of rows returned by this request + bool req_traced = false; // request is traced via TraceAPI + +protected: + unsigned int req_timeout = 0; // query timeout in milliseconds, set by the user + Firebird::RefPtr req_timer; // timeout timer +}; + + +class DsqlDmlRequest final : public DsqlRequest +{ +public: + DsqlDmlRequest(thread_db* tdbb, MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aDsqlStatement); + + // Reintroduce method to fake covariant return type with RefPtr. + auto getDsqlStatement() + { + return Firebird::RefPtr((DsqlDmlStatement*) dsqlStatement.getPtr()); + } + + Statement* getStatement() const override; + + Request* getRequest() const override + { + return request; + } + + DsqlCursor* openCursor(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMeta, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMeta, ULONG flags) override; + + DsqlBatch* openBatch(thread_db* tdbb, Firebird::IMessageMetadata* inMetadata, + unsigned parLength, const UCHAR* par) override; + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + + void setCursor(thread_db* tdbb, const TEXT* name) override; + + bool fetch(thread_db* tdbb, UCHAR* buffer) override; + + void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata) override; + + void mapInOut(Jrd::thread_db* tdbb, bool toExternal, const dsql_msg* message, Firebird::IMessageMetadata* meta, + UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf = nullptr); + +private: + // True, if request could be restarted + bool needRestarts(); + + void doExecute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton); + + // [Re]start part of "request restarts" algorithm + void executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton, bool exec, bool fetch); + +public: + Firebird::Array req_msg_buffers; + +private: + Firebird::RefPtr delayedFormat; + Request* request = nullptr; + bool needDelayedFormat = false; + bool firstRowFetched = false; +}; + + +class DsqlDdlRequest final : public DsqlRequest +{ +public: + DsqlDdlRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlCompilerScratch* aInternalScratch, DdlNode* aNode); + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + DsqlCompilerScratch* internalScratch; + NestConst node; +}; + + +class DsqlTransactionRequest final : public DsqlRequest +{ +public: + DsqlTransactionRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, TransactionNode* aNode); + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + NestConst node; +}; + + +class DsqlSessionManagementRequest final : public DsqlRequest +{ +public: + DsqlSessionManagementRequest(MemoryPool& pool, dsql_dbb* dbb, DsqlStatement* aStatement, + SessionManagementNode* aNode) + : DsqlRequest(pool, dbb, aStatement), + node(aNode) + { + } + + void execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) override; + +private: + NestConst node; +}; + + +} // namespace Jrd + +#endif // DSQL_REQUESTS_H diff --git a/src/dsql/DsqlStatementCache.cpp b/src/dsql/DsqlStatementCache.cpp new file mode 100644 index 0000000000..ecec0e2c7a --- /dev/null +++ b/src/dsql/DsqlStatementCache.cpp @@ -0,0 +1,285 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2022 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../dsql/DsqlStatementCache.h" +#include "../dsql/DsqlStatements.h" +#include "../jrd/Attachment.h" +#include "../jrd/Statement.h" +#include "../jrd/lck.h" +#include "../jrd/lck_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +// Class DsqlStatementCache + +DsqlStatementCache::DsqlStatementCache(MemoryPool& o, Attachment* attachment) + : PermanentStorage(o), + map(o), + activeStatementList(o), + inactiveStatementList(o) +{ + const auto dbb = attachment->att_database; + maxCacheSize = dbb->dbb_config->getMaxStatementCacheSize(); +} + +DsqlStatementCache::~DsqlStatementCache() +{ + purge(JRD_get_thread_data()); +} + +int DsqlStatementCache::blockingAst(void* astObject) +{ +#ifdef DSQL_STATEMENT_CACHE_DEBUG + printf("DsqlStatementCache::blockingAst()\n"); +#endif + + const auto self = static_cast(astObject); + + try + { + const auto dbb = self->lock->lck_dbb; + AsyncContextHolder tdbb(dbb, FB_FUNCTION, self->lock); + + self->purge(tdbb); + } + catch (const Exception&) + {} // no-op + + return 0; +} + +RefPtr DsqlStatementCache::getStatement(thread_db* tdbb, const string& text, USHORT clientDialect, + bool isInternalRequest) +{ + RefStrPtr key; + buildStatementKey(tdbb, key, text, clientDialect, isInternalRequest); + + if (const auto entryPtr = map.get(key)) + { + const auto entry = *entryPtr; + auto dsqlStatement(entry->dsqlStatement); + + string verifyKey; + buildVerifyKey(tdbb, verifyKey, isInternalRequest); + + FB_SIZE_T verifyPos; + if (!entry->verifyCache.find(verifyKey, verifyPos)) + { + dsqlStatement->getStatement()->verifyAccess(tdbb); + entry->verifyCache.insert(verifyPos, verifyKey); + } + + if (!entry->active) + { + entry->dsqlStatement->setCacheKey(key); + // Active statement has cacheKey and will tell us when it's going to be released. + entry->dsqlStatement->release(); + + entry->active = true; + + cacheSize -= entry->size; + + activeStatementList.splice(activeStatementList.end(), inactiveStatementList, entry); + } + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + dump(); +#endif + + return dsqlStatement; + } + + return {}; +} + +void DsqlStatementCache::putStatement(thread_db* tdbb, const string& text, USHORT clientDialect, + bool isInternalRequest, RefPtr dsqlStatement) +{ + fb_assert(dsqlStatement->isDml()); + + const unsigned statementSize = dsqlStatement->getSize(); + + RefStrPtr key; + buildStatementKey(tdbb, key, text, clientDialect, isInternalRequest); + + StatementEntry newStatement(getPool()); + newStatement.key = key; + newStatement.size = statementSize; + newStatement.dsqlStatement = std::move(dsqlStatement); + newStatement.active = true; + + string verifyKey; + buildVerifyKey(tdbb, verifyKey, isInternalRequest); + newStatement.verifyCache.add(verifyKey); + + newStatement.dsqlStatement->setCacheKey(key); + // Active statement has cacheKey and will tell us when it's going to be released. + newStatement.dsqlStatement->release(); + + activeStatementList.pushBack(std::move(newStatement)); + map.put(key, --activeStatementList.end()); + + if (!lock) + { + lock = FB_NEW_RPT(getPool(), 0) Lock(tdbb, 0, LCK_dsql_statement_cache, this, blockingAst); + LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT); + } + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + dump(); +#endif +} + +void DsqlStatementCache::statementGoingInactive(Firebird::RefStrPtr& key) +{ + const auto entryPtr = map.get(key); + + if (!entryPtr) + { + fb_assert(false); + return; + } + + const auto entry = *entryPtr; + + fb_assert(entry->active); + entry->active = false; + entry->size = entry->dsqlStatement->getSize(); // update size + + inactiveStatementList.splice(inactiveStatementList.end(), activeStatementList, entry); + + cacheSize += entry->size; + + if (cacheSize > maxCacheSize) + shrink(); +} + +void DsqlStatementCache::purge(thread_db* tdbb) +{ + for (auto& entry : activeStatementList) + { + entry.dsqlStatement->addRef(); + entry.dsqlStatement->resetCacheKey(); + } + + map.clear(); + activeStatementList.clear(); + inactiveStatementList.clear(); + + cacheSize = 0; + + if (lock) + { + LCK_release(tdbb, lock); + lock.reset(); + } +} + +void DsqlStatementCache::purgeAllAttachments(thread_db* tdbb) +{ + if (lock) + LCK_convert(tdbb, lock, LCK_EX, LCK_WAIT); + else + { + lock = FB_NEW_RPT(getPool(), 0) Lock(tdbb, 0, LCK_dsql_statement_cache, this, blockingAst); + LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT); + } + + purge(tdbb); +} + +void DsqlStatementCache::buildStatementKey(thread_db* tdbb, RefStrPtr& key, const string& text, USHORT clientDialect, + bool isInternalRequest) +{ + const auto attachment = tdbb->getAttachment(); + + const SSHORT charSetId = isInternalRequest ? CS_METADATA : attachment->att_charset; + + key = FB_NEW_POOL(getPool()) RefString(getPool()); + + key->resize(1 + sizeof(charSetId) + text.length()); + char* p = key->begin(); + *p = (clientDialect << 1) | int(isInternalRequest); + memcpy(p + 1, &charSetId, sizeof(charSetId)); + memcpy(p + 1 + sizeof(charSetId), text.c_str(), text.length()); +} + +void DsqlStatementCache::buildVerifyKey(thread_db* tdbb, string& key, bool isInternalRequest) +{ + key.clear(); + + const auto attachment = tdbb->getAttachment(); + + if (isInternalRequest || !attachment->att_user) + return; + + const auto& roles = attachment->att_user->getGrantedRoles(tdbb); + + string roleStr; + + for (const auto& role : roles) + { + roleStr.printf("%d,%s,", int(role.length()), role.c_str()); + key += roleStr; + } +} + +void DsqlStatementCache::shrink() +{ +#ifdef DSQL_STATEMENT_CACHE_DEBUG + printf("DsqlStatementCache::shrink() - cacheSize: %u, maxCacheSize: %u\n\n", cacheSize, maxCacheSize); +#endif + + while (cacheSize > maxCacheSize && !inactiveStatementList.isEmpty()) + { + const auto& front = inactiveStatementList.front(); + map.remove(front.key); + cacheSize -= front.size; + inactiveStatementList.erase(inactiveStatementList.begin()); + } + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + dump(); +#endif +} + +#ifdef DSQL_STATEMENT_CACHE_DEBUG +void DsqlStatementCache::dump() +{ + printf("DsqlStatementCache::dump() - cacheSize: %u, maxCacheSize: %u\n\n", cacheSize, maxCacheSize); + + printf("\tactive:\n"); + + for (auto& entry : activeStatementList) + printf("\t\tsize: %u; text: %s\n", entry.size, entry.dsqlStatement->getSqlText()->c_str()); + + printf("\n\tinactive:\n"); + + for (auto& entry : inactiveStatementList) + printf("\t\tsize: %u; text: %s\n", entry.size, entry.dsqlStatement->getSqlText()->c_str()); + + printf("\n"); +} +#endif diff --git a/src/dsql/DsqlStatementCache.h b/src/dsql/DsqlStatementCache.h new file mode 100644 index 0000000000..da6e3f793c --- /dev/null +++ b/src/dsql/DsqlStatementCache.h @@ -0,0 +1,137 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2022 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef DSQL_STATEMENT_CACHE_H +#define DSQL_STATEMENT_CACHE_H + +///#define DSQL_STATEMENT_CACHE_DEBUG 1 + +#include "../common/classes/alloc.h" +#include "../common/classes/DoublyLinkedList.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" +#include "../common/classes/objects_array.h" +#include "../common/classes/RefCounted.h" + +namespace Jrd { + + +class Attachment; +class DsqlStatement; +class Lock; +class thread_db; + + +class DsqlStatementCache final : public Firebird::PermanentStorage +{ +private: + struct StatementEntry + { + explicit StatementEntry(MemoryPool& p) + : verifyCache(p) + { + } + + StatementEntry(MemoryPool& p, StatementEntry&& o) + : key(std::move(o.key)), + dsqlStatement(std::move(o.dsqlStatement)), + verifyCache(p, std::move(o.verifyCache)), + size(o.size), + active(o.active) + { + } + + StatementEntry(const StatementEntry&) = delete; + StatementEntry& operator=(const StatementEntry&) = delete; + + Firebird::RefStrPtr key; + Firebird::RefPtr dsqlStatement; + Firebird::SortedObjectsArray verifyCache; + unsigned size = 0; + bool active = true; + }; + + class RefStrPtrComparator + { + public: + static bool greaterThan(const Firebird::RefStrPtr& i1, const Firebird::RefStrPtr& i2) + { + return *i1 > *i2; + } + }; + +public: + explicit DsqlStatementCache(MemoryPool& o, Attachment* attachment); + ~DsqlStatementCache(); + + DsqlStatementCache(const DsqlStatementCache&) = delete; + DsqlStatementCache& operator=(const DsqlStatementCache&) = delete; + +private: + static int blockingAst(void* astObject); + +public: + bool isActive() const + { + return maxCacheSize > 0; + } + + Firebird::RefPtr getStatement(thread_db* tdbb, const Firebird::string& text, + USHORT clientDialect, bool isInternalRequest); + + void putStatement(thread_db* tdbb, const Firebird::string& text, USHORT clientDialect, bool isInternalRequest, + Firebird::RefPtr dsqlStatement); + + void statementGoingInactive(Firebird::RefStrPtr& key); + + void purge(thread_db* tdbb); + void purgeAllAttachments(thread_db* tdbb); + +private: + void buildStatementKey(thread_db* tdbb, Firebird::RefStrPtr& key, const Firebird::string& text, + USHORT clientDialect, bool isInternalRequest); + + void buildVerifyKey(thread_db* tdbb, Firebird::string& key, bool isInternalRequest); + + void shrink(); + +#ifdef DSQL_STATEMENT_CACHE_DEBUG + void dump(); +#endif + +private: + Firebird::NonPooledMap< + Firebird::RefStrPtr, + Firebird::DoublyLinkedList::Iterator, + RefStrPtrComparator + > map; + Firebird::DoublyLinkedList activeStatementList; + Firebird::DoublyLinkedList inactiveStatementList; + Firebird::AutoPtr lock; + unsigned maxCacheSize = 0; + unsigned cacheSize = 0; +}; + + +} // namespace Jrd + +#endif // DSQL_STATEMENT_CACHE_H diff --git a/src/dsql/DsqlStatements.cpp b/src/dsql/DsqlStatements.cpp new file mode 100644 index 0000000000..9d18ac74b7 --- /dev/null +++ b/src/dsql/DsqlStatements.cpp @@ -0,0 +1,314 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.cpp + */ + +#include "firebird.h" +#include "../dsql/DsqlStatements.h" +#include "../dsql/dsql.h" +#include "../dsql/Nodes.h" +#include "../dsql/DsqlCompilerScratch.h" +#include "../dsql/DsqlStatementCache.h" +#include "../jrd/Statement.h" +#include "../dsql/errd_proto.h" +#include "../dsql/gen_proto.h" +#include "../jrd/cmp_proto.h" + +using namespace Firebird; +using namespace Jrd; + + +// Class DsqlStatement + +// Rethrow an exception with isc_no_meta_update and prefix codes. +void DsqlStatement::rethrowDdlException(status_exception& ex, bool metadataUpdate, DdlNode* node) +{ + Arg::StatusVector newVector; + + if (metadataUpdate) + newVector << Arg::Gds(isc_no_meta_update); + + node->putErrorPrefix(newVector); + + const ISC_STATUS* status = ex.value(); + + if (status[1] == isc_no_meta_update) + status += 2; + + newVector.append(Arg::StatusVector(status)); + + status_exception::raise(newVector); +} + +int DsqlStatement::release() +{ + fb_assert(refCounter.value() > 0); + int refCnt = --refCounter; + + if (!refCnt) + { + if (cacheKey) + { + refCnt = ++refCounter; + auto key = cacheKey; + cacheKey = nullptr; + dsqlAttachment->dbb_statement_cache->statementGoingInactive(key); + } + else + { + doRelease(); + dsqlAttachment->deletePool(&getPool()); + } + } + + return refCnt; +} + +void DsqlStatement::doRelease() +{ + setSqlText(nullptr); + setOrgText(nullptr, 0); +} + +void DsqlStatement::setOrgText(const char* ptr, ULONG len) +{ + if (!ptr || !len) + { + orgText = NULL; + return; + } + + const string text(ptr, len); + + if (text == *sqlText) + orgText = sqlText; + else + orgText = FB_NEW_POOL(getPool()) RefString(getPool(), text); +} + + +// DsqlDmlStatement + +void DsqlDmlStatement::doRelease() +{ + if (auto parent = getParentRequest()) + { + FB_SIZE_T pos; + if (parent->cursors.find(this, pos)) + parent->cursors.remove(pos); + } + + if (statement) + { + thread_db* tdbb = JRD_get_thread_data(); + ThreadStatusGuard status_vector(tdbb); + + try + { + statement->release(tdbb); + } + catch (Exception&) + {} // no-op + } + + DsqlStatement::doRelease(); +} + +unsigned DsqlDmlStatement::getSize() const +{ + return DsqlStatement::getSize() + statement->getSize(); +} + +void DsqlDmlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) +{ + { // scope + ContextPoolHolder scratchContext(tdbb, &scratch->getPool()); + node = Node::doDsqlPass(scratch, node); + } + + if (scratch->clientDialect > SQL_DIALECT_V5) + scratch->getDsqlStatement()->setBlrVersion(5); + else + scratch->getDsqlStatement()->setBlrVersion(4); + + GEN_statement(scratch, node); + + unsigned messageNumber = 0; + + for (auto message : ports) + message->msg_buffer_number = messageNumber++; + + // have the access method compile the statement + +#ifdef DSQL_DEBUG + if (DSQL_debug & 64) + { + dsql_trace("Resulting BLR code for DSQL:"); + gds__trace_raw("Statement:\n"); + gds__trace_raw(getSqlText()->c_str(), getSqlText()->length()); + gds__trace_raw("\nBLR:\n"); + fb_print_blr(scratch->getBlrData().begin(), + (ULONG) scratch->getBlrData().getCount(), + gds__trace_printer, 0, 0); + } +#endif + + FbLocalStatus localStatus; + + // check for warnings + if (tdbb->tdbb_status_vector->getState() & IStatus::STATE_WARNINGS) + { + // save a status vector + fb_utils::copyStatus(&localStatus, tdbb->tdbb_status_vector); + fb_utils::init_status(tdbb->tdbb_status_vector); + } + + ISC_STATUS status = FB_SUCCESS; + + try + { + const auto attachment = scratch->getAttachment()->dbb_attachment; + const auto& blr = scratch->getBlrData(); + const auto& debugData = scratch->getDebugData(); + + statement = CMP_compile(tdbb, blr.begin(), blr.getCount(), + (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST), + debugData.getCount(), debugData.begin()); + + if (getSqlText()) + statement->sqlText = getSqlText(); + + fb_assert(statement->blr.isEmpty()); + + if (attachment->getDebugOptions().getDsqlKeepBlr()) + statement->blr.insert(0, blr.begin(), blr.getCount()); + } + catch (const Exception&) + { + status = tdbb->tdbb_status_vector->getErrors()[1]; + *traceResult = status == isc_no_priv ? + ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; + } + + // restore warnings (if there are any) + if (localStatus->getState() & IStatus::STATE_WARNINGS) + { + Arg::StatusVector cur(tdbb->tdbb_status_vector->getWarnings()); + Arg::StatusVector saved(localStatus->getWarnings()); + saved << cur; + + tdbb->tdbb_status_vector->setWarnings2(saved.length(), saved.value()); + } + + // free blr memory + scratch->getBlrData().free(); + + if (status) + status_exception::raise(tdbb->tdbb_status_vector); + + node = NULL; +} + +DsqlDmlRequest* DsqlDmlStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlDmlRequest(tdbb, getPool(), dbb, this); +} + + +// DsqlDdlStatement + +DsqlDdlStatement::~DsqlDdlStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +bool DsqlDdlStatement::mustBeReplicated() const +{ + return node->mustBeReplicated(); +} + +void DsqlDdlStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) +{ + Database* const dbb = tdbb->getDatabase(); + + scratch->flags |= DsqlCompilerScratch::FLAG_DDL; + + try + { + node = Node::doDsqlPass(scratch, node); + } + catch (status_exception& ex) + { + rethrowDdlException(ex, false, node); + } + + if (dbb->readOnly()) + ERRD_post(Arg::Gds(isc_read_only_database)); + + // In read-only replica, only replicator is allowed to execute DDL. + // As an exception, not replicated DDL statements are also allowed. + if (dbb->isReplica(REPLICA_READ_ONLY) && + !(tdbb->tdbb_flags & TDBB_replicator) && + node->mustBeReplicated()) + { + ERRD_post(Arg::Gds(isc_read_only_trans)); + } + + const auto dbDialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; + + if ((scratch->flags & DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT) && + dbDialect != scratch->clientDialect) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) << + Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(dbDialect)); + } + + if (scratch->clientDialect > SQL_DIALECT_V5) + scratch->getDsqlStatement()->setBlrVersion(5); + else + scratch->getDsqlStatement()->setBlrVersion(4); + + this->scratch = scratch; +} + +DsqlDdlRequest* DsqlDdlStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlDdlRequest(getPool(), dbb, scratch, node); +} + + +// DsqlTransactionStatement + +DsqlTransactionStatement::~DsqlTransactionStatement() +{ + dsqlAttachment->deletePool(&scratch->getPool()); +} + +void DsqlTransactionStatement::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, + ntrace_result_t* /*traceResult*/) +{ + node = Node::doDsqlPass(scratch, node); + + this->scratch = scratch; +} + +DsqlTransactionRequest* DsqlTransactionStatement::createRequest(thread_db* tdbb, dsql_dbb* dbb) +{ + return FB_NEW_POOL(getPool()) DsqlTransactionRequest(getPool(), dbb, this, node); +} diff --git a/src/dsql/DsqlStatements.h b/src/dsql/DsqlStatements.h new file mode 100644 index 0000000000..42d18d01a1 --- /dev/null +++ b/src/dsql/DsqlStatements.h @@ -0,0 +1,316 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * 2022.02.07 Adriano dos Santos Fernandes: Refactored from dsql.h + */ + +#ifndef DSQL_STATEMENTS_H +#define DSQL_STATEMENTS_H + +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/NestConst.h" +#include "../common/classes/RefCounted.h" +#include "../jrd/jrd.h" +#include "../jrd/ntrace.h" +#include "../dsql/DsqlRequests.h" + +namespace Jrd { + + +class DdlNode; +class dsql_dbb; +class dsql_msg; +class dsql_par; +class DsqlRequest; +class DsqlCompilerScratch; +class Statement; +class SessionManagementNode; +class TransactionNode; + + +// Compiled statement - shared by multiple requests. +class DsqlStatement : public Firebird::PermanentStorage +{ +public: + enum Type // statement type + { + TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, + TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, + TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, + TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT, + TYPE_RETURNING_CURSOR + }; + + // Statement flags. + static const unsigned FLAG_ORPHAN = 0x01; + static const unsigned FLAG_NO_BATCH = 0x02; + //static const unsigned FLAG_BLR_VERSION4 = 0x04; + //static const unsigned FLAG_BLR_VERSION5 = 0x08; + static const unsigned FLAG_SELECTABLE = 0x10; + + static void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate, DdlNode* node); + +public: + DsqlStatement(MemoryPool& pool, dsql_dbb* aDsqlAttachment) + : PermanentStorage(pool), + dsqlAttachment(aDsqlAttachment), + type(TYPE_SELECT), + flags(0), + blrVersion(5), + ports(pool) + { + pool.setStatsGroup(memoryStats); + } + +protected: + virtual ~DsqlStatement() = default; + +public: + int addRef() + { + return ++refCounter; + } + + int release(); + + bool isCursorBased() const + { + switch (type) + { + case TYPE_SELECT: + case TYPE_SELECT_BLOCK: + case TYPE_SELECT_UPD: + case TYPE_RETURNING_CURSOR: + return true; + } + + return false; + } + + Type getType() const { return type; } + void setType(Type value) { type = value; } + + ULONG getFlags() const { return flags; } + void setFlags(ULONG value) { flags = value; } + void addFlags(ULONG value) { flags |= value; } + + unsigned getBlrVersion() const { return blrVersion; } + void setBlrVersion(unsigned value) { blrVersion = value; } + + Firebird::RefStrPtr& getSqlText() { return sqlText; } + const Firebird::RefStrPtr& getSqlText() const { return sqlText; } + void setSqlText(Firebird::RefString* value) { sqlText = value; } + + void setOrgText(const char* ptr, ULONG len); + const Firebird::string& getOrgText() const { return *orgText; } + + Firebird::Array& getPorts() { return ports; } + + dsql_msg* getSendMsg() { return sendMsg; } + const dsql_msg* getSendMsg() const { return sendMsg; } + void setSendMsg(dsql_msg* value) { sendMsg = value; } + + dsql_msg* getReceiveMsg() { return receiveMsg; } + const dsql_msg* getReceiveMsg() const { return receiveMsg; } + void setReceiveMsg(dsql_msg* value) { receiveMsg = value; } + + dsql_par* getEof() { return eof; } + const dsql_par* getEof() const { return eof; } + void setEof(dsql_par* value) { eof = value; } + + void setCacheKey(Firebird::RefStrPtr& value) { cacheKey = value; } + void resetCacheKey() { cacheKey = nullptr; } + +public: + virtual bool isDml() const + { + return false; + } + + virtual Statement* getStatement() const + { + return nullptr; + } + + virtual bool mustBeReplicated() const + { + return false; + } + + virtual bool shouldPreserveScratch() const + { + return true; + } + + virtual unsigned getSize() const + { + return (unsigned) memoryStats.getCurrentUsage(); + } + + virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) = 0; + virtual DsqlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) = 0; + +protected: + virtual void doRelease(); + +protected: + dsql_dbb* dsqlAttachment; + Firebird::MemoryStats memoryStats; + Type type; // Type of statement + ULONG flags; // generic flag + unsigned blrVersion; + Firebird::RefStrPtr sqlText; + Firebird::RefStrPtr orgText; + Firebird::RefStrPtr cacheKey; + Firebird::Array ports; // Port messages + dsql_msg* sendMsg = nullptr; // Message to be sent to start request + dsql_msg* receiveMsg = nullptr; // Per record message to be received + dsql_par* eof = nullptr; // End of file parameter + +private: + Firebird::AtomicCounter refCounter; +}; + + +class DsqlDmlStatement final : public DsqlStatement +{ +public: + DsqlDmlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, StmtNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + +public: + bool isDml() const override + { + return true; + } + + Statement* getStatement() const override + { + return statement; + } + + unsigned getSize() const override; + + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlDmlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + + dsql_par* getDbKey() { return dbKey; } + const dsql_par* getDbKey() const { return dbKey; } + void setDbKey(dsql_par* value) { dbKey = value; } + + dsql_par* getRecVersion() { return recVersion; } + const dsql_par* getRecVersion() const { return recVersion; } + void setRecVersion(dsql_par* value) { recVersion = value; } + + dsql_par* getParentRecVersion() { return parentRecVersion; } + const dsql_par* getParentRecVersion() const { return parentRecVersion; } + void setParentRecVersion(dsql_par* value) { parentRecVersion = value; } + + dsql_par* getParentDbKey() { return parentDbKey; } + const dsql_par* getParentDbKey() const { return parentDbKey; } + void setParentDbKey(dsql_par* value) { parentDbKey = value; } + + DsqlDmlRequest* getParentRequest() const { return parentRequest; } + void setParentRequest(DsqlDmlRequest* value) { parentRequest = value; } + +protected: + void doRelease() override; + +private: + NestConst node; + Statement* statement = nullptr; + dsql_par* dbKey = nullptr; // Database key for current of + dsql_par* recVersion = nullptr; // Record Version for current of + dsql_par* parentRecVersion = nullptr; // parent record version + dsql_par* parentDbKey = nullptr; // Parent database key for current of + DsqlDmlRequest* parentRequest = nullptr; // Source request, if cursor update +}; + + +class DsqlDdlStatement final : public DsqlStatement +{ +public: + DsqlDdlStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, DdlNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlDdlStatement(); + +public: + bool mustBeReplicated() const override; + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlDdlRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +class DsqlTransactionStatement final : public DsqlStatement +{ +public: + DsqlTransactionStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, TransactionNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlTransactionStatement(); + +public: + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlTransactionRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +class DsqlSessionManagementStatement final : public DsqlStatement +{ +public: + DsqlSessionManagementStatement(MemoryPool& p, dsql_dbb* aDsqlAttachment, SessionManagementNode* aNode) + : DsqlStatement(p, aDsqlAttachment), + node(aNode) + { + } + + ~DsqlSessionManagementStatement(); + +public: + void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* traceResult) override; + DsqlSessionManagementRequest* createRequest(thread_db* tdbb, dsql_dbb* dbb) override; + +private: + NestConst node; + DsqlCompilerScratch* scratch = nullptr; +}; + + +} // namespace Jrd + +#endif // DSQL_STATEMENTS_H diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 21b64eec0a..a990e1488d 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -35,7 +35,7 @@ #include "../jrd/SysFunction.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/recsrc/Cursor.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/recsrc/Cursor.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" @@ -260,7 +260,7 @@ bool ExprNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other bool ExprNode::sameAs(const ExprNode* other, bool ignoreStreams) const { - if (other->getType() != getType()) + if (!other || other->getType() != getType()) return false; NodeRefsHolder thisHolder; @@ -344,15 +344,16 @@ bool ExprNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void ExprNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) +void ExprNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - NodeRefsHolder holder(optRet->getPool()); + NodeRefsHolder holder(csb->csb_pool); getChildren(holder, false); for (auto i : holder.refs) { if (*i) - (*i)->findDependentFromStreams(optRet, streamList); + (*i)->findDependentFromStreams(csb, currentStream, streamList); } } @@ -1837,7 +1838,7 @@ ValueExprNode* ArithmeticNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ArithmeticNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); @@ -3213,7 +3214,7 @@ ValueExprNode* AtNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* AtNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* AtNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -3331,7 +3332,7 @@ ValueExprNode* BoolAsValueNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* BoolAsValueNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* BoolAsValueNode::execute(thread_db* tdbb, Request* request) const { UCHAR booleanVal = (UCHAR) boolean->execute(tdbb, request); @@ -3551,7 +3552,7 @@ ValueExprNode* CastNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Cast from one datatype to another. -dsc* CastNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CastNode::execute(thread_db* tdbb, Request* request) const { dsc* value = EVL_expr(tdbb, request, source); @@ -3731,7 +3732,7 @@ ValueExprNode* CoalesceNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* CoalesceNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CoalesceNode::execute(thread_db* tdbb, Request* request) const { for (auto& item : args->items) { @@ -3930,7 +3931,7 @@ ValueExprNode* ConcatenateNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ConcatenateNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ConcatenateNode::execute(thread_db* tdbb, Request* request) const { const dsc* value1 = EVL_expr(tdbb, request, arg1); const ULONG flags = request->req_flags; @@ -4163,25 +4164,15 @@ ValueExprNode* CurrentDateNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* CurrentDateNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentDateNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); + impure->vlu_misc.vlu_sql_date = request->getLocalTimeStamp().timestamp_date; - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = request->req_gmt_timestamp.value(); - timeStampTz.time_zone = TimeZoneUtil::GMT_ZONE; - - impure->vlu_misc.vlu_sql_date = TimeZoneUtil::timeStampTzToTimeStamp( - timeStampTz, request->req_attachment->att_current_timezone).timestamp_date; - - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_dtype = dtype_sql_date; - impure->vlu_desc.dsc_length = type_lengths[dtype_sql_date]; - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_date; + impure->vlu_desc.makeDate(&impure->vlu_misc.vlu_sql_date); return &impure->vlu_desc; } @@ -4276,27 +4267,17 @@ ValueExprNode* CurrentTimeNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/) return this; } -dsc* CurrentTimeNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentTimeNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - - ISC_TIMESTAMP_TZ currentTimeStamp; - currentTimeStamp.utc_timestamp = request->req_gmt_timestamp.value(); - currentTimeStamp.time_zone = tdbb->getAttachment()->att_current_timezone; - - impure->vlu_desc.dsc_dtype = dtype_sql_time_tz; - impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time_tz]; - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_time_tz; - - impure->vlu_misc.vlu_sql_time_tz.time_zone = tdbb->getAttachment()->att_current_timezone; - impure->vlu_misc.vlu_sql_time_tz.utc_time = TimeZoneUtil::timeStampTzToTimeTz(currentTimeStamp).utc_time; - + impure->vlu_misc.vlu_sql_time_tz = request->getTimeTz(); TimeStamp::round_time(impure->vlu_misc.vlu_sql_time_tz.utc_time, precision); + impure->vlu_desc.makeTimeTz(&impure->vlu_misc.vlu_sql_time_tz); + return &impure->vlu_desc; } @@ -4391,25 +4372,16 @@ ValueExprNode* CurrentTimeStampNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch return this; } -dsc* CurrentTimeStampNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentTimeStampNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - ISC_TIMESTAMP encTimes = request->req_gmt_timestamp.value(); + impure->vlu_misc.vlu_timestamp_tz = request->getTimeStampTz(); + TimeStamp::round_time(impure->vlu_misc.vlu_timestamp_tz.utc_timestamp.timestamp_time, precision); - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_timestamp_tz; - - TimeStamp::round_time(encTimes.timestamp_time, precision); - - impure->vlu_desc.dsc_dtype = dtype_timestamp_tz; - impure->vlu_desc.dsc_length = type_lengths[dtype_timestamp_tz]; - - impure->vlu_misc.vlu_timestamp_tz.utc_timestamp = encTimes; - impure->vlu_misc.vlu_timestamp_tz.time_zone = tdbb->getAttachment()->att_current_timezone; + impure->vlu_desc.makeTimestampTz(&impure->vlu_misc.vlu_timestamp_tz); return &impure->vlu_desc; } @@ -4478,7 +4450,7 @@ ValueExprNode* CurrentRoleNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // CVC: Current role will get a validated role; IE one that exists. -dsc* CurrentRoleNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentRoleNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4564,7 +4536,7 @@ ValueExprNode* CurrentUserNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* CurrentUserNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* CurrentUserNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -4813,7 +4785,7 @@ ValueExprNode* DecodeNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* DecodeNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* DecodeNode::execute(thread_db* tdbb, Request* request) const { dsc* testDesc = EVL_expr(tdbb, request, test); @@ -5064,15 +5036,15 @@ bool DerivedExprNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void DerivedExprNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void DerivedExprNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - arg->findDependentFromStreams(optRet, streamList); + arg->findDependentFromStreams(csb, currentStream, streamList); for (const auto derivedStream : internalStreamList) { - if (derivedStream != optRet->stream && - (optRet->csb->csb_rpt[derivedStream].csb_flags & csb_active)) + if (derivedStream != currentStream && + (csb->csb_rpt[derivedStream].csb_flags & csb_active)) { if (!streamList->exist(derivedStream)) streamList->add(derivedStream); @@ -5151,7 +5123,7 @@ ValueExprNode* DerivedExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* DerivedExprNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* DerivedExprNode::execute(thread_db* tdbb, Request* request) const { if (cursorNumber.specified) request->req_cursors[cursorNumber.value]->checkState(request); @@ -5236,7 +5208,7 @@ ValueExprNode* DomainValidationNode::pass2(thread_db* tdbb, CompilerScratch* csb return this; } -dsc* DomainValidationNode::execute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* DomainValidationNode::execute(thread_db* /*tdbb*/, Request* request) const { if (request->req_domain_validation == NULL || (request->req_domain_validation->dsc_flags & DSC_null)) @@ -5447,7 +5419,7 @@ ValueExprNode* ExtractNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Handles EXTRACT(part FROM date/time/timestamp) -dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ExtractNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -6508,13 +6480,14 @@ bool FieldNode::computable(CompilerScratch* csb, StreamType stream, return csb->csb_rpt[fieldStream].csb_flags & csb_active; } -void FieldNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) +void FieldNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - // dimitr: OLD/NEW contexts shouldn't create any stream dependencies. + // dimitr: OLD/NEW contexts shouldn't create any stream dependencies - if (fieldStream != optRet->stream && - (optRet->csb->csb_rpt[fieldStream].csb_flags & csb_active) && - !(optRet->csb->csb_rpt[fieldStream].csb_flags & csb_trigger)) + if (fieldStream != currentStream && + (csb->csb_rpt[fieldStream].csb_flags & csb_active) && + !(csb->csb_rpt[fieldStream].csb_flags & csb_trigger)) { if (!streamList->exist(fieldStream)) streamList->add(fieldStream); @@ -6651,14 +6624,14 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb) tail->csb_view->rel_id : (csb->csb_view ? csb->csb_view->rel_id : 0); CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId, - privilege, SCL_object_table, relation->rel_name); + privilege, obj_relations, relation->rel_name); // Field-level privilege access is posted for every operation except DELETE if (privilege != SCL_delete) { CMP_post_access(tdbb, csb, field->fld_security_name, ssRelationId, - privilege, SCL_object_column, field->fld_name, relation->rel_name); + privilege, obj_column, field->fld_name, relation->rel_name); } } @@ -6823,7 +6796,7 @@ ValueExprNode* FieldNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* FieldNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* FieldNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); @@ -7066,7 +7039,7 @@ ValueExprNode* GenIdNode::pass1(thread_db* tdbb, CompilerScratch* csb) if (!identity) { CMP_post_access(tdbb, csb, generator.secName, 0, - SCL_usage, SCL_object_generator, generator.name); + SCL_usage, obj_generators, generator.name); } return this; @@ -7083,7 +7056,7 @@ ValueExprNode* GenIdNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* GenIdNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* GenIdNode::execute(thread_db* tdbb, Request* request) const { request->req_flags &= ~req_null; @@ -7281,7 +7254,7 @@ ValueExprNode* InternalInfoNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Return a given element of the internal engine data. -dsc* InternalInfoNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* InternalInfoNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -7981,7 +7954,7 @@ ValueExprNode* LiteralNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* LiteralNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const +dsc* LiteralNode::execute(thread_db* /*tdbb*/, Request* /*request*/) const { return const_cast(&litDesc); } @@ -8158,27 +8131,16 @@ ValueExprNode* LocalTimeNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/) return this; } -dsc* LocalTimeNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* LocalTimeNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = request->req_gmt_timestamp.value(); - timeStampTz.time_zone = TimeZoneUtil::GMT_ZONE; - - impure->vlu_misc.vlu_sql_time = TimeZoneUtil::timeStampTzToTimeStamp( - timeStampTz, request->req_attachment->att_current_timezone).timestamp_time; - + impure->vlu_misc.vlu_sql_time = request->getLocalTimeStamp().timestamp_time; TimeStamp::round_time(impure->vlu_misc.vlu_sql_time, precision); - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_dtype = dtype_sql_time; - impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time]; - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_time; + impure->vlu_desc.makeTime(&impure->vlu_misc.vlu_sql_time); return &impure->vlu_desc; } @@ -8261,21 +8223,16 @@ ValueExprNode* LocalTimeStampNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/ return this; } -dsc* LocalTimeStampNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* LocalTimeStampNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; // Use the request timestamp. - fb_assert(!request->req_gmt_timestamp.isEmpty()); - - impure->vlu_misc.vlu_timestamp = request->getLocalTimeStamp().value(); + impure->vlu_misc.vlu_timestamp = request->getLocalTimeStamp(); TimeStamp::round_time(impure->vlu_misc.vlu_timestamp.timestamp_time, precision); - memset(&impure->vlu_desc, 0, sizeof(impure->vlu_desc)); - impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_timestamp; - impure->vlu_desc.dsc_dtype = dtype_timestamp; - impure->vlu_desc.dsc_length = type_lengths[dtype_timestamp]; + impure->vlu_desc.makeTimestamp(&impure->vlu_misc.vlu_timestamp); return &impure->vlu_desc; } @@ -8877,7 +8834,7 @@ ValueExprNode* NegateNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* NegateNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* NegateNode::execute(thread_db* tdbb, Request* request) const { request->req_flags &= ~req_null; @@ -9005,7 +8962,7 @@ ValueExprNode* NullNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) const return &INSTANCE; } -dsc* NullNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const +dsc* NullNode::execute(thread_db* /*tdbb*/, Request* /*request*/) const { return NULL; } @@ -9406,7 +9363,7 @@ ValueExprNode* OverNode::copy(thread_db* /*tdbb*/, NodeCopier& /*copier*/) const return NULL; } -dsc* OverNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const +dsc* OverNode::execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -9453,7 +9410,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) //-------------------- -static RegisterNode regParameterNode({blr_parameter, blr_parameter2, blr_parameter3}); +static RegisterNode regParameterNode({blr_parameter, blr_parameter2}); ParameterNode::ParameterNode(MemoryPool& pool) : TypedNode(pool) @@ -9462,27 +9419,28 @@ ParameterNode::ParameterNode(MemoryPool& pool) DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { - MessageNode* message = NULL; - USHORT n = csb->csb_blr_reader.getByte(); + MessageNode* message = nullptr; + const USHORT messageNum = csb->csb_blr_reader.getByte(); - if (n >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[n].csb_message)) + if (messageNum >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[messageNum].csb_message)) PAR_error(csb, Arg::Gds(isc_badmsgnum)); - ParameterNode* node = FB_NEW_POOL(pool) ParameterNode(pool); - + const auto node = FB_NEW_POOL(pool) ParameterNode(pool); node->message = message; node->argNumber = csb->csb_blr_reader.getWord(); + node->outerDecl = csb->outerMessagesMap.exist(messageNum); - const Format* format = message->format; + const auto format = message->format; if (node->argNumber >= format->fmt_count) PAR_error(csb, Arg::Gds(isc_badparnum)); if (blrOp != blr_parameter) { - ParameterNode* flagNode = FB_NEW_POOL(pool) ParameterNode(pool); + const auto flagNode = FB_NEW_POOL(pool) ParameterNode(pool); flagNode->message = message; flagNode->argNumber = csb->csb_blr_reader.getWord(); + flagNode->outerDecl = node->outerDecl; if (flagNode->argNumber >= format->fmt_count) PAR_error(csb, Arg::Gds(isc_badparnum)); @@ -9490,16 +9448,12 @@ DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScr node->argFlag = flagNode; } - if (blrOp == blr_parameter3) + if (node->outerDecl) { - ParameterNode* indicatorNode = FB_NEW_POOL(pool) ParameterNode(pool); - indicatorNode->message = message; - indicatorNode->argNumber = csb->csb_blr_reader.getWord(); + fb_assert(csb->mainCsb); - if (indicatorNode->argNumber >= format->fmt_count) - PAR_error(csb, Arg::Gds(isc_badparnum)); - - node->argIndicator = indicatorNode; + if (csb->mainCsb) + message->itemsUsedInSubroutines.add(node->argNumber); } return node; @@ -9514,8 +9468,8 @@ string ParameterNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, message); NODE_PRINT(printer, argNumber); NODE_PRINT(printer, argFlag); - NODE_PRINT(printer, argIndicator); NODE_PRINT(printer, argInfo); + NODE_PRINT(printer, outerDecl); return "ParameterNode"; } @@ -9530,11 +9484,12 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) auto msg = dsqlMessage ? dsqlMessage : dsqlParameter ? dsqlParameter->par_message : - dsqlScratch->getStatement()->getSendMsg(); + dsqlScratch->getDsqlStatement()->getSendMsg(); auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool()); node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr); node->dsqlParameterIndex = dsqlParameterIndex; + node->outerDecl = outerDecl; return node; } @@ -9601,7 +9556,7 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, if (!dsqlParameter) { - dsqlParameter = MAKE_parameter(dsqlScratch->getStatement()->getSendMsg(), true, true, + dsqlParameter = MAKE_parameter(dsqlScratch->getDsqlStatement()->getSendMsg(), true, true, dsqlParameterIndex, NULL); dsqlParameterIndex = dsqlParameter->par_index; } @@ -9666,6 +9621,7 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, void ParameterNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + fb_assert(!outerDecl); GEN_parameter(dsqlScratch, dsqlParameter); } @@ -9686,7 +9642,20 @@ bool ParameterNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* { const ParameterNode* o = nodeAs(other); - return o && dsqlParameter->par_index == o->dsqlParameter->par_index; + return o && outerDecl == o->outerDecl && dsqlParameter->par_index == o->dsqlParameter->par_index; +} + +Request* ParameterNode::getParamRequest(Request* request) const +{ + auto paramRequest = request; + + if (outerDecl) + { + while (paramRequest->getStatement()->parentStatement) + paramRequest = paramRequest->req_caller; + } + + return paramRequest; } void ParameterNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) @@ -9721,14 +9690,16 @@ ValueExprNode* ParameterNode::copy(thread_db* tdbb, NodeCopier& copier) const node->message = message; node->argFlag = copier.copy(tdbb, argFlag); - node->argIndicator = copier.copy(tdbb, argIndicator); + node->outerDecl = outerDecl; return node; } ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - argInfo = CMP_pass2_validation(tdbb, csb, + const auto paramCsb = outerDecl ? csb->mainCsb : csb; + + argInfo = CMP_pass2_validation(tdbb, paramCsb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber)); ValueExprNode::pass2(tdbb, csb); @@ -9736,21 +9707,38 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc desc; getDesc(tdbb, csb, &desc); - if (nodFlags & FLAG_VALUE) - impureOffset = csb->allocImpure(); + if (message->itemsUsedInSubroutines.exist(argNumber)) + impureOffset = csb->allocImpure(); else impureOffset = csb->allocImpure(); return this; } -dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const { - impure_value* const impure = request->getImpure(impureOffset); - request->req_flags &= ~req_null; + dsc* retDesc; + impure_value* impureForOuter; + if (message->itemsUsedInSubroutines.exist(argNumber)) + { + impureForOuter = request->getImpure(impureOffset); + retDesc = &impureForOuter->vlu_desc; + } + else + { + impureForOuter = nullptr; + retDesc = request->getImpure(impureOffset); + } + + const auto paramRequest = getParamRequest(request); + + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); const dsc* desc; + request->req_flags &= ~req_null; + if (argFlag) { desc = EVL_expr(tdbb, request, argFlag); @@ -9760,32 +9748,37 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const desc = &message->format->fmt_desc[argNumber]; - impure->vlu_desc.dsc_address = request->getImpure( + retDesc->dsc_address = paramRequest->getImpure( message->impureOffset + (IPTR) desc->dsc_address); - impure->vlu_desc.dsc_dtype = desc->dsc_dtype; - impure->vlu_desc.dsc_length = desc->dsc_length; - impure->vlu_desc.dsc_scale = desc->dsc_scale; - impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; + retDesc->dsc_dtype = desc->dsc_dtype; + retDesc->dsc_length = desc->dsc_length; + retDesc->dsc_scale = desc->dsc_scale; + retDesc->dsc_sub_type = desc->dsc_sub_type; - if (impure->vlu_desc.dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); + if (!(request->req_flags & req_null)) + { + if (impureForOuter) + EVL_make_value(tdbb, retDesc, impureForOuter); - USHORT* impure_flags = request->getImpure( + if (retDesc->dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, retDesc); + } + + auto impureFlags = paramRequest->getImpure( message->impureFlags + (sizeof(USHORT) * argNumber)); - if (!(*impure_flags & VLU_checked)) + if (!(*impureFlags & VLU_checked)) { if (!(request->req_flags & req_null)) { USHORT maxLen = desc->dsc_length; // not adjusted length - desc = &impure->vlu_desc; - if (DTYPE_IS_TEXT(desc->dsc_dtype)) + if (DTYPE_IS_TEXT(retDesc->dsc_dtype)) { - const UCHAR* p = desc->dsc_address; + const UCHAR* p = retDesc->dsc_address; USHORT len; - switch (desc->dsc_dtype) + switch (retDesc->dsc_dtype) { case dtype_cstring: len = strnlen((const char*) p, maxLen); @@ -9793,7 +9786,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const break; case dtype_text: - len = desc->dsc_length; + len = retDesc->dsc_length; break; case dtype_varying: @@ -9803,24 +9796,24 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const break; } - CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc)); + auto charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(retDesc)); EngineCallbacks::instance->validateData(charSet, len, p); - EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(desc), len, p, maxLen); + EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(retDesc), len, p, maxLen); } - else if (desc->isBlob()) + else if (retDesc->isBlob()) { - const bid* const blobId = reinterpret_cast(desc->dsc_address); + const bid* const blobId = reinterpret_cast(retDesc->dsc_address); if (!blobId->isEmpty()) { if (!request->hasInternalStatement()) tdbb->getTransaction()->checkBlob(tdbb, blobId, NULL, false); - if (desc->getCharSet() != CS_NONE && desc->getCharSet() != CS_BINARY) + if (retDesc->getCharSet() != CS_NONE && retDesc->getCharSet() != CS_BINARY) { AutoBlb blob(tdbb, blb::open(tdbb, tdbb->getTransaction(), blobId)); - blob.getBlb()->BLB_check_well_formed(tdbb, desc); + blob.getBlb()->BLB_check_well_formed(tdbb, retDesc); } } } @@ -9829,13 +9822,13 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const if (argInfo) { EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber), - argInfo, &impure->vlu_desc, request->req_flags & req_null); + argInfo, retDesc, request->req_flags & req_null); } - *impure_flags |= VLU_checked; + *impureFlags |= VLU_checked; } - return (request->req_flags & req_null) ? NULL : &impure->vlu_desc; + return (request->req_flags & req_null) ? nullptr : retDesc; } @@ -10075,9 +10068,10 @@ bool RecordKeyNode::computable(CompilerScratch* csb, StreamType stream, return csb->csb_rpt[recStream].csb_flags & csb_active; } -void RecordKeyNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) +void RecordKeyNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - if (recStream != optRet->stream && (optRet->csb->csb_rpt[recStream].csb_flags & csb_active)) + if (recStream != currentStream && (csb->csb_rpt[recStream].csb_flags & csb_active)) { if (!streamList->exist(recStream)) streamList->add(recStream); @@ -10284,7 +10278,7 @@ ValueExprNode* RecordKeyNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* RecordKeyNode::execute(thread_db* /*tdbb*/, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); const record_param* rpb = &request->req_rpb[recStream]; @@ -10472,7 +10466,7 @@ ValueExprNode* ScalarNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Evaluate a scalar item from an array. -dsc* ScalarNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ScalarNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); const dsc* desc = EVL_expr(tdbb, request, field); @@ -10559,7 +10553,7 @@ ValueExprNode* StmtExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) return NULL; } -dsc* StmtExprNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* StmtExprNode::execute(thread_db* tdbb, Request* request) const { fb_assert(false); return NULL; @@ -10686,7 +10680,7 @@ ValueExprNode* StrCaseNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Low/up case a string. -dsc* StrCaseNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* StrCaseNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -10904,7 +10898,7 @@ ValueExprNode* StrLenNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Handles BIT_LENGTH(s), OCTET_LENGTH(s) and CHAR[ACTER]_LENGTH(s) -dsc* StrLenNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* StrLenNode::execute(thread_db* tdbb, Request* request) const { impure_value* const impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -11162,17 +11156,17 @@ bool SubQueryNode::computable(CompilerScratch* csb, StreamType stream, return rse->computable(csb, stream, allowOnlyCurrentStream, value1); } -void SubQueryNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void SubQueryNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { if (value2) - value2->findDependentFromStreams(optRet, streamList); + value2->findDependentFromStreams(csb, currentStream, streamList); - rse->findDependentFromStreams(optRet, streamList); + rse->findDependentFromStreams(csb, currentStream, streamList); // Check value expression, if any. if (value1) - value1->findDependentFromStreams(optRet, streamList); + value1->findDependentFromStreams(csb, currentStream, streamList); } void SubQueryNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) @@ -11357,7 +11351,7 @@ ValueExprNode* SubQueryNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Evaluate a subquery expression. -dsc* SubQueryNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SubQueryNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -11680,7 +11674,7 @@ ValueExprNode* SubstringNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* SubstringNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SubstringNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); @@ -11986,7 +11980,7 @@ ValueExprNode* SubstringSimilarNode::pass2(thread_db* tdbb, CompilerScratch* csb return this; } -dsc* SubstringSimilarNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SubstringSimilarNode::execute(thread_db* tdbb, Request* request) const { // Run all expression arguments. @@ -12283,7 +12277,7 @@ ValueExprNode* SysFuncCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* SysFuncCallNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* SysFuncCallNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); return function->evlFunc(tdbb, function, args->items, impure); @@ -12552,7 +12546,7 @@ ValueExprNode* TrimNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Perform trim function = TRIM([where what FROM] string). -dsc* TrimNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* TrimNode::execute(thread_db* tdbb, Request* request) const { impure_value* impure = request->getImpure(impureOffset); request->req_flags &= ~req_null; @@ -12925,13 +12919,13 @@ ValueExprNode* UdfCallNode::pass1(thread_db* tdbb, CompilerScratch* csb) } CMP_post_access(tdbb, csb, function->getSecurityName(), ssRelationId, - SCL_execute, SCL_object_function, function->getName().identifier); + SCL_execute, obj_functions, function->getName().identifier); } else { CMP_post_access(tdbb, csb, function->getSecurityName(), (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, SCL_object_package, function->getName().package); + SCL_execute, obj_packages, function->getName().package); } ExternalAccess temp(ExternalAccess::exa_function, function->getId()); @@ -12980,7 +12974,7 @@ ValueExprNode* UdfCallNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const { UCHAR* impure = request->getImpure(impureOffset); Impure* impureArea = request->getImpure(impureOffset); @@ -13104,7 +13098,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const const SavNumber savNumber = transaction->tra_save_point ? transaction->tra_save_point->getNumber() : 0; - jrd_req* funcRequest = function->getStatement()->findRequest(tdbb); + Request* funcRequest = function->getStatement()->findRequest(tdbb); // trace function execution start TraceFuncExecute trace(tdbb, funcRequest, request, inMsg, inMsgLength); @@ -13115,7 +13109,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const { Jrd::ContextPoolHolder context(tdbb, funcRequest->req_pool); // Save the old pool. - funcRequest->req_gmt_timestamp = request->req_gmt_timestamp; + funcRequest->setGmtTimeStamp(request->getGmtTimeStamp()); EXE_start(tdbb, funcRequest, transaction); @@ -13145,7 +13139,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const EXE_unwind(tdbb, funcRequest); funcRequest->req_attachment = NULL; funcRequest->req_flags &= ~(req_in_use | req_proc_fetch); - funcRequest->req_gmt_timestamp.invalidate(); + funcRequest->invalidateTimeStamp(); throw; } @@ -13173,7 +13167,7 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const funcRequest->req_attachment = NULL; funcRequest->req_flags &= ~(req_in_use | req_proc_fetch); - funcRequest->req_gmt_timestamp.invalidate(); + funcRequest->invalidateTimeStamp(); } if (!(request->req_flags & req_null)) @@ -13496,7 +13490,7 @@ ValueExprNode* ValueIfNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -dsc* ValueIfNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* ValueIfNode::execute(thread_db* tdbb, Request* request) const { return EVL_expr(tdbb, request, (condition->execute(tdbb, request) ? trueValue : falseValue)); } @@ -13509,24 +13503,25 @@ static RegisterNode regVariableNode({blr_variable}); VariableNode::VariableNode(MemoryPool& pool) : TypedNode(pool), - dsqlName(pool), - dsqlVar(NULL), - varDecl(NULL), - varInfo(NULL), - varId(0) + dsqlName(pool) { } -DmlNode* VariableNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) +DmlNode* VariableNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { const USHORT n = csb->csb_blr_reader.getWord(); - vec* vector = csb->csb_variables; - - if (!vector || n >= vector->count()) - PAR_error(csb, Arg::Gds(isc_badvarnum)); VariableNode* node = FB_NEW_POOL(pool) VariableNode(pool); node->varId = n; + node->outerDecl = csb->outerVarsMap.exist(n); + + if (node->outerDecl) + { + fb_assert(csb->mainCsb); + + if (csb->mainCsb) + csb->mainCsb->csb_variables_used_in_subroutines.add(node->varId); + } return node; } @@ -13540,6 +13535,7 @@ string VariableNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, varId); NODE_PRINT(printer, varDecl); NODE_PRINT(printer, varInfo); + NODE_PRINT(printer, outerDecl); return "VariableNode"; } @@ -13550,6 +13546,35 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) node->dsqlName = dsqlName; node->dsqlVar = dsqlVar ? dsqlVar.getObject() : dsqlScratch->resolveVariable(dsqlName); + if (!node->dsqlVar && dsqlScratch->mainScratch) + { + if ((node->dsqlVar = dsqlScratch->mainScratch->resolveVariable(dsqlName))) + { + node->outerDecl = true; + + const bool execBlock = (dsqlScratch->mainScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && + !(dsqlScratch->mainScratch->flags & + (DsqlCompilerScratch::FLAG_PROCEDURE | + DsqlCompilerScratch::FLAG_TRIGGER | + DsqlCompilerScratch::FLAG_FUNCTION)); + + if (node->dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) + { + if (!dsqlScratch->outerMessagesMap.exist(node->dsqlVar->msgNumber)) + { + // 0 = input, 1 = output. Start outer messages with 2. + dsqlScratch->outerMessagesMap.put( + node->dsqlVar->msgNumber, 2 + dsqlScratch->outerMessagesMap.count()); + } + } + else + { + if (!dsqlScratch->outerVarsMap.exist(node->dsqlVar->number)) + dsqlScratch->outerVarsMap.put(node->dsqlVar->number, dsqlScratch->hiddenVarsNumber++); + } + } + } + if (!node->dsqlVar) PASS1_field_unknown(NULL, dsqlName.c_str(), this); @@ -13563,8 +13588,10 @@ void VariableNode::setParameterName(dsql_par* parameter) const void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - bool execBlock = (dsqlScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && - !(dsqlScratch->flags & + auto varScratch = outerDecl ? dsqlScratch->mainScratch : dsqlScratch; + + const bool execBlock = (varScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && + !(varScratch->flags & (DsqlCompilerScratch::FLAG_PROCEDURE | DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)); @@ -13572,7 +13599,16 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) { dsqlScratch->appendUChar(blr_parameter2); - dsqlScratch->appendUChar(dsqlVar->msgNumber); + + if (outerDecl) + { + const auto messageNumPtr = dsqlScratch->outerMessagesMap.get(dsqlVar->msgNumber); + fb_assert(messageNumPtr); + dsqlScratch->appendUChar(*messageNumPtr); + } + else + dsqlScratch->appendUChar(dsqlVar->msgNumber); + dsqlScratch->appendUShort(dsqlVar->msgItem); dsqlScratch->appendUShort(dsqlVar->msgItem + 1); } @@ -13580,7 +13616,15 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) { // If this is an EXECUTE BLOCK input parameter, use the internal variable. dsqlScratch->appendUChar(blr_variable); - dsqlScratch->appendUShort(dsqlVar->number); + + if (outerDecl) + { + const auto varNumPtr = dsqlScratch->outerVarsMap.get(dsqlVar->number); + fb_assert(varNumPtr); + dsqlScratch->appendUShort(*varNumPtr); + } + else + dsqlScratch->appendUShort(dsqlVar->number); } } @@ -13595,7 +13639,8 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o if (!o) return false; - if (dsqlVar->field != o->dsqlVar->field || + if (outerDecl != o->outerDecl || + dsqlVar->field != o->dsqlVar->field || dsqlVar->field->fld_name != o->dsqlVar->field->fld_name || dsqlVar->number != o->dsqlVar->number || dsqlVar->msgItem != o->dsqlVar->msgItem || @@ -13607,6 +13652,19 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o return true; } +Request* VariableNode::getVarRequest(Request* request) const +{ + auto varRequest = request; + + if (outerDecl) + { + while (varRequest->getStatement()->parentStatement) + varRequest = varRequest->req_caller; + } + + return varRequest; +} + void VariableNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) { *desc = varDecl->varDesc; @@ -13616,6 +13674,7 @@ ValueExprNode* VariableNode::copy(thread_db* tdbb, NodeCopier& copier) const { VariableNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) VariableNode(*tdbb->getDefaultPool()); node->varId = copier.csb->csb_remap_variable + varId; + node->outerDecl = outerDecl; node->varDecl = varDecl; node->varInfo = varInfo; @@ -13636,45 +13695,84 @@ ValueExprNode* VariableNode::pass1(thread_db* tdbb, CompilerScratch* csb) ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - varInfo = CMP_pass2_validation(tdbb, csb, Item(Item::TYPE_VARIABLE, varId)); + const auto varCsb = outerDecl ? csb->mainCsb : csb; + + varInfo = CMP_pass2_validation(tdbb, varCsb, Item(Item::TYPE_VARIABLE, varDecl->varId)); ValueExprNode::pass2(tdbb, csb); - if (nodFlags & FLAG_VALUE) - impureOffset = csb->allocImpure(); + if (varDecl->usedInSubRoutines) + impureOffset = csb->allocImpure(); else impureOffset = csb->allocImpure(); return this; } -dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const +dsc* VariableNode::execute(thread_db* tdbb, Request* request) const { - impure_value* const impure = request->getImpure(impureOffset); - impure_value* impure2 = request->getImpure(varDecl->impureOffset); + const auto varRequest = getVarRequest(request); + const auto varImpure = varRequest->getImpure(varDecl->impureOffset); request->req_flags &= ~req_null; - if (impure2->vlu_desc.dsc_flags & DSC_null) - request->req_flags |= req_null; + dsc* desc; - impure->vlu_desc = impure2->vlu_desc; - - if (impure->vlu_desc.dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); - - if (!(impure2->vlu_flags & VLU_checked)) + if (varDecl->usedInSubRoutines) { - if (varInfo) + const auto impure = request->getImpure(impureOffset); + + if (varImpure->vlu_desc.dsc_flags & DSC_null) + request->req_flags |= req_null; + else { - EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, - &impure->vlu_desc, (impure->vlu_desc.dsc_flags & DSC_null)); + EVL_make_value(tdbb, &varImpure->vlu_desc, impure); + + if (impure->vlu_desc.dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); } - impure2->vlu_flags |= VLU_checked; + if (!(varImpure->vlu_flags & VLU_checked)) + { + if (varInfo) + { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); + + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, + &impure->vlu_desc, (varImpure->vlu_desc.dsc_flags & DSC_null)); + } + + varImpure->vlu_flags |= VLU_checked; + } + + desc = &impure->vlu_desc; + } + else + { + desc = request->getImpure(impureOffset); + + if (varImpure->vlu_desc.dsc_flags & DSC_null) + request->req_flags |= req_null; + + *desc = varImpure->vlu_desc; + + if (desc->dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, desc); + + if (!(varImpure->vlu_flags & VLU_checked)) + { + if (varInfo) + { + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, + desc, (desc->dsc_flags & DSC_null)); + } + + varImpure->vlu_flags |= VLU_checked; + } } - return (request->req_flags & req_null) ? NULL : &impure->vlu_desc; + return (request->req_flags & req_null) ? nullptr : desc; } diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index ba81a90344..a1d53e82aa 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -40,7 +40,7 @@ class RelationSourceNode; class ValueListNode; -class ArithmeticNode : public TypedNode +class ArithmeticNode final : public TypedNode { public: ArithmeticNode(MemoryPool& pool, UCHAR aBlrOp, bool aDialect1, @@ -91,7 +91,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; // add and add2 are used in somewhat obscure way in aggregation. static dsc* add(thread_db* tdbb, const dsc* desc, impure_value* value, const ValueExprNode* node, @@ -124,7 +124,7 @@ public: }; -class ArrayNode : public TypedNode +class ArrayNode final : public TypedNode { public: ArrayNode(MemoryPool& pool, FieldNode* aField); @@ -160,7 +160,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -171,7 +171,7 @@ public: }; -class AtNode : public TypedNode +class AtNode final : public TypedNode { public: AtNode(MemoryPool& pool, ValueExprNode* aDateTimeArg = NULL, ValueExprNode* aZoneArg = NULL); @@ -196,7 +196,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst dateTimeArg; @@ -204,7 +204,7 @@ public: }; -class BoolAsValueNode : public TypedNode +class BoolAsValueNode final : public TypedNode { public: explicit BoolAsValueNode(MemoryPool& pool, BoolExprNode* aBoolean = NULL); @@ -230,14 +230,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst boolean; }; -class CastNode : public TypedNode +class CastNode final : public TypedNode { public: explicit CastNode(MemoryPool& pool, ValueExprNode* aSource = NULL, dsql_fld* aDsqlField = NULL); @@ -269,7 +269,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: MetaName dsqlAlias; @@ -281,7 +281,7 @@ public: }; -class CoalesceNode : public TypedNode +class CoalesceNode final : public TypedNode { public: explicit CoalesceNode(MemoryPool& pool, ValueListNode* aArgs = NULL) @@ -310,7 +310,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; virtual bool possiblyUnknown() const { @@ -322,7 +322,7 @@ public: }; -class CollateNode : public TypedNode +class CollateNode final : public TypedNode { public: CollateNode(MemoryPool& pool, ValueExprNode* aArg, const MetaName& aCollation); @@ -369,7 +369,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -384,7 +384,7 @@ public: }; -class ConcatenateNode : public TypedNode +class ConcatenateNode final : public TypedNode { public: explicit ConcatenateNode(MemoryPool& pool, ValueExprNode* aArg1 = NULL, ValueExprNode* aArg2 = NULL); @@ -409,7 +409,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg1; @@ -417,7 +417,7 @@ public: }; -class CurrentDateNode : public TypedNode +class CurrentDateNode final : public TypedNode { public: explicit CurrentDateNode(MemoryPool& pool) @@ -435,11 +435,11 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; }; -class CurrentTimeNode : public TypedNode +class CurrentTimeNode final : public TypedNode { public: CurrentTimeNode(MemoryPool& pool, unsigned aPrecision) @@ -459,14 +459,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; }; -class CurrentTimeStampNode : public TypedNode +class CurrentTimeStampNode final : public TypedNode { public: CurrentTimeStampNode(MemoryPool& pool, unsigned aPrecision) @@ -486,14 +486,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; }; -class CurrentRoleNode : public TypedNode +class CurrentRoleNode final : public TypedNode { public: explicit CurrentRoleNode(MemoryPool& pool) @@ -512,11 +512,11 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; }; -class CurrentUserNode : public TypedNode +class CurrentUserNode final : public TypedNode { public: explicit CurrentUserNode(MemoryPool& pool) @@ -535,11 +535,11 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; }; -class DecodeNode : public TypedNode +class DecodeNode final : public TypedNode { public: explicit DecodeNode(MemoryPool& pool, ValueExprNode* aTest = NULL, @@ -575,7 +575,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: Firebird::string label; @@ -615,7 +615,7 @@ private: }; -class DerivedExprNode : public TypedNode +class DerivedExprNode final : public TypedNode { public: explicit DerivedExprNode(MemoryPool& pool) @@ -666,14 +666,14 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg; @@ -682,7 +682,7 @@ public: }; -class DomainValidationNode : public TypedNode +class DomainValidationNode final : public TypedNode { public: explicit DomainValidationNode(MemoryPool& pool) @@ -704,14 +704,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: dsc domDesc; }; -class ExtractNode : public TypedNode +class ExtractNode final : public TypedNode { public: ExtractNode(MemoryPool& pool, UCHAR aBlrSubOp, ValueExprNode* aArg = NULL); @@ -737,7 +737,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: UCHAR blrSubOp; @@ -745,7 +745,7 @@ public: }; -class FieldNode : public TypedNode +class FieldNode final : public TypedNode { public: FieldNode(MemoryPool& pool, dsql_ctx* context = NULL, dsql_fld* field = NULL, ValueListNode* indices = NULL); @@ -795,14 +795,14 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; private: static dsql_fld* resolveContext(DsqlCompilerScratch* dsqlScratch, @@ -823,7 +823,7 @@ public: }; -class GenIdNode : public TypedNode +class GenIdNode final : public TypedNode { public: GenIdNode(MemoryPool& pool, bool aDialect1, @@ -853,7 +853,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: GeneratorItem generator; @@ -868,7 +868,7 @@ private: }; -class InternalInfoNode : public TypedNode +class InternalInfoNode final : public TypedNode { public: struct InfoAttr @@ -898,14 +898,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg; }; -class LiteralNode : public TypedNode +class LiteralNode final : public TypedNode { public: explicit LiteralNode(MemoryPool& pool); @@ -927,7 +927,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; SLONG getSlong() const { @@ -952,7 +952,7 @@ public: }; -class DsqlAliasNode : public TypedNode +class DsqlAliasNode final : public TypedNode { public: DsqlAliasNode(MemoryPool& pool, const MetaName& aName, ValueExprNode* aValue) @@ -992,7 +992,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1005,7 +1005,7 @@ public: }; -class DsqlMapNode : public TypedNode +class DsqlMapNode final : public TypedNode { public: DsqlMapNode(MemoryPool& pool, dsql_ctx* aContext, dsql_map* aMap); @@ -1041,7 +1041,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1055,7 +1055,7 @@ public: }; -class DerivedFieldNode : public TypedNode +class DerivedFieldNode final : public TypedNode { public: DerivedFieldNode(MemoryPool& pool, const MetaName& aName, USHORT aScope, @@ -1117,7 +1117,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1131,7 +1131,7 @@ public: }; -class LocalTimeNode : public TypedNode +class LocalTimeNode final : public TypedNode { public: LocalTimeNode(MemoryPool& pool, unsigned aPrecision) @@ -1151,14 +1151,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; }; -class LocalTimeStampNode : public TypedNode +class LocalTimeStampNode final : public TypedNode { public: LocalTimeStampNode(MemoryPool& pool, unsigned aPrecision) @@ -1178,14 +1178,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: unsigned precision; }; -class NegateNode : public TypedNode +class NegateNode final : public TypedNode { public: explicit NegateNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -1209,14 +1209,14 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst arg; }; -class NullNode : public TypedNode +class NullNode final : public TypedNode { private: friend class Firebird::GlobalPtr; @@ -1247,7 +1247,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; private: static Firebird::GlobalPtr INSTANCE; @@ -1288,7 +1288,7 @@ class WindowClause : public DsqlNode public: // ListExprNode has no relation with this but works perfectly here for now. - class Frame : public TypedNode + class Frame final : public TypedNode { public: enum class Bound : UCHAR @@ -1360,7 +1360,7 @@ public: NestConst value; }; - class FrameExtent : public TypedNode + class FrameExtent final : public TypedNode { public: enum class Unit : UCHAR @@ -1521,7 +1521,7 @@ public: // OVER is used only in DSQL. In the engine, normal aggregate functions are used in partitioned // maps. -class OverNode : public TypedNode +class OverNode final : public TypedNode { public: explicit OverNode(MemoryPool& pool, AggNode* aAggExpr, const MetaName* aWindowName); @@ -1553,7 +1553,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst aggExpr; @@ -1562,7 +1562,7 @@ public: }; -class ParameterNode : public TypedNode +class ParameterNode final : public TypedNode { private: // CVC: This is a guess for the length of the parameter for LIKE and others, when the @@ -1579,10 +1579,7 @@ public: ValueExprNode::getChildren(holder, dsql); if (!dsql) - { holder.add(argFlag); - holder.add(argIndicator); - } } virtual Firebird::string internalPrint(NodePrinter& printer) const; @@ -1598,24 +1595,26 @@ public: virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; + Request* getParamRequest(Request* request) const; + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: dsql_msg* dsqlMessage = nullptr; dsql_par* dsqlParameter = nullptr; NestConst message; NestConst argFlag; - NestConst argIndicator; NestConst argInfo; USHORT dsqlParameterIndex = 0; USHORT argNumber = 0; + bool outerDecl = false; }; -class RecordKeyNode : public TypedNode +class RecordKeyNode final : public TypedNode { public: RecordKeyNode(MemoryPool& pool, UCHAR aBlrOp, const MetaName& aDsqlQualifier = NULL); @@ -1657,8 +1656,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; @@ -1666,7 +1665,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; const char* getAlias(bool rdb) const { @@ -1693,7 +1692,7 @@ public: }; -class ScalarNode : public TypedNode +class ScalarNode final : public TypedNode { public: explicit ScalarNode(MemoryPool& pool) @@ -1743,7 +1742,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst field; @@ -1751,7 +1750,7 @@ public: }; -class StmtExprNode : public TypedNode +class StmtExprNode final : public TypedNode { public: explicit StmtExprNode(MemoryPool& pool) @@ -1802,7 +1801,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst stmt; @@ -1810,7 +1809,7 @@ public: }; -class StrCaseNode : public TypedNode +class StrCaseNode final : public TypedNode { public: StrCaseNode(MemoryPool& pool, UCHAR aBlrOp, ValueExprNode* aArg = NULL); @@ -1836,7 +1835,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: const UCHAR blrOp; @@ -1844,7 +1843,7 @@ public: }; -class StrLenNode : public TypedNode +class StrLenNode final : public TypedNode { public: StrLenNode(MemoryPool& pool, UCHAR aBlrSubOp, ValueExprNode* aArg = NULL); @@ -1870,7 +1869,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: UCHAR blrSubOp; @@ -1879,7 +1878,7 @@ public: // This node is used for DSQL subqueries and for legacy (BLR-only) functionality. -class SubQueryNode : public TypedNode +class SubQueryNode final : public TypedNode { public: explicit SubQueryNode(MemoryPool& pool, UCHAR aBlrOp, RecordSourceNode* aDsqlRse = NULL, @@ -1916,15 +1915,15 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst dsqlRse; @@ -1937,7 +1936,7 @@ public: }; -class SubstringNode : public TypedNode +class SubstringNode final : public TypedNode { public: explicit SubstringNode(MemoryPool& pool, ValueExprNode* aExpr = NULL, @@ -1965,7 +1964,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; static dsc* perform(thread_db* tdbb, impure_value* impure, const dsc* valueDsc, const dsc* startDsc, const dsc* lengthDsc); @@ -1977,7 +1976,7 @@ public: }; -class SubstringSimilarNode : public TypedNode +class SubstringSimilarNode final : public TypedNode { public: explicit SubstringSimilarNode(MemoryPool& pool, ValueExprNode* aExpr = NULL, @@ -2006,7 +2005,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst expr; @@ -2015,7 +2014,7 @@ public: }; -class SysFuncCallNode : public TypedNode +class SysFuncCallNode final : public TypedNode { public: explicit SysFuncCallNode(MemoryPool& pool, const MetaName& aName, @@ -2040,7 +2039,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: MetaName name; @@ -2050,7 +2049,7 @@ public: }; -class TrimNode : public TypedNode +class TrimNode final : public TypedNode { public: explicit TrimNode(MemoryPool& pool, UCHAR aWhere, @@ -2079,7 +2078,7 @@ public: virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: UCHAR where; @@ -2088,7 +2087,7 @@ public: }; -class UdfCallNode : public TypedNode +class UdfCallNode final : public TypedNode { private: struct Impure @@ -2126,7 +2125,7 @@ public: virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: QualifiedName name; @@ -2139,7 +2138,7 @@ private: }; -class ValueIfNode : public TypedNode +class ValueIfNode final : public TypedNode { public: explicit ValueIfNode(MemoryPool& pool, BoolExprNode* aCondition = NULL, @@ -2173,7 +2172,7 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool sameAs(const ExprNode* other, bool ignoreStreams) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: NestConst condition; @@ -2182,7 +2181,7 @@ public: }; -class VariableNode : public TypedNode +class VariableNode final : public TypedNode { public: explicit VariableNode(MemoryPool& pool); @@ -2201,18 +2200,21 @@ public: dsqlDesc = desc; } + Request* getVarRequest(Request* request) const; + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; public: MetaName dsqlName; NestConst dsqlVar; NestConst varDecl; NestConst varInfo; - USHORT varId; + USHORT varId = 0; + bool outerDecl = false; }; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 2ba6afaa41..ee21b08f64 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -42,7 +42,7 @@ class Node; class NodePrinter; class ExprNode; class NodeRefsHolder; -class OptimizerBlk; +class Optimizer; class OptimizerRetrieval; class RecordSource; class RseNode; @@ -52,7 +52,7 @@ class ValueExprNode; // Must be less then MAX_SSHORT. Not used for static arrays. -const int MAX_CONJUNCTS = 32000; +const unsigned MAX_CONJUNCTS = 32000; // New: MAX_STREAMS should be a multiple of BITS_PER_LONG (32 and hard to believe it will change) @@ -64,7 +64,7 @@ const StreamType STREAM_MAP_LENGTH = MAX_STREAMS + 2; // New formula is simply MAX_STREAMS / BITS_PER_LONG const int OPT_STREAM_BITS = MAX_STREAMS / BITS_PER_LONG; // 128 with 4096 streams -typedef Firebird::HalfStaticArray StreamList; +typedef Firebird::HalfStaticArray StreamList; typedef Firebird::SortedArray SortedStreamList; typedef Firebird::Array > NestValueArray; @@ -213,7 +213,7 @@ public: virtual DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_DDL); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_DDL); return this; } @@ -283,7 +283,7 @@ public: return this; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const = 0; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const = 0; }; @@ -300,12 +300,12 @@ public: { Node::dsqlPass(dsqlScratch); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_SESSION_MANAGEMENT); return this; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const = 0; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const = 0; }; @@ -539,8 +539,7 @@ public: static const USHORT FLAG_DOUBLE = 0x20; static const USHORT FLAG_DATE = 0x40; static const USHORT FLAG_DECFLOAT = 0x80; - static const USHORT FLAG_VALUE = 0x100; // Full value area required in impure space. - static const USHORT FLAG_INT128 = 0x200; + static const USHORT FLAG_INT128 = 0x100; explicit ExprNode(Type aType, MemoryPool& pool) : DmlNode(pool), @@ -693,8 +692,8 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value = NULL); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual ExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); virtual ExprNode* copy(thread_db* tdbb, NodeCopier& copier) const = 0; @@ -747,7 +746,7 @@ public: } virtual BoolExprNode* copy(thread_db* tdbb, NodeCopier& copier) const = 0; - virtual bool execute(thread_db* tdbb, jrd_req* request) const = 0; + virtual bool execute(thread_db* tdbb, Request* request) const = 0; }; class ValueExprNode : public ExprNode @@ -829,7 +828,7 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) = 0; virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const = 0; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const = 0; + virtual dsc* execute(thread_db* tdbb, Request* request) const = 0; public: SCHAR nodScale; @@ -886,7 +885,7 @@ public: return NULL; } - virtual dsc* execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const + virtual dsc* execute(thread_db* /*tdbb*/, Request* /*request*/) const { fb_assert(false); return NULL; @@ -1044,19 +1043,19 @@ public: return false; } - virtual dsc* winPass(thread_db* /*tdbb*/, jrd_req* /*request*/, SlidingWindow* /*window*/) const + virtual dsc* winPass(thread_db* /*tdbb*/, Request* /*request*/, SlidingWindow* /*window*/) const { return NULL; } - virtual void aggInit(thread_db* tdbb, jrd_req* request) const = 0; // pure, but defined - virtual void aggFinish(thread_db* tdbb, jrd_req* request) const; - virtual bool aggPass(thread_db* tdbb, jrd_req* request) const; - virtual dsc* execute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const = 0; // pure, but defined + virtual void aggFinish(thread_db* tdbb, Request* request) const; + virtual bool aggPass(thread_db* tdbb, Request* request) const; + virtual dsc* execute(thread_db* tdbb, Request* request) const; virtual unsigned getCapabilities() const = 0; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const = 0; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const = 0; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const = 0; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const = 0; virtual AggNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); @@ -1088,11 +1087,11 @@ public: explicit WinFuncNode(MemoryPool& pool, const AggInfo& aAggInfo, ValueExprNode* aArg = NULL); public: - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const { } - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const { return NULL; } @@ -1190,7 +1189,7 @@ public: streamList.add(getStream()); } - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) = 0; + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream) = 0; public: dsql_ctx* dsqlContext; @@ -1409,6 +1408,7 @@ public: TYPE_MERGE_SEND, TYPE_MESSAGE, TYPE_MODIFY, + TYPE_OUTER_MAP, TYPE_POST_EVENT, TYPE_RECEIVE, TYPE_RETURN, @@ -1441,7 +1441,7 @@ public: struct ExeState { - ExeState(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) + ExeState(thread_db* tdbb, Request* request, jrd_tra* transaction) : savedTdbb(tdbb), oldPool(tdbb->getDefaultPool()), oldRequest(tdbb->getRequest()), @@ -1467,7 +1467,7 @@ public: thread_db* savedTdbb; MemoryPool* oldPool; // Save the old pool to restore on exit. - jrd_req* oldRequest; // Save the old request to restore on exit. + Request* oldRequest; // Save the old request to restore on exit. jrd_tra* oldTransaction; // Save the old transcation to restore on exit. const StmtNode* topNode; const StmtNode* prevNode; @@ -1529,7 +1529,7 @@ public: return NULL; } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const = 0; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const = 0; public: NestConst parentStmt; @@ -1567,7 +1567,7 @@ public: return NULL; } - const StmtNode* execute(thread_db* /*tdbb*/, jrd_req* /*request*/, ExeState* /*exeState*/) const + const StmtNode* execute(thread_db* /*tdbb*/, Request* /*request*/, ExeState* /*exeState*/) const { fb_assert(false); return NULL; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 71d40ac857..da0ebd5637 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -221,21 +221,14 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // items for (unsigned i = 0; i < items->getCount(); ++i) { - DsqlCompiledStatement* itemStatement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); - - DsqlCompilerScratch* itemScratch = (*items)[i].dsqlScratch = - FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), - dsqlScratch->getTransaction(), itemStatement); - - itemScratch->clientDialect = dsqlScratch->clientDialect; - itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; - itemScratch->package = name; + DdlNode* ddlNode; switch ((*items)[i].type) { case CreateAlterPackageNode::Item::FUNCTION: { CreateAlterFunctionNode* const fun = (*items)[i].function; + ddlNode = fun; if (functionNames.exist(fun->name)) { @@ -249,13 +242,13 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) fun->alter = true; fun->package = name; - fun->dsqlPass(itemScratch); break; } case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* const proc = (*items)[i].procedure; + ddlNode = proc; if (procedureNames.exist(proc->name)) { @@ -269,10 +262,24 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) proc->alter = true; proc->package = name; - proc->dsqlPass(itemScratch); break; } + + default: + fb_assert(false); } + + auto itemStatement = FB_NEW_POOL(pool) DsqlDdlStatement(pool, dsqlScratch->getAttachment(), ddlNode); + + auto itemScratch = (*items)[i].dsqlScratch = + FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), + dsqlScratch->getTransaction(), itemStatement); + + itemScratch->clientDialect = dsqlScratch->clientDialect; + itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; + itemScratch->package = name; + + ddlNode->dsqlPass(itemScratch); } return DdlNode::dsqlPass(dsqlScratch); @@ -290,7 +297,7 @@ void CreateAlterPackageNode::checkPermission(thread_db* tdbb, jrd_tra* transacti return; } - SCL_check_create_access(tdbb, SCL_object_package); + SCL_check_create_access(tdbb, obj_packages); } @@ -432,6 +439,8 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* PKG.RDB$SQL_SECURITY.NULL = FALSE; PKG.RDB$SQL_SECURITY = ssDefiner.value ? FB_TRUE : FB_FALSE; } + else + PKG.RDB$SQL_SECURITY.NULL = TRUE; END_MODIFY owner = PKG.RDB$OWNER_NAME; @@ -616,21 +625,14 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) for (unsigned j = 0; j < arrays[i]->getCount(); ++j) { - DsqlCompiledStatement* itemStatement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); - - DsqlCompilerScratch* itemScratch = (*arrays[i])[j].dsqlScratch = - FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), - dsqlScratch->getTransaction(), itemStatement); - - itemScratch->clientDialect = dsqlScratch->clientDialect; - itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; - itemScratch->package = name; + DdlNode* ddlNode; switch ((*arrays[i])[j].type) { case CreateAlterPackageNode::Item::FUNCTION: { CreateAlterFunctionNode* const fun = (*arrays[i])[j].function; + ddlNode = fun; if (functionNames[i].exist(fun->name)) { @@ -648,14 +650,13 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (arrays[i] == items) fun->alter = true; - fun->dsqlPass(itemScratch); - break; } case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* const proc = (*arrays[i])[j].procedure; + ddlNode = proc; if (procedureNames[i].exist(proc->name)) { @@ -673,11 +674,24 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (arrays[i] == items) proc->alter = true; - proc->dsqlPass(itemScratch); - break; } + + default: + fb_assert(false); } + + auto itemStatement = FB_NEW_POOL(pool) DsqlDdlStatement(pool, dsqlScratch->getAttachment(), ddlNode); + + auto itemScratch = (*arrays[i])[j].dsqlScratch = + FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), + dsqlScratch->getTransaction(), itemStatement); + + itemScratch->clientDialect = dsqlScratch->clientDialect; + itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL; + itemScratch->package = name; + + ddlNode->dsqlPass(itemScratch); } } @@ -687,7 +701,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) void CreatePackageBodyNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) { - SCL_check_create_access(tdbb, SCL_object_package); + SCL_check_create_access(tdbb, obj_packages); } diff --git a/src/dsql/Parser.cpp b/src/dsql/Parser.cpp index bacacc6378..af0f9ab9f4 100644 --- a/src/dsql/Parser.cpp +++ b/src/dsql/Parser.cpp @@ -98,10 +98,10 @@ namespace } -Parser::Parser(thread_db* tdbb, MemoryPool& pool, DsqlCompilerScratch* aScratch, - USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, - SSHORT characterSet) +Parser::Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, + USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT characterSet) : PermanentStorage(pool), + statementPool(aStatementPool), scratch(aScratch), client_dialect(aClientDialect), db_dialect(aDbDialect), @@ -172,7 +172,7 @@ Parser::~Parser() } -dsql_req* Parser::parse() +DsqlStatement* Parser::parse() { if (parseAux() != 0) { @@ -182,7 +182,7 @@ dsql_req* Parser::parse() transformString(lex.start, lex.end - lex.start, transformedString); - return DSQL_parse; + return parsedStatement; } diff --git a/src/dsql/Parser.h b/src/dsql/Parser.h index da561e1dc1..28e22323f5 100644 --- a/src/dsql/Parser.h +++ b/src/dsql/Parser.h @@ -130,13 +130,12 @@ public: static const int MAX_TOKEN_LEN = 256; public: - Parser(thread_db* tdbb, MemoryPool& pool, DsqlCompilerScratch* aScratch, USHORT aClientDialect, - USHORT aDbDialect, const TEXT* string, size_t length, - SSHORT characterSet); + Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch, + USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT characterSet); ~Parser(); public: - dsql_req* parse(); + DsqlStatement* parse(); const Firebird::string& getTransformedString() const { @@ -233,11 +232,6 @@ private: return cmpNode; } - MemoryPool& getStatementPool() - { - return scratch->getStatement()->getPool(); - } - void yyReducePosn(YYPOSN& ret, YYPOSN* termPosns, YYSTYPE* termVals, int termNo, int stkPos, int yychar, YYPOSN& yyposn, void*); @@ -376,6 +370,7 @@ private: // end - defined in btyacc_fb.ske private: + MemoryPool* statementPool; DsqlCompilerScratch* scratch; USHORT client_dialect; USHORT db_dialect; @@ -385,7 +380,7 @@ private: Firebird::string transformedString; Firebird::GenericMap > strMarks; bool stmt_ambiguous; - dsql_req* DSQL_parse; + DsqlStatement* parsedStatement; // These value/posn are taken from the lexer YYSTYPE yylval; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index e755666a5d..585bf5fa6d 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -34,7 +34,7 @@ #include "../jrd/tra.h" #include "../jrd/Coercion.h" #include "../jrd/Function.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/VirtualTable.h" #include "../jrd/extds/ExtDS.h" @@ -72,8 +72,8 @@ namespace Jrd { template static void dsqlExplodeFields(dsql_rel* relation, Array >& fields, bool includeComputed); -static dsql_par* dsqlFindDbKey(const dsql_req*, const RelationSourceNode*); -static dsql_par* dsqlFindRecordVersion(const dsql_req*, const RelationSourceNode*); +static dsql_par* dsqlFindDbKey(const DsqlDmlStatement*, const RelationSourceNode*); +static dsql_par* dsqlFindRecordVersion(const DsqlDmlStatement*, const RelationSourceNode*); static void dsqlGenEofAssignment(DsqlCompilerScratch* dsqlScratch, SSHORT value); static void dsqlGenReturning(DsqlCompilerScratch* dsqlScratch, ReturningClause* returning, Nullable localTableNumber); @@ -111,7 +111,7 @@ static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs, StmtNode::WhichTrigger whichTrig, record_param* rpb, record_param* rec, TriggerAction op); static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, StreamType stream, CompoundStmtNode* compoundNode, const Nullable* insertOverride); -static void restartRequest(const jrd_req* request, jrd_tra* transaction); +static void restartRequest(const Request* request, jrd_tra* transaction); static void validateExpressions(thread_db* tdbb, const Array& validations); } // namespace Jrd @@ -382,12 +382,12 @@ AssignmentNode* AssignmentNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* AssignmentNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* AssignmentNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { EXE_assignment(tdbb, this); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -484,14 +484,14 @@ BlockNode* BlockNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* BlockNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { jrd_tra* transaction = request->req_transaction; SavNumber savNumber; switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (!(transaction->tra_flags & TRA_system)) { const Savepoint* const savepoint = transaction->startSavepoint(); @@ -500,7 +500,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } return action; - case jrd_req::req_unwind: + case Request::req_unwind: { if (request->req_flags & (req_leave | req_continue_loop)) { @@ -574,7 +574,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* if (testAndFixupError(tdbb, request, handlerNode->conditions)) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; exeState->errorPending = false; // On entering looper exeState->oldRequest etc. are saved. @@ -649,7 +649,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* return temp; } - case jrd_req::req_return: + case Request::req_return: if (!(transaction->tra_flags & TRA_system)) { savNumber = *request->getImpure(impureOffset); @@ -673,7 +673,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Test for match of current state with list of error conditions. Fix type and code of the exception. -bool BlockNode::testAndFixupError(thread_db* tdbb, jrd_req* request, const ExceptionArray& conditions) +bool BlockNode::testAndFixupError(thread_db* tdbb, Request* request, const ExceptionArray& conditions) { if (tdbb->tdbb_flags & TDBB_sys_error) return false; @@ -841,13 +841,13 @@ CompoundStmtNode* CompoundStmtNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { const NestConst* end = statements.end(); if (onlyAssignments) { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { for (const NestConst* i = statements.begin(); i != end; ++i) { @@ -862,7 +862,7 @@ const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, jrd_req* request, Exe EXE_assignment(tdbb, static_cast(stmt)); } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -872,18 +872,18 @@ const StmtNode* CompoundStmtNode::execute(thread_db* tdbb, jrd_req* request, Exe switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: impure->sta_state = 0; // fall into - case jrd_req::req_return: - case jrd_req::req_sync: + case Request::req_return: + case Request::req_sync: if (impure->sta_state < statements.getCount()) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statements[impure->sta_state++]; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; // fall into default: @@ -938,11 +938,11 @@ void ContinueLeaveNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(labelNumber); } -const StmtNode* ContinueLeaveNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ContinueLeaveNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { - request->req_operation = jrd_req::req_unwind; + request->req_operation = Request::req_unwind; request->req_label = labelNumber; request->req_flags |= (blrOp == blr_continue_loop ? req_continue_loop : req_leave); } @@ -1117,7 +1117,7 @@ CursorStmtNode* CursorStmtNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* CursorStmtNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { fb_assert(cursorNumber < request->req_cursors.getCount()); const Cursor* const cursor = request->req_cursors[cursorNumber]; @@ -1126,18 +1126,18 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt switch (cursorOp) { case blr_cursor_open: - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { cursor->open(tdbb); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; case blr_cursor_close: - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { cursor->close(tdbb); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -1145,7 +1145,7 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt case blr_cursor_fetch_scroll: switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (cursor->isUpdateCounters()) request->req_records_affected.clear(); @@ -1187,11 +1187,11 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt if (fetched) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return intoStmt; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; default: return parentStmt; @@ -1329,9 +1329,9 @@ DeclareCursorNode* DeclareCursorNode::pass2(thread_db* tdbb, CompilerScratch* cs return this; } -const StmtNode* DeclareCursorNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareCursorNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { // Set up the cursors array... if (cursorNumber >= request->req_cursors.getCount()) @@ -1339,7 +1339,7 @@ const StmtNode* DeclareCursorNode::execute(thread_db* /*tdbb*/, jrd_req* request // And store cursor there. request->req_cursors[cursorNumber] = cursor; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -1439,15 +1439,23 @@ DeclareLocalTableNode* DeclareLocalTableNode::pass2(thread_db* /*tdbb*/, Compile return this; } -const StmtNode* DeclareLocalTableNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareLocalTableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + { + if (auto& recordBuffer = getImpure(tdbb, request, false)->recordBuffer) + { + delete recordBuffer; + recordBuffer = nullptr; + } + + request->req_operation = Request::req_return; + } return parentStmt; } -DeclareLocalTableNode::Impure* DeclareLocalTableNode::getImpure(thread_db* tdbb, jrd_req* request, bool createWhenDead) const +DeclareLocalTableNode::Impure* DeclareLocalTableNode::getImpure(thread_db* tdbb, Request* request, bool createWhenDead) const { const auto impure = request->getImpure(impureOffset); @@ -1697,7 +1705,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (prevDecl) dsqlScratch->putSubFunction(this, true); - DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); + auto statement = FB_NEW_POOL(pool) DsqlDmlStatement(pool, dsqlScratch->getAttachment(), dsqlBlock); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) statement->setBlrVersion(5); @@ -1709,7 +1717,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); @@ -1729,7 +1737,7 @@ void DeclareSubFuncNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlBlock) // forward decl return; - GEN_request(blockScratch, dsqlBlock); + GEN_statement(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subfunc_decl); dsqlScratch->appendNullString(name.c_str()); @@ -1767,28 +1775,25 @@ void DeclareSubFuncNode::genParameters(DsqlCompilerScratch* dsqlScratch, } } -DeclareSubFuncNode* DeclareSubFuncNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +DeclareSubFuncNode* DeclareSubFuncNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/) { - return this; -} - -DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/) -{ - // scope needed here? - { // scope - ContextPoolHolder context(tdbb, &subCsb->csb_pool); - PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); - } + ContextPoolHolder context(tdbb, &subCsb->csb_pool); + PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); return this; } -const StmtNode* DeclareSubFuncNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +{ + return this; +} + +const StmtNode* DeclareSubFuncNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { // Nothing to execute. This is the declaration node. - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; return parentStmt; } @@ -2039,7 +2044,7 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (prevDecl) dsqlScratch->putSubProcedure(this, true); - DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); + auto statement = FB_NEW_POOL(pool) DsqlDmlStatement(pool, dsqlScratch->getAttachment(), dsqlBlock); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) statement->setBlrVersion(5); @@ -2051,7 +2056,7 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + statement->setType(DsqlStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); @@ -2071,7 +2076,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlBlock) // forward decl return; - GEN_request(blockScratch, dsqlBlock); + GEN_statement(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subproc_decl); dsqlScratch->appendNullString(name.c_str()); @@ -2079,7 +2084,7 @@ void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(SUB_ROUTINE_TYPE_PSQL); dsqlScratch->appendUChar( - blockScratch->getStatement()->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ? 1 : 0); + blockScratch->getDsqlStatement()->getFlags() & DsqlStatement::FLAG_SELECTABLE ? 1 : 0); genParameters(dsqlScratch, dsqlBlock->parameters); genParameters(dsqlScratch, dsqlBlock->returns); @@ -2111,12 +2116,7 @@ void DeclareSubProcNode::genParameters(DsqlCompilerScratch* dsqlScratch, } } -DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) -{ - return this; -} - -DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/) +DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/) { ContextPoolHolder context(tdbb, &subCsb->csb_pool); PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); @@ -2124,12 +2124,17 @@ DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* return this; } -const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +{ + return this; +} + +const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { // Nothing to execute. This is the declaration node. - if (request->req_operation == jrd_req::req_evaluate) - request->req_operation = jrd_req::req_return; + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; return parentStmt; } @@ -2207,6 +2212,9 @@ DeclareVariableNode* DeclareVariableNode::pass1(thread_db* tdbb, CompilerScratch fb_assert(!(*vector)[varId]); (*vector)[varId] = this; + if (!csb->mainCsb && csb->csb_variables_used_in_subroutines.exist(varId)) + usedInSubRoutines = true; + return this; } @@ -2216,9 +2224,9 @@ DeclareVariableNode* DeclareVariableNode::pass2(thread_db* /*tdbb*/, CompilerScr return this; } -const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { impure_value* variable = request->getImpure(impureOffset); variable->vlu_desc = varDesc; @@ -2238,7 +2246,7 @@ const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, jrd_req* request, else variable->vlu_desc.dsc_address = (UCHAR*) &variable->vlu_misc; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -2290,8 +2298,8 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return SavepointEncloseNode::make(dsqlScratch->getPool(), dsqlScratch, node); } - dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? - DsqlCompiledStatement::TYPE_DELETE_CURSOR : DsqlCompiledStatement::TYPE_DELETE); + dsqlScratch->getDsqlStatement()->setType(dsqlCursorName.hasData() ? + DsqlStatement::TYPE_DELETE_CURSOR : DsqlStatement::TYPE_DELETE); // Generate record selection expression. @@ -2377,7 +2385,7 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) else { dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); } } @@ -2569,13 +2577,13 @@ EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* EraseNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { const StmtNode* retNode; - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) retNode = parentStmt; - else if (request->req_operation == jrd_req::req_return && subStatement) + else if (request->req_operation == Request::req_return && subStatement) { if (!exeState->topNode) { @@ -2599,7 +2607,7 @@ const StmtNode* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState->whichEraseTrig = ALL_TRIGS; } else - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; } else { @@ -2614,7 +2622,7 @@ const StmtNode* EraseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Perform erase operation. -const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const { jrd_tra* transaction = request->req_transaction; record_param* rpb = &request->req_rpb[stream]; @@ -2622,7 +2630,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: { if (!(marks & MARK_AVOID_COUNTERS)) request->req_records_affected.bumpModified(false); @@ -2640,14 +2648,14 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger return statement; } - case jrd_req::req_return: + case Request::req_return: break; default: return parentStmt; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; RLCK_reserve_relation(tdbb, transaction, relation, true); if (rpb->rpb_runtime_flags & RPB_just_deleted) @@ -2884,7 +2892,7 @@ ErrorHandlerNode* ErrorHandlerNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* exeState) const +const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* exeState) const { if ((request->req_flags & req_error_handler) && !exeState->errorPending) { @@ -2897,7 +2905,7 @@ const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, const StmtNode* retNode = parentStmt; retNode = retNode->parentStmt; - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) retNode = retNode->parentStmt; request->req_last_xcp.clear(); @@ -2917,6 +2925,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr { SET_TDBB(tdbb); + const auto blrStartPos = csb->csb_blr_reader.getPos(); jrd_prc* procedure = NULL; QualifiedName name; @@ -2949,6 +2958,25 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr if (!procedure) PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); + else + { + if (procedure->isImplemented() && !procedure->isDefined()) + { + if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) + { + PAR_warning( + Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Warning(isc_modnotfound)); + } + else + { + csb->csb_blr_reader.setPos(blrStartPos); + PAR_error(csb, + Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Gds(isc_modnotfound)); + } + } + } ExecProcedureNode* node = FB_NEW_POOL(pool) ExecProcedureNode(pool); node->procedure = procedure; @@ -2990,7 +3018,7 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } if (!dsqlScratch->isPsql()) - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE); ExecProcedureNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName); node->dsqlProcedure = procedure; @@ -3081,7 +3109,7 @@ ValueListNode* ExecProcedureNode::explodeOutputs(DsqlCompilerScratch* dsqlScratc *ptr = paramNode; dsql_par* parameter = paramNode->dsqlParameter = MAKE_parameter( - dsqlScratch->getStatement()->getReceiveMsg(), true, true, 0, NULL); + dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, NULL); paramNode->dsqlParameterIndex = parameter->par_index; DsqlDescMaker::fromField(¶meter->par_desc, field); @@ -3112,9 +3140,9 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) { const dsql_msg* message = NULL; - if (dsqlScratch->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_PROCEDURE) + if (dsqlScratch->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE) { - if ((message = dsqlScratch->getStatement()->getReceiveMsg())) + if ((message = dsqlScratch->getDsqlStatement()->getReceiveMsg())) { dsqlScratch->appendUChar(blr_begin); dsqlScratch->appendUChar(blr_send); @@ -3205,12 +3233,12 @@ ExecProcedureNode* ExecProcedureNode::pass2(thread_db* tdbb, CompilerScratch* cs return this; } -const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { executeProcedure(tdbb, request); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -3218,7 +3246,7 @@ const StmtNode* ExecProcedureNode::execute(thread_db* tdbb, jrd_req* request, Ex // Execute a stored procedure. Begin by assigning the input parameters. // End by assigning the output parameters. -void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) const +void ExecProcedureNode::executeProcedure(thread_db* tdbb, Request* request) const { if (!procedure->isImplemented()) { @@ -3226,6 +3254,12 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons Arg::Gds(isc_proc_pack_not_implemented) << Arg::Str(procedure->getName().identifier) << Arg::Str(procedure->getName().package)); } + else if (!procedure->isDefined()) + { + status_exception::raise( + Arg::Gds(isc_prcnotdef) << Arg::Str(procedure->getName().toString()) << + Arg::Gds(isc_modnotfound)); + } const_cast(procedure.getObject())->checkReload(tdbb); @@ -3275,7 +3309,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons const SavNumber savNumber = transaction->tra_save_point ? transaction->tra_save_point->getNumber() : 0; - jrd_req* procRequest = procedure->getStatement()->findRequest(tdbb); + Request* procRequest = procedure->getStatement()->findRequest(tdbb); // trace procedure execution start TraceProcExecute trace(tdbb, procRequest, request, inputTargets); @@ -3288,7 +3322,7 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons &tdbb->getAttachment()->att_original_timezone, tdbb->getAttachment()->att_current_timezone); - procRequest->req_gmt_timestamp = request->req_gmt_timestamp; + procRequest->setGmtTimeStamp(request->getGmtTimeStamp()); EXE_start(tdbb, procRequest, transaction); @@ -3754,12 +3788,12 @@ ExecStatementNode* ExecStatementNode::pass2(thread_db* tdbb, CompilerScratch* cs return this; } -const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ExecStatementNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { EDS::Statement** stmtPtr = request->getImpure(impureOffset); EDS::Statement* stmt = *stmtPtr; - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { fb_assert(!*stmtPtr); @@ -3798,10 +3832,10 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex else stmt->execute(tdbb, tran, inpNames, inputs, excessInputs, outputs); - request->req_operation = jrd_req::req_return; - } // jrd_req::req_evaluate + request->req_operation = Request::req_return; + } // Request::req_evaluate - if (request->req_operation == jrd_req::req_return || request->req_operation == jrd_req::req_sync) + if (request->req_operation == Request::req_return || request->req_operation == Request::req_sync) { fb_assert(stmt); @@ -3809,15 +3843,15 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex { if (stmt->fetch(tdbb, outputs)) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return innerStmt; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } } - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) { const LabelNode* label = nodeAs(parentStmt.getObject()); @@ -3825,7 +3859,7 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; return this; } } @@ -3836,7 +3870,7 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex return parentStmt; } -void ExecStatementNode::getString(thread_db* tdbb, jrd_req* request, const ValueExprNode* node, +void ExecStatementNode::getString(thread_db* tdbb, Request* request, const ValueExprNode* node, string& str, bool useAttCS) const { MoveBuffer buffer; @@ -3925,23 +3959,23 @@ IfNode* IfNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* IfNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* IfNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (condition->execute(tdbb, request)) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return trueAction; } if (falseAction) { - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return falseAction; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -4010,14 +4044,14 @@ InAutonomousTransactionNode* InAutonomousTransactionNode::pass2(thread_db* tdbb, return this; } -const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); Impure* const impure = request->getImpure(impureOffset); - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { // Force unconditional reschedule. It prevents new transactions being // started after an attachment or a database shutdown has been initiated. @@ -4071,7 +4105,7 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r switch (request->req_operation) { - case jrd_req::req_return: + case Request::req_return: if (!(attachment->att_flags & ATT_no_db_triggers)) { // run ON TRANSACTION COMMIT triggers @@ -4086,13 +4120,13 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r } { // scope - AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } // end scope break; - case jrd_req::req_unwind: + case Request::req_unwind: if (request->req_flags & (req_leave | req_continue_loop)) { try @@ -4110,7 +4144,7 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r transaction->releaseSavepoint(tdbb); } - AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } @@ -4140,7 +4174,7 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r try { - AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_rollback(tdbb, transaction, false, false); @@ -4230,9 +4264,9 @@ InitVariableNode* InitVariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* InitVariableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (varInfo) { @@ -4255,7 +4289,7 @@ const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, Exe } } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -4267,12 +4301,12 @@ const StmtNode* InitVariableNode::execute(thread_db* tdbb, jrd_req* request, Exe ExecBlockNode* ExecBlockNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); if (returns.hasData()) - statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK); + statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); else - statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK); + statement->setType(DsqlStatement::TYPE_EXEC_BLOCK); dsqlScratch->flags |= DsqlCompilerScratch::FLAG_BLOCK; @@ -4404,7 +4438,7 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); dsqlScratch->appendUChar(blr_begin); @@ -4490,6 +4524,8 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->loopLevel = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -4501,9 +4537,9 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) stmtNode->genBlr(dsqlScratch); if (returns.hasData()) - statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK); + statement->setType(DsqlStatement::TYPE_SELECT_BLOCK); else - statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK); + statement->setType(DsqlStatement::TYPE_EXEC_BLOCK); dsqlScratch->appendUChar(blr_end); dsqlScratch->genReturn(true); @@ -4669,7 +4705,7 @@ ExceptionNode* ExceptionNode::pass1(thread_db* tdbb, CompilerScratch* csb) if (exception) { CMP_post_access(tdbb, csb, exception->secName, 0, - SCL_usage, SCL_object_exception, exception->name); + SCL_usage, obj_exceptions, exception->name); } return this; @@ -4682,9 +4718,9 @@ ExceptionNode* ExceptionNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ExceptionNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ExceptionNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (exception) { @@ -4701,7 +4737,7 @@ const StmtNode* ExceptionNode::execute(thread_db* tdbb, jrd_req* request, ExeSta { // PsqlException is undefined and there weren't any exceptions before, // so just do nothing. - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } } @@ -4713,7 +4749,7 @@ void ExceptionNode::setError(thread_db* tdbb) const { SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (!exception) { @@ -5070,7 +5106,7 @@ StmtNode* ForNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ForNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { jrd_tra* transaction = request->req_transaction; ImpureMerge* merge = request->getImpure(impureOffset); @@ -5078,7 +5114,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: // initialize impure values impure->savepoint = 0; impure->writeLockMode = false; @@ -5100,13 +5136,13 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* // fall into - case jrd_req::req_return: + case Request::req_return: if (stall) return stall; // fall into - case jrd_req::req_sync: + case Request::req_sync: { if (hasLineColumn) { @@ -5117,7 +5153,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* const bool fetched = cursor->fetchNext(tdbb); if (withLock) { - const jrd_req* top_request = request->req_snapshot.m_owner; + const Request* top_request = request->req_snapshot.m_owner; if ((top_request) && (top_request->req_flags & req_update_conflict)) impure->writeLockMode = true; } @@ -5127,11 +5163,11 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* if (impure->writeLockMode && withLock) { // Skip statement execution and fetch (and try to lock) next record. - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; return this; } - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement; } } @@ -5139,7 +5175,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* if (impure->writeLockMode) restartRequest(request, transaction); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; if (impure->savepoint) { @@ -5155,7 +5191,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* default: { - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) { if (request->req_flags & (req_leave | req_continue_loop)) { @@ -5167,7 +5203,7 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; return this; } @@ -5202,13 +5238,13 @@ const StmtNode* ForNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /* return NULL; } -bool ForNode::isWriteLockMode(jrd_req* request) const +bool ForNode::isWriteLockMode(Request* request) const { const Impure* impure = request->getImpure(impureOffset); return impure->writeLockMode; } -void ForNode::setWriteLockMode(jrd_req* request) const +void ForNode::setWriteLockMode(Request* request) const { Impure* impure = request->getImpure(impureOffset); fb_assert(!impure->writeLockMode); @@ -5216,7 +5252,7 @@ void ForNode::setWriteLockMode(jrd_req* request) const impure->writeLockMode = true; } -void ForNode::checkRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const +void ForNode::checkRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const { jrd_rel* relation = rpb->rpb_relation; if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->rel_file || relation->rel_view_rse) @@ -5231,7 +5267,7 @@ void ForNode::checkRecordUpdated(thread_db* tdbb, jrd_req* request, record_param Arg::Gds(isc_merge_dup_update).raise(); } -void ForNode::setRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const +void ForNode::setRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const { jrd_rel* relation = rpb->rpb_relation; if (!(marks & MARK_MERGE) || relation->isVirtual() || relation->rel_file || relation->rel_view_rse) @@ -5284,16 +5320,16 @@ HandlerNode* HandlerNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* HandlerNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* HandlerNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: return statement; - case jrd_req::req_unwind: + case Request::req_unwind: if (!request->req_label) - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; default: return parentStmt; @@ -5347,21 +5383,21 @@ LabelNode* LabelNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* LabelNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* LabelNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: return statement; - case jrd_req::req_unwind: + case Request::req_unwind: fb_assert(!(request->req_flags & req_continue_loop)); if (request->req_label == labelNumber && (request->req_flags & (req_leave | req_error_handler))) { request->req_flags &= ~req_leave; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } // fall into @@ -5473,16 +5509,16 @@ LoopNode* LoopNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* LoopNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* LoopNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: - case jrd_req::req_return: - request->req_operation = jrd_req::req_evaluate; + case Request::req_evaluate: + case Request::req_return: + request->req_operation = Request::req_evaluate; return statement; - case jrd_req::req_unwind: + case Request::req_unwind: { const LabelNode* label = nodeAs(parentStmt.getObject()); @@ -5490,7 +5526,7 @@ const StmtNode* LoopNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeStat (request->req_flags & req_continue_loop)) { request->req_flags &= ~req_continue_loop; - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement; } // fall into @@ -5966,9 +6002,9 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (!dsqlScratch->isPsql()) { // Describe it as TYPE_RETURNING_CURSOR if RETURNING is present or as INSERT otherwise. - dsqlScratch->getStatement()->setType(returning ? - DsqlCompiledStatement::TYPE_RETURNING_CURSOR : - DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getDsqlStatement()->setType(returning ? + DsqlStatement::TYPE_RETURNING_CURSOR : + DsqlStatement::TYPE_INSERT); } // Setup the main node. @@ -6370,13 +6406,13 @@ MessageNode* MessageNode::pass2(thread_db* /*tdbb*/, CompilerScratch* csb) return this; } -const StmtNode* MessageNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* MessageNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { USHORT* flags = request->getImpure(impureFlags); memset(flags, 0, sizeof(USHORT) * format->fmt_count); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -6527,8 +6563,8 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up return node; } - dsqlScratch->getStatement()->setType(dsqlCursorName.hasData() ? - DsqlCompiledStatement::TYPE_UPDATE_CURSOR : DsqlCompiledStatement::TYPE_UPDATE); + dsqlScratch->getDsqlStatement()->setType(dsqlCursorName.hasData() ? + DsqlStatement::TYPE_UPDATE_CURSOR : DsqlStatement::TYPE_UPDATE); doDsqlPass(dsqlScratch, node->dsqlRelation, relation, false); dsql_ctx* mod_context = dsqlGetContext(node->dsqlRelation); @@ -6654,7 +6690,7 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) else { dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); } } @@ -6888,15 +6924,15 @@ ModifyNode* ModifyNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* ModifyNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* ModifyNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { impure_state* impure = request->getImpure(impureOffset); const StmtNode* retNode; - if (request->req_operation == jrd_req::req_unwind) + if (request->req_operation == Request::req_unwind) return parentStmt; - if (request->req_operation == jrd_req::req_return && !impure->sta_state && subMod) + if (request->req_operation == Request::req_return && !impure->sta_state && subMod) { if (!exeState->topNode) { @@ -6920,7 +6956,7 @@ const StmtNode* ModifyNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState->whichModTrig = ALL_TRIGS; } else - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; } else { @@ -6935,7 +6971,7 @@ const StmtNode* ModifyNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Execute a MODIFY statement. -const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const { jrd_tra* transaction = request->req_transaction; impure_state* impure = request->getImpure(impureOffset); @@ -6947,24 +6983,24 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (!(marks & MARK_AVOID_COUNTERS)) request->req_records_affected.bumpModified(false); if (impure->sta_state == 0 && forNode && forNode->isWriteLockMode(request)) - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; // fall thru else break; - case jrd_req::req_return: + case Request::req_return: if (impure->sta_state == 1) { impure->sta_state = 0; Record* orgRecord = orgRpb->rpb_record; const Record* newRecord = newRpb->rpb_record; orgRecord->copyDataFrom(newRecord, true); - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement; } @@ -7045,7 +7081,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg if (statement2) { impure->sta_state = 2; - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement2; } } @@ -7066,7 +7102,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg if (orgRpb->rpb_runtime_flags & RPB_just_deleted) { - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; return parentStmt; } @@ -7088,7 +7124,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg if (orgRpb->rpb_runtime_flags & RPB_undo_deleted) { - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; return parentStmt; } @@ -7133,6 +7169,104 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg //-------------------- +static RegisterNode regOuterMapNode({blr_outer_map}); + +DmlNode* OuterMapNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) +{ + fb_assert(csb->mainCsb); + if (!csb->mainCsb) + PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_outer_map. Must be inside subroutine."); + + const auto node = FB_NEW_POOL(pool) OuterMapNode(pool); + + auto& blrReader = csb->csb_blr_reader; + UCHAR subCode; + + while ((subCode = blrReader.getByte()) != blr_end) + { + switch (subCode) + { + case blr_outer_map_message: + { + const USHORT outerNumber = blrReader.getWord(); + const USHORT innerNumber = blrReader.getWord(); + + csb->outerMessagesMap.put(innerNumber, outerNumber); + + const auto outerMessage = CMP_csb_element(csb->mainCsb, outerNumber)->csb_message; + if (!outerMessage) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_message: outer message does not exist"); + } + + const auto tail = CMP_csb_element(csb, innerNumber); + if (tail->csb_message) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_message: inner message already exist"); + } + + tail->csb_message = outerMessage; + + if (innerNumber > csb->csb_msg_number) + csb->csb_msg_number = innerNumber; + + break; + } + + case blr_outer_map_variable: + { + const USHORT outerNumber = blrReader.getWord(); + const USHORT innerNumber = blrReader.getWord(); + + csb->mainCsb->csb_variables_used_in_subroutines.add(outerNumber); + csb->outerVarsMap.put(innerNumber, outerNumber); + + auto& outerVariables = *csb->mainCsb->csb_variables; + if (outerNumber >= outerVariables.count() || !outerVariables[outerNumber]) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_variable: outer variable does not exist"); + } + + auto& innerVariables = *(csb->csb_variables = vec::newVector( + *tdbb->getDefaultPool(), csb->csb_variables, innerNumber + 1)); + + if (innerVariables[innerNumber]) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_variable: inner variable already exist"); + } + + innerVariables[innerNumber] = outerVariables[outerNumber]; + break; + } + + default: + PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_outer_map sub code"); + } + } + + return node; +} + +const StmtNode* OuterMapNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const +{ + if (request->req_operation == Request::req_evaluate) + request->req_operation = Request::req_return; + + return parentStmt; +} + + +//-------------------- + + static RegisterNode regPostEventNode({blr_post, blr_post_arg}); DmlNode* PostEventNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) @@ -7195,9 +7329,9 @@ PostEventNode* PostEventNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* PostEventNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* PostEventNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { jrd_tra* transaction = request->req_transaction; @@ -7212,7 +7346,7 @@ const StmtNode* PostEventNode::execute(thread_db* tdbb, jrd_req* request, ExeSta if (transaction->tra_flags & TRA_autocommit) transaction->tra_flags |= TRA_perform_autocommit; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -7273,23 +7407,23 @@ ReceiveNode* ReceiveNode::pass2(thread_db* tdbb, CompilerScratch* csb) // Execute a RECEIVE statement. This can be entered either with "req_evaluate" (ordinary receive // statement) or "req_proceed" (select statement). // In the latter case, the statement isn't every formalled evaluated. -const StmtNode* ReceiveNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* ReceiveNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_return: + case Request::req_return: if (!(request->req_batch_mode && batchFlag)) break; // fall into - case jrd_req::req_evaluate: - request->req_operation = jrd_req::req_receive; + case Request::req_evaluate: + request->req_operation = Request::req_receive; request->req_message = message; request->req_flags |= req_stall; return this; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_evaluate; + case Request::req_proceed: + request->req_operation = Request::req_evaluate; return statement; default: @@ -7361,7 +7495,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, { DsqlContextStack::AutoRestore autoContext(*dsqlScratch->context); - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_INSERT); const auto node = FB_NEW_POOL(dsqlScratch->getPool()) StoreNode(dsqlScratch->getPool()); node->overrideClause = overrideClause; @@ -7571,7 +7705,7 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) else if (!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT)) { dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); } } @@ -7846,12 +7980,12 @@ StoreNode* StoreNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* StoreNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +const StmtNode* StoreNode::execute(thread_db* tdbb, Request* request, ExeState* exeState) const { impure_state* impure = request->getImpure(impureOffset); const StmtNode* retNode; - if (request->req_operation == jrd_req::req_return && !impure->sta_state && subStore) + if (request->req_operation == Request::req_return && !impure->sta_state && subStore) { if (!exeState->topNode) { @@ -7875,7 +8009,7 @@ const StmtNode* StoreNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState->whichStoTrig = ALL_TRIGS; } else - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; } else { @@ -7890,7 +8024,7 @@ const StmtNode* StoreNode::execute(thread_db* tdbb, jrd_req* request, ExeState* } // Execute a STORE statement. -const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const +const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const { jrd_tra* transaction = request->req_transaction; impure_state* impure = request->getImpure(impureOffset); @@ -7907,7 +8041,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: if (!(marks & MARK_AVOID_COUNTERS)) { if (!nodeIs(parentStmt)) @@ -7921,7 +8055,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger RLCK_reserve_relation(tdbb, transaction, relation, true); break; - case jrd_req::req_return: + case Request::req_return: if (!impure->sta_state) { SavepointChangeMarker scMarker(transaction); @@ -7979,7 +8113,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger if (statement2) { impure->sta_state = 1; - request->req_operation = jrd_req::req_evaluate; + request->req_operation = Request::req_evaluate; return statement2; } } @@ -8064,8 +8198,8 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (dsqlForUpdate) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SELECT_UPD); - dsqlScratch->getStatement()->addFlags(DsqlCompiledStatement::FLAG_NO_BATCH); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_SELECT_UPD); + dsqlScratch->getDsqlStatement()->addFlags(DsqlStatement::FLAG_NO_BATCH); } else { @@ -8077,8 +8211,8 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (rseNode->dsqlOrder || rseNode->dsqlDistinct) { - dsqlScratch->getStatement()->setFlags( - dsqlScratch->getStatement()->getFlags() & ~DsqlCompiledStatement::FLAG_NO_BATCH); + dsqlScratch->getDsqlStatement()->setFlags( + dsqlScratch->getDsqlStatement()->getFlags() & ~DsqlStatement::FLAG_NO_BATCH); } } @@ -8104,7 +8238,7 @@ void SelectNode::genBlr(DsqlCompilerScratch* dsqlScratch) RseNode* const rse = nodeAs(dsqlRse); fb_assert(rse); - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); // Set up parameter for things in the select list. ValueListNode* list = rse->dsqlSelectList; @@ -8265,13 +8399,13 @@ SelectNode* SelectNode::pass2(thread_db* tdbb, CompilerScratch* csb) // EXE_send will then loop thru the sub-statements of select looking for the appropriate RECEIVE // statement. When (or if) it finds it, it will set it up the next statement to be executed. // The RECEIVE, then, will be entered with the operation "req_proceed". -const StmtNode* SelectNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SelectNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: request->req_message = this; - request->req_operation = jrd_req::req_receive; + request->req_operation = Request::req_receive; request->req_flags |= req_stall; return this; @@ -8321,7 +8455,7 @@ SetGeneratorNode* SetGeneratorNode::pass1(thread_db* tdbb, CompilerScratch* csb) doPass1(tdbb, csb, value.getAddress()); CMP_post_access(tdbb, csb, generator.secName, 0, - SCL_usage, SCL_object_generator, generator.name); + SCL_usage, obj_generators, generator.name); return this; } @@ -8332,9 +8466,9 @@ SetGeneratorNode* SetGeneratorNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { jrd_tra* const transaction = request->req_transaction; @@ -8347,7 +8481,7 @@ const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, jrd_req* request, Exe DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_SEQUENCE, generator.name, NULL, *request->getStatement()->sqlText); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -8393,19 +8527,19 @@ StallNode* StallNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) // Execute a stall statement. This is like a blr_receive, except that there is no need for a // gds__send () from the user (i.e. EXE_send () in the engine). // A gds__receive () will unblock the user. -const StmtNode* StallNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* StallNode::execute(thread_db* /*tdbb*/, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: - case jrd_req::req_return: + case Request::req_evaluate: + case Request::req_return: request->req_message = this; - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; request->req_flags |= req_stall; return this; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_return; + case Request::req_proceed: + request->req_operation = Request::req_return; return parentStmt; default: @@ -8432,7 +8566,7 @@ DmlNode* SuspendNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + DsqlStatement* const statement = dsqlScratch->getDsqlStatement(); if (dsqlScratch->flags & (DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)) { @@ -8454,7 +8588,7 @@ SuspendNode* SuspendNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) Arg::Gds(isc_dsql_unsupported_in_auto_trans) << Arg::Str("SUSPEND")); } - statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE); + statement->addFlags(DsqlStatement::FLAG_SELECTABLE); return this; } @@ -8487,11 +8621,11 @@ SuspendNode* SuspendNode::pass2(thread_db* tdbb, CompilerScratch* csb) } // Execute a SEND statement. -const StmtNode* SuspendNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SuspendNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: { // ASF: If this is the send in the tail of a procedure and the procedure was called // with a SELECT, don't run all the send statements. It may make validations fail when @@ -8523,14 +8657,14 @@ const StmtNode* SuspendNode::execute(thread_db* tdbb, jrd_req* request, ExeState // fall into } - case jrd_req::req_return: - request->req_operation = jrd_req::req_send; + case Request::req_return: + request->req_operation = Request::req_send; request->req_message = message; request->req_flags |= req_stall; return this; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_return; + case Request::req_proceed: + request->req_operation = Request::req_return; return parentStmt; default: @@ -8651,11 +8785,11 @@ SavepointEncloseNode* SavepointEncloseNode::pass2(thread_db* tdbb, CompilerScrat return this; } -const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { const auto transaction = request->req_transaction; - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { if (!(transaction->tra_flags & TRA_system)) { @@ -8666,7 +8800,7 @@ const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, return statement; } - if (request->req_operation == jrd_req::req_return) + if (request->req_operation == Request::req_return) { if (!(transaction->tra_flags & TRA_system)) { @@ -8690,7 +8824,7 @@ const StmtNode* SavepointEncloseNode::execute(thread_db* tdbb, jrd_req* request, SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_START_TRANS); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_START_TRANS); // Generate tpb for set transaction. Use blr string of dsqlScratch. // If a value is not specified, default is not stuffed, let the engine handle it. @@ -8769,7 +8903,7 @@ SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratc return this; } -void SetTransactionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void SetTransactionNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const { JRD_start_transaction(tdbb, &request->req_transaction, request->req_dbb->dbb_attachment, tpb.getCount(), tpb.begin()); @@ -8806,7 +8940,7 @@ void SetTransactionNode::genTableLock(DsqlCompilerScratch* dsqlScratch, //-------------------- -void SessionResetNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const +void SessionResetNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8817,7 +8951,7 @@ void SessionResetNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** tra //-------------------- -void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetRoleNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8848,7 +8982,7 @@ SetDebugOptionNode::SetDebugOptionNode(MemoryPool& pool, MetaName* aName, ExprNo { } -void SetDebugOptionNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDebugOptionNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); auto& debugOptions = tdbb->getAttachment()->getDebugOptions(); @@ -8884,7 +9018,7 @@ SetDecFloatRoundNode::SetDecFloatRoundNode(MemoryPool& pool, MetaName* name) rndMode = mode->val; } -void SetDecFloatRoundNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDecFloatRoundNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8904,7 +9038,7 @@ void SetDecFloatTrapsNode::trap(MetaName* name) traps |= trap->val; } -void SetDecFloatTrapsNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetDecFloatTrapsNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -8927,7 +9061,7 @@ SessionManagementNode* SetBindNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } -void SetBindNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetBindNode::execute(thread_db* tdbb, DsqlRequest* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); @@ -8991,7 +9125,7 @@ string SetSessionNode::internalPrint(NodePrinter& printer) const return "SetSessionNode"; } -void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetSessionNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { Attachment* att = tdbb->getAttachment(); @@ -9011,7 +9145,7 @@ void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*tra //-------------------- -void SetTimeZoneNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const +void SetTimeZoneNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*traHandle*/) const { Attachment* const attachment = tdbb->getAttachment(); @@ -9060,9 +9194,9 @@ TruncateLocalTableNode* TruncateLocalTableNode::copy(thread_db* tdbb, NodeCopier return node; } -const StmtNode* TruncateLocalTableNode::execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const +const StmtNode* TruncateLocalTableNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { const auto localTable = request->getStatement()->localTables[tableNumber]; @@ -9072,7 +9206,7 @@ const StmtNode* TruncateLocalTableNode::execute(thread_db* tdbb, jrd_req* reques recordBuffer = nullptr; } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -9263,9 +9397,9 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) node->modifyNode = nodeAs(node->modifyNode->internalDsqlPass(dsqlScratch, true)); fb_assert(node->modifyNode); - // If RETURNING is present, type is already DsqlCompiledStatement::TYPE_EXEC_PROCEDURE. + // If RETURNING is present, type is already DsqlStatement::TYPE_EXEC_PROCEDURE. if (!returning) - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_INSERT); return SavepointEncloseNode::make(dsqlScratch->getPool(), dsqlScratch, node); } @@ -9338,20 +9472,20 @@ CommitRollbackNode* CommitRollbackNode::dsqlPass(DsqlCompilerScratch* dsqlScratc switch (command) { case CMD_COMMIT: - dsqlScratch->getStatement()->setType(retain ? - DsqlCompiledStatement::TYPE_COMMIT_RETAIN : DsqlCompiledStatement::TYPE_COMMIT); + dsqlScratch->getDsqlStatement()->setType(retain ? + DsqlStatement::TYPE_COMMIT_RETAIN : DsqlStatement::TYPE_COMMIT); break; case CMD_ROLLBACK: - dsqlScratch->getStatement()->setType(retain ? - DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN : DsqlCompiledStatement::TYPE_ROLLBACK); + dsqlScratch->getDsqlStatement()->setType(retain ? + DsqlStatement::TYPE_ROLLBACK_RETAIN : DsqlStatement::TYPE_ROLLBACK); break; } return this; } -void CommitRollbackNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void CommitRollbackNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const { if (retain) { @@ -9399,11 +9533,11 @@ Firebird::string UserSavepointNode::internalPrint(NodePrinter& printer) const UserSavepointNode* UserSavepointNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SAVEPOINT); + dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_SAVEPOINT); return this; } -void UserSavepointNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*transaction*/) const +void UserSavepointNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** /*transaction*/) const { jrd_tra* const transaction = request->req_transaction; fb_assert(!(transaction->tra_flags & TRA_system)); @@ -9510,12 +9644,11 @@ static void dsqlExplodeFields(dsql_rel* relation, Array >& fields, } // Find dbkey for named relation in statement's saved dbkeys. -static dsql_par* dsqlFindDbKey(const dsql_req* request, const RelationSourceNode* relation_name) +static dsql_par* dsqlFindDbKey(const DsqlDmlStatement* statement, const RelationSourceNode* relation_name) { - DEV_BLKCHK(request, dsql_type_req); DEV_BLKCHK(relation_name, dsql_type_nod); - const dsql_msg* message = request->getStatement()->getReceiveMsg(); + const dsql_msg* message = statement->getReceiveMsg(); dsql_par* candidate = NULL; const MetaName& relName = relation_name->dsqlName; @@ -9536,11 +9669,9 @@ static dsql_par* dsqlFindDbKey(const dsql_req* request, const RelationSourceNode } // Find record version for relation in statement's saved record version. -static dsql_par* dsqlFindRecordVersion(const dsql_req* request, const RelationSourceNode* relation_name) +static dsql_par* dsqlFindRecordVersion(const DsqlDmlStatement* statement, const RelationSourceNode* relation_name) { - DEV_BLKCHK(request, dsql_type_req); - - const dsql_msg* message = request->getStatement()->getReceiveMsg(); + const dsql_msg* message = statement->getReceiveMsg(); dsql_par* candidate = NULL; const MetaName& relName = relation_name->dsqlName; @@ -9569,7 +9700,7 @@ static void dsqlGenEofAssignment(DsqlCompilerScratch* dsqlScratch, SSHORT value) dsqlScratch->appendUChar(blr_assignment); LiteralNode::genConstant(dsqlScratch, &valueDesc, false); - GEN_parameter(dsqlScratch, dsqlScratch->getStatement()->getEof()); + GEN_parameter(dsqlScratch, dsqlScratch->getDsqlStatement()->getEof()); } static void dsqlGenReturning(DsqlCompilerScratch* dsqlScratch, ReturningClause* returning, @@ -9637,7 +9768,7 @@ static void dsqlGenReturningLocalTableCursor(DsqlCompilerScratch* dsqlScratch, R dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); dsqlScratch->appendUChar(blr_begin); @@ -9655,7 +9786,7 @@ static void dsqlGenReturningLocalTableCursor(DsqlCompilerScratch* dsqlScratch, R dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_send); - dsqlScratch->appendUChar(dsqlScratch->getStatement()->getReceiveMsg()->msg_number); + dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number); dsqlGenEofAssignment(dsqlScratch, 0); } @@ -9893,7 +10024,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const // Lookup parent dsqlScratch - dsql_req* const* const symbol = dsqlScratch->getAttachment()->dbb_cursors.get(cursor.c_str()); + const auto* const symbol = dsqlScratch->getAttachment()->dbb_cursors.get(cursor.c_str()); if (!symbol) { @@ -9903,12 +10034,12 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_not_found) << cursor); } - dsql_req* parent = *symbol; + auto parent = *symbol; // Verify that the cursor is appropriate and updatable - dsql_par* source = dsqlFindDbKey(parent, relation_name); - dsql_par* rv_source = dsqlFindRecordVersion(parent, relation_name); + dsql_par* source = dsqlFindDbKey(parent->getDsqlStatement(), relation_name); + dsql_par* rv_source = dsqlFindRecordVersion(parent->getDsqlStatement(), relation_name); if (!source || !rv_source) { @@ -9917,7 +10048,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const Arg::Gds(isc_dsql_cursor_update_err) << cursor); } - DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); + const auto statement = static_cast(dsqlScratch->getDsqlStatement()); statement->setParentRequest(parent); statement->setParentDbKey(source); @@ -10164,7 +10295,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d for (auto& src : node->first->items) { - auto parameter = MAKE_parameter(dsqlScratch->getStatement()->getReceiveMsg(), + auto parameter = MAKE_parameter(dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, src); parameter->par_node = src; DsqlDescMaker::fromNode(dsqlScratch, ¶meter->par_desc, src, true); @@ -10181,8 +10312,8 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d if (!singleton) { // Set up parameter to handle EOF - auto parameter = MAKE_parameter(dsqlScratch->getStatement()->getReceiveMsg(), false, false, 0, nullptr); - dsqlScratch->getStatement()->setEof(parameter); + auto parameter = MAKE_parameter(dsqlScratch->getDsqlStatement()->getReceiveMsg(), false, false, 0, nullptr); + dsqlScratch->getDsqlStatement()->setEof(parameter); parameter->par_desc.dsc_dtype = dtype_short; parameter->par_desc.dsc_scale = 0; parameter->par_desc.dsc_length = sizeof(SSHORT); @@ -10192,8 +10323,8 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d if (!dsqlScratch->isPsql()) { - dsqlScratch->getStatement()->setType(singleton ? - DsqlCompiledStatement::TYPE_EXEC_PROCEDURE : DsqlCompiledStatement::TYPE_RETURNING_CURSOR); + dsqlScratch->getDsqlStatement()->setType(singleton ? + DsqlStatement::TYPE_EXEC_PROCEDURE : DsqlStatement::TYPE_RETURNING_CURSOR); } return node; @@ -10528,7 +10659,7 @@ static RelationSourceNode* pass1Update(thread_db* tdbb, CompilerScratch* csb, jr // unless this is an internal request, check access permission CMP_post_access(tdbb, csb, relation->rel_security_name, (view ? view->rel_id : 0), - priv, SCL_object_table, relation->rel_name); + priv, obj_relations, relation->rel_name); // ensure that the view is set for the input streams, // so that access to views can be checked at the field level @@ -10779,9 +10910,9 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, } } -static void restartRequest(const jrd_req* request, jrd_tra* transaction) +static void restartRequest(const Request* request, jrd_tra* transaction) { - const jrd_req* top_request = request->req_snapshot.m_owner; + const Request* top_request = request->req_snapshot.m_owner; fb_assert(top_request); fb_assert(top_request->req_flags & req_update_conflict); @@ -10801,7 +10932,7 @@ static void validateExpressions(thread_db* tdbb, const Array& vali for (Array::const_iterator i = validations.begin(); i != end; ++i) { - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (!i->boolean->execute(tdbb, request) && !(request->req_flags & req_null)) { diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index a3bca654ea..5886d3fd63 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -119,7 +119,7 @@ enum OverrideClause : UCHAR }; -class AssignmentNode : public TypedNode +class AssignmentNode final : public TypedNode { public: explicit AssignmentNode(MemoryPool& pool) @@ -143,7 +143,7 @@ public: virtual AssignmentNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AssignmentNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual AssignmentNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst asgnFrom; @@ -153,7 +153,7 @@ public: }; -class BlockNode : public TypedNode +class BlockNode final : public TypedNode { public: explicit BlockNode(MemoryPool& pool) @@ -171,10 +171,10 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual BlockNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual BlockNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: - static bool testAndFixupError(thread_db* tdbb, jrd_req* request, const ExceptionArray& conditions); + static bool testAndFixupError(thread_db* tdbb, Request* request, const ExceptionArray& conditions); public: NestConst action; @@ -201,7 +201,7 @@ public: virtual CompoundStmtNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual CompoundStmtNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual CompoundStmtNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: Firebird::Array > statements; @@ -209,7 +209,7 @@ public: }; -class ContinueLeaveNode : public TypedNode +class ContinueLeaveNode final : public TypedNode { public: explicit ContinueLeaveNode(MemoryPool& pool, UCHAR aBlrOp) @@ -238,7 +238,7 @@ public: return this; } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: UCHAR blrOp; @@ -247,7 +247,7 @@ public: }; -class CursorStmtNode : public TypedNode +class CursorStmtNode final : public TypedNode { public: explicit CursorStmtNode(MemoryPool& pool, UCHAR aCursorOp, const MetaName& aDsqlName = "", @@ -271,7 +271,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual CursorStmtNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual CursorStmtNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: MetaName dsqlName; @@ -284,7 +284,7 @@ public: }; -class DeclareCursorNode : public TypedNode +class DeclareCursorNode final : public TypedNode { public: static const USHORT CUR_TYPE_NONE = 0; @@ -314,7 +314,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual DeclareCursorNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareCursorNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: MetaName dsqlName; @@ -328,7 +328,7 @@ public: }; -class DeclareLocalTableNode : public TypedNode +class DeclareLocalTableNode final : public TypedNode { public: struct Impure @@ -356,10 +356,10 @@ public: } DeclareLocalTableNode* pass2(thread_db* tdbb, CompilerScratch* csb) override; - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; public: - Impure* getImpure(thread_db* tdbb, jrd_req* request, bool createWhenDead = true) const; + Impure* getImpure(thread_db* tdbb, Request* request, bool createWhenDead = true) const; public: NestConst format; @@ -367,7 +367,7 @@ public: }; -class DeclareSubFuncNode : public TypedNode +class DeclareSubFuncNode final : public TypedNode { public: explicit DeclareSubFuncNode(MemoryPool& pool, const MetaName& aName) @@ -396,7 +396,7 @@ public: virtual DeclareSubFuncNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareSubFuncNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void parseParameters(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, @@ -421,7 +421,7 @@ public: }; -class DeclareSubProcNode : public TypedNode +class DeclareSubProcNode final : public TypedNode { public: explicit DeclareSubProcNode(MemoryPool& pool, const MetaName& aName) @@ -449,7 +449,7 @@ public: virtual DeclareSubProcNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareSubProcNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void parseParameters(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, @@ -473,13 +473,11 @@ public: }; -class DeclareVariableNode : public TypedNode +class DeclareVariableNode final : public TypedNode { public: explicit DeclareVariableNode(MemoryPool& pool) - : TypedNode(pool), - dsqlDef(NULL), - varId(0) + : TypedNode(pool) { varDesc.clear(); } @@ -493,16 +491,17 @@ public: virtual DeclareVariableNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual DeclareVariableNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual DeclareVariableNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst dsqlDef; dsc varDesc; - USHORT varId; + USHORT varId = 0; + bool usedInSubRoutines = false; }; -class EraseNode : public TypedNode +class EraseNode final : public TypedNode { public: explicit EraseNode(MemoryPool& pool) @@ -531,11 +530,11 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual EraseNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual EraseNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* node); - const StmtNode* erase(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; + const StmtNode* erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const; public: NestConst dsqlRelation; @@ -555,7 +554,7 @@ public: }; -class ErrorHandlerNode : public TypedNode +class ErrorHandlerNode final : public TypedNode { public: explicit ErrorHandlerNode(MemoryPool& pool) @@ -573,7 +572,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ErrorHandlerNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ErrorHandlerNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst action; @@ -581,7 +580,7 @@ public: }; -class ExecProcedureNode : public TypedNode +class ExecProcedureNode final : public TypedNode { public: explicit ExecProcedureNode(MemoryPool& pool, @@ -608,11 +607,11 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ExecProcedureNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExecProcedureNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: ValueListNode* explodeOutputs(DsqlCompilerScratch* dsqlScratch, const dsql_prc* procedure); - void executeProcedure(thread_db* tdbb, jrd_req* request) const; + void executeProcedure(thread_db* tdbb, Request* request) const; public: QualifiedName dsqlName; @@ -627,7 +626,7 @@ public: }; -class ExecStatementNode : public TypedNode +class ExecStatementNode final : public TypedNode { public: explicit ExecStatementNode(MemoryPool& pool) @@ -657,12 +656,12 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ExecStatementNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExecStatementNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void genOptionalExpr(DsqlCompilerScratch* dsqlScratch, const UCHAR code, ValueExprNode* node); - void getString(thread_db* tdbb, jrd_req* request, const ValueExprNode* node, + void getString(thread_db* tdbb, Request* request, const ValueExprNode* node, Firebird::string& str, bool useAttCS = false) const; public: @@ -683,7 +682,7 @@ public: }; -class IfNode : public TypedNode +class IfNode final : public TypedNode { public: explicit IfNode(MemoryPool& pool) @@ -702,7 +701,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual IfNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual IfNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst condition; @@ -711,7 +710,7 @@ public: }; -class InAutonomousTransactionNode : public TypedNode +class InAutonomousTransactionNode final : public TypedNode { struct Impure { @@ -734,14 +733,14 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual InAutonomousTransactionNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual InAutonomousTransactionNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst action; }; -class InitVariableNode : public TypedNode +class InitVariableNode final : public TypedNode { public: explicit InitVariableNode(MemoryPool& pool) @@ -761,7 +760,7 @@ public: virtual InitVariableNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual InitVariableNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual InitVariableNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: USHORT varId; @@ -770,7 +769,7 @@ public: }; -class ExecBlockNode : public TypedNode +class ExecBlockNode final : public TypedNode { public: explicit ExecBlockNode(MemoryPool& pool) @@ -797,7 +796,7 @@ public: }; -class ExceptionNode : public TypedNode +class ExceptionNode final : public TypedNode { public: ExceptionNode(MemoryPool& pool, const MetaName& name, @@ -827,7 +826,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ExceptionNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ExceptionNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: void setError(thread_db* tdbb) const; @@ -839,7 +838,7 @@ public: }; -class ExitNode : public TypedNode +class ExitNode final : public TypedNode { public: explicit ExitNode(MemoryPool& pool) @@ -853,7 +852,7 @@ public: }; -class ForNode : public TypedNode +class ForNode final : public TypedNode { public: explicit ForNode(MemoryPool& pool) @@ -882,14 +881,14 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual StmtNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual StmtNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; - bool isWriteLockMode(jrd_req* request) const; - void setWriteLockMode(jrd_req* request) const; + bool isWriteLockMode(Request* request) const; + void setWriteLockMode(Request* request) const; // Used by UPDATE and DELETE sub-statements of MERGE - void checkRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const; - void setRecordUpdated(thread_db* tdbb, jrd_req* request, record_param* rpb) const; + void checkRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const; + void setRecordUpdated(thread_db* tdbb, Request* request, record_param* rpb) const; public: struct Impure @@ -919,7 +918,7 @@ public: }; -class HandlerNode : public TypedNode +class HandlerNode final : public TypedNode { public: explicit HandlerNode(MemoryPool& pool) @@ -936,14 +935,14 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual HandlerNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual HandlerNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; }; -class LabelNode : public TypedNode +class LabelNode final : public TypedNode { public: explicit LabelNode(MemoryPool& pool) @@ -961,7 +960,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual LabelNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual LabelNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; @@ -969,7 +968,7 @@ public: }; -class LineColumnNode : public TypedNode +class LineColumnNode final : public TypedNode { public: explicit LineColumnNode(MemoryPool& pool, ULONG aLine, ULONG aColumn, StmtNode* aStatement) @@ -990,7 +989,7 @@ private: }; -class LoopNode : public TypedNode +class LoopNode final : public TypedNode { public: explicit LoopNode(MemoryPool& pool) @@ -1010,7 +1009,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual LoopNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual LoopNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: MetaName* dsqlLabelName; @@ -1020,7 +1019,7 @@ public: }; -class MergeNode : public TypedNode +class MergeNode final : public TypedNode { public: struct Matched @@ -1093,9 +1092,7 @@ class MessageNode : public TypedNode public: explicit MessageNode(MemoryPool& pool) : TypedNode(pool), - format(NULL), - impureFlags(0), - messageNumber(0) + itemsUsedInSubroutines(pool) { } @@ -1113,16 +1110,17 @@ public: virtual MessageNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual MessageNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual MessageNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: + Firebird::SortedArray itemsUsedInSubroutines; NestConst format; - ULONG impureFlags; - USHORT messageNumber; + ULONG impureFlags = 0; + USHORT messageNumber = 0; }; -class ModifyNode : public TypedNode +class ModifyNode final : public TypedNode { public: explicit ModifyNode(MemoryPool& pool) @@ -1141,11 +1139,11 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ModifyNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ModifyNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static void pass1Modify(thread_db* tdbb, CompilerScratch* csb, ModifyNode* node); - const StmtNode* modify(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; + const StmtNode* modify(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const; public: NestConst dsqlRelation; @@ -1171,7 +1169,41 @@ public: }; -class PostEventNode : public TypedNode +class OuterMapNode final : public TypedNode +{ +public: + explicit OuterMapNode(MemoryPool& pool) + : TypedNode(pool) + { + } + +public: + static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + + Firebird::string internalPrint(NodePrinter& /*printer*/) const override + { + return "OuterMapNode"; + } + + void genBlr(DsqlCompilerScratch* /*dsqlScratch*/) override + { + } + + OuterMapNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override + { + return this; + } + + OuterMapNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override + { + return this; + } + + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; +}; + + +class PostEventNode final : public TypedNode { public: explicit PostEventNode(MemoryPool& pool) @@ -1189,7 +1221,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual PostEventNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual PostEventNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst event; @@ -1197,7 +1229,7 @@ public: }; -class ReceiveNode : public TypedNode +class ReceiveNode final : public TypedNode { public: explicit ReceiveNode(MemoryPool& pool) @@ -1216,7 +1248,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual ReceiveNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ReceiveNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; @@ -1225,7 +1257,7 @@ public: }; -class StoreNode : public TypedNode +class StoreNode final : public TypedNode { public: explicit StoreNode(MemoryPool& pool) @@ -1245,12 +1277,12 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual StoreNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual StoreNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; private: static bool pass1Store(thread_db* tdbb, CompilerScratch* csb, StoreNode* node); void makeDefaults(thread_db* tdbb, CompilerScratch* csb); - const StmtNode* store(thread_db* tdbb, jrd_req* request, WhichTrigger whichTrig) const; + const StmtNode* store(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const; public: NestConst target; @@ -1268,7 +1300,7 @@ public: }; -class SelectNode : public TypedNode +class SelectNode final : public TypedNode { public: explicit SelectNode(MemoryPool& pool) @@ -1289,7 +1321,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual SelectNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SelectNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst dsqlExpr; @@ -1301,7 +1333,7 @@ public: // This is only for GPRE's cmp_set_generator(). -class SetGeneratorNode : public TypedNode +class SetGeneratorNode final : public TypedNode { public: SetGeneratorNode(MemoryPool& pool, const MetaName& name, ValueExprNode* aValue = NULL) @@ -1323,7 +1355,7 @@ public: virtual SetGeneratorNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SetGeneratorNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: GeneratorItem generator; @@ -1331,7 +1363,7 @@ public: }; -class StallNode : public TypedNode +class StallNode final : public TypedNode { public: explicit StallNode(MemoryPool& pool) @@ -1347,7 +1379,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual StallNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual StallNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; }; @@ -1369,7 +1401,7 @@ public: virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual SuspendNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SuspendNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst message; @@ -1377,7 +1409,7 @@ public: }; -class ReturnNode : public TypedNode +class ReturnNode final : public TypedNode { public: explicit ReturnNode(MemoryPool& pool, ValueExprNode* val = NULL) @@ -1395,7 +1427,7 @@ public: }; -class SavepointEncloseNode : public TypedNode +class SavepointEncloseNode final : public TypedNode { public: explicit SavepointEncloseNode(MemoryPool& pool, StmtNode* stmt) @@ -1416,14 +1448,14 @@ public: virtual SavepointEncloseNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual SavepointEncloseNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; public: NestConst statement; }; -class SessionManagementWrapperNode : public TypedNode +class SessionManagementWrapperNode final : public TypedNode { public: explicit SessionManagementWrapperNode(MemoryPool& pool, SessionManagementNode* aWrapped, @@ -1441,9 +1473,9 @@ public: // Save and reset the statement type, as SessionManagementNode sets it to TYPE_SESSION_MANAGEMENT but // we are a DML statement. - DsqlCompiledStatement::Type statementType = dsqlScratch->getStatement()->getType(); + DsqlStatement::Type statementType = dsqlScratch->getDsqlStatement()->getType(); wrapped->dsqlPass(dsqlScratch); - dsqlScratch->getStatement()->setType(statementType); + dsqlScratch->getDsqlStatement()->setType(statementType); return this; } @@ -1534,7 +1566,7 @@ public: } virtual SetTransactionNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; private: void genTableLock(DsqlCompilerScratch* dsqlScratch, const RestrictionOption& tblLock, @@ -1575,7 +1607,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual CommitRollbackNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; private: const Command command; @@ -1605,7 +1637,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual UserSavepointNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** transaction) const; public: const Command command; @@ -1629,7 +1661,7 @@ public: return "SessionResetNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; }; @@ -1661,7 +1693,7 @@ public: return "SetRoleNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: bool trusted; @@ -1682,7 +1714,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const; - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; private: Type m_type; @@ -1706,7 +1738,7 @@ public: return "SetDebugOptionNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; private: MetaName name; @@ -1729,7 +1761,7 @@ public: return "SetDecFloatRoundNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: USHORT rndMode; @@ -1755,7 +1787,7 @@ public: return "SetDecFloatTrapsNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; void trap(MetaName* name); @@ -1786,7 +1818,7 @@ public: } virtual SessionManagementNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: dsql_fld* from; @@ -1822,7 +1854,7 @@ public: return "SetTimeZoneNode"; } - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; + virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const; public: Firebird::string str; @@ -1830,7 +1862,7 @@ public: }; -class TruncateLocalTableNode : public TypedNode +class TruncateLocalTableNode final : public TypedNode { public: explicit TruncateLocalTableNode(MemoryPool& pool) @@ -1861,14 +1893,14 @@ public: return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override; public: USHORT tableNumber = 0; }; -class UpdateOrInsertNode : public TypedNode +class UpdateOrInsertNode final : public TypedNode { public: explicit UpdateOrInsertNode(MemoryPool& pool) diff --git a/src/dsql/WinNodes.cpp b/src/dsql/WinNodes.cpp index 5caaca0190..296ed000cd 100644 --- a/src/dsql/WinNodes.cpp +++ b/src/dsql/WinNodes.cpp @@ -76,7 +76,7 @@ ValueExprNode* DenseRankWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c return FB_NEW_POOL(*tdbb->getDefaultPool()) DenseRankWinNode(*tdbb->getDefaultPool()); } -void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void DenseRankWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -84,11 +84,11 @@ void DenseRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0, 0); } -void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* /*request*/, dsc* /*desc*/) const +void DenseRankWinNode::aggPass(thread_db* /*tdbb*/, Request* /*request*/, dsc* /*desc*/) const { } -dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* DenseRankWinNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlu_misc.vlu_int64; @@ -146,7 +146,7 @@ AggNode* RankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RankWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -155,13 +155,13 @@ void RankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->vlux_count = 0; } -void RankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const +void RankWinNode::aggPass(thread_db* /*tdbb*/, Request* request, dsc* /*desc*/) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; } -dsc* RankWinNode::aggExecute(thread_db* tdbb, jrd_req* request) const +dsc* RankWinNode::aggExecute(thread_db* tdbb, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -222,7 +222,7 @@ AggNode* PercentRankWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void PercentRankWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -235,13 +235,13 @@ void PercentRankWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impureTemp->vlux_count = 0; } -void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, jrd_req* request, dsc* /*desc*/) const +void PercentRankWinNode::aggPass(thread_db* /*tdbb*/, Request* request, dsc* /*desc*/) const { impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; } -dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const +dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, Request* request) const { impure_value_ex* impure = request->getImpure(impureOffset); impure_value_ex* impureTemp = request->getImpure(tempImpure); @@ -254,7 +254,7 @@ dsc* PercentRankWinNode::aggExecute(thread_db* /*tdbb*/, jrd_req* request) const return NULL; } -dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* PercentRankWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impureTemp = request->getImpure(tempImpure); @@ -307,7 +307,7 @@ AggNode* CumeDistWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void CumeDistWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -315,7 +315,7 @@ void CumeDistWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_double(0); } -dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* CumeDistWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impure = request->getImpure(impureOffset); @@ -366,7 +366,7 @@ ValueExprNode* RowNumberWinNode::copy(thread_db* tdbb, NodeCopier& /*copier*/) c return FB_NEW_POOL(*tdbb->getDefaultPool()) RowNumberWinNode(*tdbb->getDefaultPool()); } -void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void RowNumberWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -374,7 +374,7 @@ void RowNumberWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0, 0); } -dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* RowNumberWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impure = request->getImpure(impureOffset); impure->vlu_misc.vlu_int64 = window->getRecordPosition() - window->getPartitionStart() + 1; @@ -425,12 +425,12 @@ ValueExprNode* FirstValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) cons return node; } -void FirstValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void FirstValueWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); } -dsc* FirstValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* FirstValueWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { if (!window->moveWithinFrame(-(window->getRecordPosition() - window->getFrameStart()))) return NULL; @@ -486,12 +486,12 @@ ValueExprNode* LastValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } -void LastValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void LastValueWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); } -dsc* LastValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* LastValueWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { if (!window->moveWithinFrame(window->getFrameEnd() - window->getRecordPosition())) return NULL; @@ -558,7 +558,7 @@ ValueExprNode* NthValueWinNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } -void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void NthValueWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -566,7 +566,7 @@ void NthValueWinNode::aggInit(thread_db* tdbb, jrd_req* request) const impure->make_int64(0, 0); } -dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* NthValueWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { dsc* desc = EVL_expr(tdbb, request, row); if (!desc || (request->req_flags & req_null)) @@ -652,12 +652,12 @@ void LagLeadWinNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) arg->getDesc(tdbb, csb, desc); } -void LagLeadWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void LagLeadWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); } -dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const +dsc* LagLeadWinNode::winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const { dsc* desc = EVL_expr(tdbb, request, rows); if (!desc || (request->req_flags & req_null)) @@ -818,7 +818,7 @@ AggNode* NTileWinNode::pass2(thread_db* tdbb, CompilerScratch* csb) return this; } -void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const +void NTileWinNode::aggInit(thread_db* tdbb, Request* request) const { AggNode::aggInit(tdbb, request); @@ -847,7 +847,7 @@ void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const } } -dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, jrd_req* request, SlidingWindow* window) const +dsc* NTileWinNode::winPass(thread_db* /*tdbb*/, Request* request, SlidingWindow* window) const { impure_value_ex* impure = request->getImpure(impureOffset); ThisImpure* thisImpure = request->getImpure(thisImpureOffset); diff --git a/src/dsql/WinNodes.h b/src/dsql/WinNodes.h index 54f584d6f3..68097198e8 100644 --- a/src/dsql/WinNodes.h +++ b/src/dsql/WinNodes.h @@ -31,7 +31,7 @@ namespace Jrd { // DENSE_RANK function. -class DenseRankWinNode : public WinFuncNode +class DenseRankWinNode final : public WinFuncNode { public: explicit DenseRankWinNode(MemoryPool& pool); @@ -51,16 +51,16 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; // RANK function. -class RankWinNode : public WinFuncNode +class RankWinNode final : public WinFuncNode { public: explicit RankWinNode(MemoryPool& pool); @@ -81,9 +81,9 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -93,7 +93,7 @@ private: }; // PERCENT_RANK function. -class PercentRankWinNode : public WinFuncNode +class PercentRankWinNode final : public WinFuncNode { public: explicit PercentRankWinNode(MemoryPool& pool); @@ -114,11 +114,11 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual void aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const; - virtual dsc* aggExecute(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual void aggPass(thread_db* tdbb, Request* request, dsc* desc) const; + virtual dsc* aggExecute(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -128,7 +128,7 @@ private: }; // CUME_DIST function. -class CumeDistWinNode : public WinFuncNode +class CumeDistWinNode final : public WinFuncNode { public: explicit CumeDistWinNode(MemoryPool& pool); @@ -149,16 +149,16 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; // ROW_NUMBER function. -class RowNumberWinNode : public WinFuncNode +class RowNumberWinNode final : public WinFuncNode { public: explicit RowNumberWinNode(MemoryPool& pool); @@ -178,16 +178,16 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; }; // FIRST_VALUE function. -class FirstValueWinNode : public WinFuncNode +class FirstValueWinNode final : public WinFuncNode { public: explicit FirstValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -202,9 +202,9 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -213,7 +213,7 @@ protected: }; // LAST_VALUE function. -class LastValueWinNode : public WinFuncNode +class LastValueWinNode final : public WinFuncNode { public: explicit LastValueWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -228,9 +228,9 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -239,7 +239,7 @@ protected: }; // NTH_VALUE function. -class NthValueWinNode : public WinFuncNode +class NthValueWinNode final : public WinFuncNode { public: enum @@ -269,9 +269,9 @@ public: virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; @@ -306,8 +306,8 @@ public: virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual void parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned count); @@ -319,7 +319,7 @@ protected: }; // LAG function. -class LagWinNode : public LagLeadWinNode +class LagWinNode final : public LagLeadWinNode { public: explicit LagWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, @@ -338,7 +338,7 @@ protected: }; // LEAD function. -class LeadWinNode : public LagLeadWinNode +class LeadWinNode final : public LagLeadWinNode { public: explicit LeadWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL, ValueExprNode* aRows = NULL, @@ -357,7 +357,7 @@ protected: }; // NTILE function. -class NTileWinNode : public WinFuncNode +class NTileWinNode final : public WinFuncNode { public: explicit NTileWinNode(MemoryPool& pool, ValueExprNode* aArg = NULL); @@ -373,9 +373,9 @@ public: virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual AggNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual void aggInit(thread_db* tdbb, jrd_req* request) const; + virtual void aggInit(thread_db* tdbb, Request* request) const; - virtual dsc* winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* window) const; + virtual dsc* winPass(thread_db* tdbb, Request* request, SlidingWindow* window) const; protected: virtual AggNode* dsqlCopy(DsqlCompilerScratch* dsqlScratch) /*const*/; diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index b7bea575a8..0d60b0aca8 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -57,12 +57,13 @@ #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../yvalve/gds_proto.h" +#include "../jrd/exe_proto.h" #include "../jrd/inf_proto.h" #include "../jrd/ini_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/tra_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/replication/Publisher.h" #include "../jrd/trace/TraceManager.h" @@ -71,6 +72,7 @@ #include "../common/utils_proto.h" #include "../common/StatusArg.h" #include "../dsql/DsqlBatch.h" +#include "../dsql/DsqlStatementCache.h" #ifdef HAVE_CTYPE_H #include @@ -80,30 +82,15 @@ using namespace Jrd; using namespace Firebird; -static ULONG get_request_info(thread_db*, dsql_req*, ULONG, UCHAR*); +static ULONG get_request_info(thread_db*, DsqlRequest*, ULONG, UCHAR*); static dsql_dbb* init(Jrd::thread_db*, Jrd::Attachment*); -static dsql_req* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); -static dsql_req* prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); +static DsqlRequest* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool); +static RefPtr prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, + bool, ntrace_result_t* traceResult); static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const); -static void release_statement(DsqlCompiledStatement* statement); -static void sql_info(thread_db*, dsql_req*, ULONG, const UCHAR*, ULONG, UCHAR*); +static void sql_info(thread_db*, DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*); static UCHAR* var_info(const dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*, const UCHAR* const, USHORT, bool); -static void checkD(IStatus*); - -static inline bool reqTypeWithCursor(DsqlCompiledStatement::Type type) -{ - switch (type) - { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - return true; - } - - return false; -} #ifdef DSQL_DEBUG unsigned DSQL_debug = 0; @@ -123,6 +110,21 @@ namespace IMPLEMENT_TRACE_ROUTINE(dsql_trace, "DSQL") #endif +dsql_dbb::dsql_dbb(MemoryPool& p, Attachment* attachment) + : dbb_relations(p), + dbb_procedures(p), + dbb_functions(p), + dbb_charsets(p), + dbb_collations(p), + dbb_charsets_by_id(p), + dbb_cursors(p), + dbb_pool(p), + dbb_dfl_charset(p) +{ + dbb_attachment = attachment; + dbb_statement_cache = FB_NEW_POOL(p) DsqlStatementCache(p, dbb_attachment); +} + dsql_dbb::~dsql_dbb() { } @@ -131,17 +133,17 @@ dsql_dbb::~dsql_dbb() // Execute a dynamic SQL statement. void DSQL_execute(thread_db* tdbb, jrd_tra** tra_handle, - dsql_req* request, + DsqlRequest* dsqlRequest, IMessageMetadata* in_meta, const UCHAR* in_msg, IMessageMetadata* out_meta, UCHAR* out_msg) { SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); - const DsqlCompiledStatement* statement = request->getStatement(); + const auto statement = dsqlRequest->getDsqlStatement(); - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) + if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_req_handle)); @@ -150,22 +152,22 @@ void DSQL_execute(thread_db* tdbb, // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) + statement->getType() != DsqlStatement::TYPE_START_TRANS && + statement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); } // A select with a non zero output length is a singleton select - const bool singleton = reqTypeWithCursor(statement->getType()) && out_msg; + const bool singleton = statement->isCursorBased() && out_msg; // If the request is a SELECT or blob statement then this is an open. // Make sure the cursor is not already open. - if (reqTypeWithCursor(statement->getType())) + if (statement->isCursorBased()) { - if (request->req_cursor) + if (dsqlRequest->req_cursor) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << Arg::Gds(isc_dsql_cursor_open_err)); @@ -175,153 +177,8 @@ void DSQL_execute(thread_db* tdbb, (Arg::Gds(isc_random) << "Cannot execute SELECT statement").raise(); } - request->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); -} - - -// Open a dynamic SQL cursor. -DsqlCursor* DSQL_open(thread_db* tdbb, - jrd_tra** tra_handle, - dsql_req* request, - IMessageMetadata* in_meta, const UCHAR* in_msg, - IMessageMetadata* out_meta, ULONG flags) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - - const DsqlCompiledStatement* statement = request->getStatement(); - - if (statement->getFlags() & DsqlCompiledStatement::FLAG_ORPHAN) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_bad_req_handle)); - } - - // Validate transaction handle - - if (!*tra_handle) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_bad_trans_handle)); - } - - // Validate statement type - - if (!reqTypeWithCursor(statement->getType())) - Arg::Gds(isc_no_cursor).raise(); - - // Validate cursor or batch being not already open - - if (request->req_cursor) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_cursor_open_err)); - } - - if (request->req_batch) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_batch_open)); - } - - request->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, NULL, false); - - request->req_cursor = FB_NEW_POOL(request->getPool()) DsqlCursor(request, flags); - return request->req_cursor; -} - - -// Provide backward-compatibility -void DsqlDmlRequest::setDelayedFormat(thread_db* tdbb, IMessageMetadata* metadata) -{ - if (!needDelayedFormat) - { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); - } - - needDelayedFormat = false; - - const auto message = (dsql_msg*) getStatement()->getReceiveMsg(); - if (metadata && message) - parseMetadata(metadata, message->msg_parameters); -} - - -// Fetch next record from a dynamic SQL cursor. -bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &getPool()); - - const DsqlCompiledStatement* statement = getStatement(); - - // if the cursor isn't open, we've got a problem - if (reqTypeWithCursor(statement->getType())) - { - if (!req_cursor) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_dsql_cursor_err) << - Arg::Gds(isc_dsql_cursor_not_open)); - } - } - - if (!req_request) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_unprepared_stmt)); - } - - const auto message = (dsql_msg*) statement->getReceiveMsg(); - - // Set up things for tracing this call - Jrd::Attachment* att = req_dbb->dbb_attachment; - TraceDSQLFetch trace(att, this); - - thread_db::TimerGuard timerGuard(tdbb, req_timer, false); - if (req_timer && req_timer->expired()) - tdbb->checkCancelState(); - - UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - if (!firstRowFetched && needRestarts()) - { - // Note: tra_handle can't be changed by executeReceiveWithRestarts below - // and outMetadata and outMsg in not used there, so passing NULL's is safe. - jrd_tra* tra = req_transaction; - - executeReceiveWithRestarts(tdbb, &tra, NULL, NULL, false, false, true); - fb_assert(tra == req_transaction); - } - else - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer); - - firstRowFetched = true; - - const dsql_par* const eof = statement->getEof(); - const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL; - const bool eofReached = eof && !(*eofPtr); - - if (eofReached) - { - if (req_timer) - req_timer->stop(); - - trace.fetch(true, ITracePlugin::RESULT_SUCCESS); - return false; - } - - if (msgBuffer) - mapInOut(tdbb, true, message, NULL, msgBuffer); - - trace.fetch(false, ITracePlugin::RESULT_SUCCESS); - return true; + dsqlRequest->req_transaction = *tra_handle; + dsqlRequest->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); } @@ -337,38 +194,33 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer) @param option **/ -void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option) +void DSQL_free_statement(thread_db* tdbb, DsqlRequest* dsqlRequest, USHORT option) { SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); - const DsqlCompiledStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); + + fb_assert(!(option & DSQL_unprepare)); // handled in y-valve if (option & DSQL_drop) { // Release everything associated with the request - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, dsqlRequest); } - /* - else if (option & DSQL_unprepare) - { - // Release everything but the request itself - dsql_req::destroy(tdbb, request, false); - } - */ else if (option & DSQL_close) { // Just close the cursor associated with the request - if (reqTypeWithCursor(statement->getType())) + if (dsqlStatement->isCursorBased()) { - if (!request->req_cursor) + if (!dsqlRequest->req_cursor) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-501) << Arg::Gds(isc_dsql_cursor_close_err)); } - DsqlCursor::close(tdbb, request->req_cursor); + DsqlCursor::close(tdbb, dsqlRequest->req_cursor); } } } @@ -393,7 +245,7 @@ void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option) @param buffer **/ -dsql_req* DSQL_prepare(thread_db* tdbb, +DsqlRequest* DSQL_prepare(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction, ULONG length, const TEXT* string, USHORT dialect, unsigned prepareFlags, Array* items, Array* buffer, @@ -402,19 +254,19 @@ dsql_req* DSQL_prepare(thread_db* tdbb, SET_TDBB(tdbb); dsql_dbb* database = init(tdbb, attachment); - dsql_req* request = NULL; + DsqlRequest* dsqlRequest = NULL; try { // Allocate a new request block and then prepare the request. - request = prepareRequest(tdbb, database, transaction, length, string, dialect, + dsqlRequest = prepareRequest(tdbb, database, transaction, length, string, dialect, isInternalRequest); // Can not prepare a CREATE DATABASE/SCHEMA statement - const DsqlCompiledStatement* statement = request->getStatement(); - if (statement->getType() == DsqlCompiledStatement::TYPE_CREATE_DB) + const auto statement = dsqlRequest->getDsqlStatement(); + if (statement->getType() == DsqlStatement::TYPE_CREATE_DB) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-530) << Arg::Gds(isc_dsql_crdb_prepare_err)); @@ -422,101 +274,25 @@ dsql_req* DSQL_prepare(thread_db* tdbb, if (items && buffer) { - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - sql_info(tdbb, request, items->getCount(), items->begin(), + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); + sql_info(tdbb, dsqlRequest, items->getCount(), items->begin(), buffer->getCount(), buffer->begin()); } - return request; + return dsqlRequest; } catch (const Exception&) { - if (request) + if (dsqlRequest) { - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - dsql_req::destroy(tdbb, request, true); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); + DsqlRequest::destroy(tdbb, dsqlRequest); } throw; } } -// Set a cursor name for a dynamic request. -void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) -{ - SET_TDBB(tdbb); - - Jrd::ContextPoolHolder context(tdbb, &getPool()); - - const size_t MAX_CURSOR_LENGTH = 132 - 1; - string cursor = name; - - if (cursor.hasData() && cursor[0] == '\"') - { - // Quoted cursor names eh? Strip'em. - // Note that "" will be replaced with ". - // The code is very strange, because it doesn't check for "" really - // and thus deletes one isolated " in the middle of the cursor. - for (string::iterator i = cursor.begin(); i < cursor.end(); ++i) - { - if (*i == '\"') - cursor.erase(i); - } - } - else // not quoted name - { - const string::size_type i = cursor.find(' '); - if (i != string::npos) - cursor.resize(i); - - cursor.upper(); - } - - USHORT length = (USHORT) fb_utils::name_length(cursor.c_str()); - - if (!length) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_invalid)); - } - - if (length > MAX_CURSOR_LENGTH) - length = MAX_CURSOR_LENGTH; - - cursor.resize(length); - - // If there already is a different cursor by the same name, bitch - - dsql_req* const* symbol = req_dbb->dbb_cursors.get(cursor); - if (symbol) - { - if (this == *symbol) - return; - - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_redefined) << cursor); - } - - // If there already is a cursor and its name isn't the same, ditto. - // We already know there is no cursor by this name in the hash table - - if (req_cursor && req_cursor_name.hasData()) - { - fb_assert(!symbol); - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) << - Arg::Gds(isc_dsql_decl_err) << - Arg::Gds(isc_dsql_cursor_redefined) << req_cursor_name); - } - - if (req_cursor_name.hasData()) - req_dbb->dbb_cursors.remove(req_cursor_name); - req_cursor_name = cursor; - req_dbb->dbb_cursors.put(cursor, this); -} - - /** DSQL_sql_info @@ -533,15 +309,15 @@ void DsqlDmlRequest::setCursor(thread_db* tdbb, const TEXT* name) **/ void DSQL_sql_info(thread_db* tdbb, - dsql_req* request, + DsqlRequest* dsqlRequest, ULONG item_length, const UCHAR* items, ULONG info_length, UCHAR* info) { SET_TDBB(tdbb); - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); - sql_info(tdbb, request, item_length, items, info_length, info); + sql_info(tdbb, dsqlRequest, item_length, items, info_length, info); } @@ -555,546 +331,53 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr SET_TDBB(tdbb); dsql_dbb* const database = init(tdbb, attachment); - dsql_req* request = NULL; + DsqlRequest* dsqlRequest = NULL; try { - request = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, + dsqlRequest = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, isInternalRequest); - const DsqlCompiledStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); // Only allow NULL trans_handle if we're starting a transaction or set session properties if (!*tra_handle && - statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) + dsqlStatement->getType() != DsqlStatement::TYPE_START_TRANS && + dsqlStatement->getType() != DsqlStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); } - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); // A select having cursor is a singleton select when executed immediate - const bool singleton = reqTypeWithCursor(statement->getType()); + const bool singleton = dsqlStatement->isCursorBased(); if (singleton && !(out_msg && out_meta)) { ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << Arg::Gds(isc_dsql_no_output_sqlda)); } - request->req_transaction = *tra_handle; + dsqlRequest->req_transaction = *tra_handle; - request->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); + dsqlRequest->execute(tdbb, tra_handle, in_meta, in_msg, out_meta, out_msg, singleton); - dsql_req::destroy(tdbb, request, true); + DsqlRequest::destroy(tdbb, dsqlRequest); } catch (const Exception&) { - if (request) + if (dsqlRequest) { - Jrd::ContextPoolHolder context(tdbb, &request->getPool()); - dsql_req::destroy(tdbb, request, true); + Jrd::ContextPoolHolder context(tdbb, &dsqlRequest->getPool()); + DsqlRequest::destroy(tdbb, dsqlRequest); } throw; } } -void DsqlDmlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) -{ - { // scope - Jrd::ContextPoolHolder scratchContext(tdbb, &scratch->getPool()); - node = Node::doDsqlPass(scratch, node); - } - - if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); - else - scratch->getStatement()->setBlrVersion(4); - - GEN_request(scratch, node); - - // Create the messages buffers - for (FB_SIZE_T i = 0; i < scratch->ports.getCount(); ++i) - { - dsql_msg* message = scratch->ports[i]; - - // Allocate buffer for message - const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1; - UCHAR* msgBuffer = FB_NEW_POOL(scratch->getStatement()->getPool()) UCHAR[newLen]; - msgBuffer = FB_ALIGN(msgBuffer, FB_DOUBLE_ALIGN); - message->msg_buffer_number = req_msg_buffers.add(msgBuffer); - } - - // have the access method compile the statement - -#ifdef DSQL_DEBUG - if (DSQL_debug & 64) - { - dsql_trace("Resulting BLR code for DSQL:"); - gds__trace_raw("Statement:\n"); - gds__trace_raw(statement->getSqlText()->c_str(), statement->getSqlText()->length()); - gds__trace_raw("\nBLR:\n"); - fb_print_blr(scratch->getBlrData().begin(), - (ULONG) scratch->getBlrData().getCount(), - gds__trace_printer, 0, 0); - } -#endif - - FbLocalStatus localStatus; - - // check for warnings - if (tdbb->tdbb_status_vector->getState() & IStatus::STATE_WARNINGS) - { - // save a status vector - fb_utils::copyStatus(&localStatus, tdbb->tdbb_status_vector); - fb_utils::init_status(tdbb->tdbb_status_vector); - } - - ISC_STATUS status = FB_SUCCESS; - - try - { - JRD_compile(tdbb, scratch->getAttachment()->dbb_attachment, &req_request, - scratch->getBlrData().getCount(), scratch->getBlrData().begin(), - statement->getSqlText(), - scratch->getDebugData().getCount(), scratch->getDebugData().begin(), - (scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST)); - } - catch (const Exception&) - { - status = tdbb->tdbb_status_vector->getErrors()[1]; - *traceResult = status == isc_no_priv ? - ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; - } - - // restore warnings (if there are any) - if (localStatus->getState() & IStatus::STATE_WARNINGS) - { - Arg::StatusVector cur(tdbb->tdbb_status_vector->getWarnings()); - Arg::StatusVector saved(localStatus->getWarnings()); - saved << cur; - - tdbb->tdbb_status_vector->setWarnings2(saved.length(), saved.value()); - } - - // free blr memory - scratch->getBlrData().free(); - - if (status) - status_exception::raise(tdbb->tdbb_status_vector); - - // We don't need the scratch pool anymore. Tell our caller to delete it. - node = NULL; - *destroyScratchPool = true; -} - -bool DsqlDmlRequest::needRestarts() -{ - return (req_transaction && (req_transaction->tra_flags & TRA_read_consistency) && - statement->getType() != DsqlCompiledStatement::TYPE_SAVEPOINT); -}; - -// Execute a dynamic SQL statement -void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - firstRowFetched = false; - const dsql_msg* message = statement->getSendMsg(); - - if (!message) - JRD_start(tdbb, req_request, req_transaction); - else - { - UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_start_and_send(tdbb, req_request, req_transaction, message->msg_number, - message->msg_length, msgBuffer); - } - - // Selectable execute block should get the "proc fetch" flag assigned, - // which ensures that the savepoint stack is preserved while suspending - if (statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK) - req_request->req_flags |= req_proc_fetch; - - // TYPE_EXEC_BLOCK has no outputs so there are no out_msg - // supplied from client side, but TYPE_EXEC_BLOCK requires - // 2-byte message for EOS synchronization - const bool isBlock = (statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK); - - message = statement->getReceiveMsg(); - - if (outMetadata == DELAYED_OUT_FORMAT) - { - needDelayedFormat = true; - outMetadata = NULL; - } - - if (outMetadata && message) - parseMetadata(outMetadata, message->msg_parameters); - - if ((outMsg && message) || isBlock) - { - UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2]; - dsql_msg temp_msg(*getDefaultMemoryPool()); - - // Insure that the metadata for the message is parsed, regardless of - // whether anything is found by the call to receive. - - UCHAR* msgBuffer = req_msg_buffers[message->msg_buffer_number]; - - if (!outMetadata && isBlock) - { - message = &temp_msg; - temp_msg.msg_number = 1; - temp_msg.msg_length = 2; - msgBuffer = FB_ALIGN(temp_buffer, FB_DOUBLE_ALIGN); - } - - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, msgBuffer); - - if (outMsg) - mapInOut(tdbb, true, message, NULL, outMsg); - - // if this is a singleton select, make sure there's in fact one record - - if (singleton) - { - USHORT counter; - - // Create a temp message buffer and try two more receives. - // If both succeed then the first is the next record and the - // second is either another record or the end of record message. - // In either case, there's more than one record. - - UCHAR* message_buffer = (UCHAR*) gds__alloc(message->msg_length); - - ISC_STATUS status = FB_SUCCESS; - FbLocalStatus localStatus; - - for (counter = 0; counter < 2 && !status; counter++) - { - localStatus->init(); - AutoSetRestore autoStatus(&tdbb->tdbb_status_vector, &localStatus); - - try - { - JRD_receive(tdbb, req_request, message->msg_number, - message->msg_length, message_buffer); - status = FB_SUCCESS; - } - catch (Exception&) - { - status = tdbb->tdbb_status_vector->getErrors()[1]; - } - } - - gds__free(message_buffer); - - // two successful receives means more than one record - // a req_sync error on the first pass above means no records - // a non-req_sync error on any of the passes above is an error - - if (!status) - status_exception::raise(Arg::Gds(isc_sing_select_err)); - else if (status == isc_req_sync && counter == 1) - status_exception::raise(Arg::Gds(isc_stream_eof)); - else if (status != isc_req_sync) - status_exception::raise(&localStatus); - } - } - - switch (statement->getType()) - { - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: - if (!req_request->req_records_updated) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << - Arg::Gds(isc_deadlock) << - Arg::Gds(isc_update_conflict)); - } - break; - - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: - if (!req_request->req_records_deleted) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) << - Arg::Gds(isc_deadlock) << - Arg::Gds(isc_update_conflict)); - } - break; - } -} - -// Execute a dynamic SQL statement with tracing, restart and timeout handler -void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - if (!req_request) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) << - Arg::Gds(isc_unprepared_stmt)); - } - - // If there is no data required, just start the request - - const dsql_msg* message = statement->getSendMsg(); - if (message) - mapInOut(tdbb, false, message, inMetadata, NULL, inMsg); - - // we need to mapInOut() before tracing of execution start to let trace - // manager know statement parameters values - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - - // Setup and start timeout timer - const bool have_cursor = reqTypeWithCursor(statement->getType()) && !singleton; - - setupTimer(tdbb); - thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor); - - if (needRestarts()) - executeReceiveWithRestarts(tdbb, traHandle, outMetadata, outMsg, singleton, true, false); - else { - doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); - } - - trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS); -} - -void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton, bool exec, bool fetch) -{ - req_request->req_flags &= ~req_update_conflict; - int numTries = 0; - const int MAX_RESTARTS = 10; - - while (true) - { - AutoSavePoint savePoint(tdbb, req_transaction); - - // Don't set req_restart_ready flag at last attempt to restart request. - // It allows to raise update conflict error (if any) as usual and - // handle error by PSQL handler. - const ULONG flag = (numTries >= MAX_RESTARTS) ? 0 : req_restart_ready; - AutoSetRestoreFlag restartReady(&req_request->req_flags, flag, true); - try - { - if (exec) - doExecute(tdbb, traHandle, outMetadata, outMsg, singleton); - - if (fetch) - { - fb_assert(reqTypeWithCursor(statement->getType())); - - const dsql_msg* message = statement->getReceiveMsg(); - - UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number]; - JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer); - } - } - catch (const status_exception&) - { - if (!(req_transaction->tra_flags & TRA_ex_restart)) - { - req_request->req_flags &= ~req_update_conflict; - throw; - } - } - - if (!(req_request->req_flags & req_update_conflict)) - { - fb_assert((req_transaction->tra_flags & TRA_ex_restart) == 0); - req_transaction->tra_flags &= ~TRA_ex_restart; - -#ifdef DEV_BUILD - if (numTries > 0) - { - string s; - s.printf("restarts = %d", numTries); - - ERRD_post_warning(Arg::Warning(isc_random) << Arg::Str(s)); - } -#endif - savePoint.release(); // everything is ok - break; - } - - fb_assert((req_transaction->tra_flags & TRA_ex_restart) != 0); - - req_request->req_flags &= ~req_update_conflict; - req_transaction->tra_flags &= ~TRA_ex_restart; - fb_utils::init_status(tdbb->tdbb_status_vector); - - // Undo current savepoint but preserve already taken locks. - // Savepoint will be restarted at the next loop iteration. - savePoint.rollback(true); - - numTries++; - if (numTries >= MAX_RESTARTS) - { - gds__log("Update conflict: unable to get a stable set of rows in the source tables\n" - "\tafter %d attempts of restart.\n" - "\tQuery:\n%s\n", numTries, req_request->getStatement()->sqlText->c_str() ); - } - - // When restart we must execute query - exec = true; - } -} - -void DsqlDdlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) -{ - Database* const dbb = tdbb->getDatabase(); - - internalScratch = scratch; - - scratch->flags |= DsqlCompilerScratch::FLAG_DDL; - - try - { - node = Node::doDsqlPass(scratch, node); - } - catch (status_exception& ex) - { - rethrowDdlException(ex, false); - } - - if (dbb->readOnly()) - ERRD_post(Arg::Gds(isc_read_only_database)); - - // In read-only replica, only replicator is allowed to execute DDL. - // As an exception, not replicated DDL statements are also allowed. - if (dbb->isReplica(REPLICA_READ_ONLY) && - !(tdbb->tdbb_flags & TDBB_replicator) && - node->mustBeReplicated()) - { - ERRD_post(Arg::Gds(isc_read_only_trans)); - } - - const auto dbDialect = - (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; - - if ((scratch->flags & DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT) && - dbDialect != scratch->clientDialect) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) << - Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(dbDialect)); - } - - if (scratch->clientDialect > SQL_DIALECT_V5) - scratch->getStatement()->setBlrVersion(5); - else - scratch->getStatement()->setBlrVersion(4); -} - -// Execute a dynamic SQL statement. -void DsqlDdlRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - - fb_utils::init_status(tdbb->tdbb_status_vector); - - // run all statements under savepoint control - { // scope - AutoSavePoint savePoint(tdbb, req_transaction); - - try - { - AutoSetRestoreFlag execDdl(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); - - node->executeDdl(tdbb, internalScratch, req_transaction); - - if (node->mustBeReplicated()) - REPL_exec_sql(tdbb, req_transaction, getStatement()->getOrgText()); - } - catch (status_exception& ex) - { - rethrowDdlException(ex, true); - } - - savePoint.release(); // everything is ok - } - - JRD_autocommit_ddl(tdbb, req_transaction); - - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - -bool DsqlDdlRequest::mustBeReplicated() const -{ - return node->mustBeReplicated(); -} - -// Rethrow an exception with isc_no_meta_update and prefix codes. -void DsqlDdlRequest::rethrowDdlException(status_exception& ex, bool metadataUpdate) -{ - Arg::StatusVector newVector; - - if (metadataUpdate) - newVector << Arg::Gds(isc_no_meta_update); - - node->putErrorPrefix(newVector); - - const ISC_STATUS* status = ex.value(); - - if (status[1] == isc_no_meta_update) - status += 2; - - newVector.append(Arg::StatusVector(status)); - - status_exception::raise(newVector); -} - - -void DsqlTransactionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* /*traceResult*/) -{ - node = Node::doDsqlPass(scratch, node); - - // Don't trace anything except savepoint statements - req_traced = (scratch->getStatement()->getType() == DsqlCompiledStatement::TYPE_SAVEPOINT); -} - -// Execute a dynamic SQL statement. -void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, - IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, - bool /*singleton*/) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - node->execute(tdbb, this, traHandle); - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - - -void DsqlSessionManagementRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* /*traceResult*/) -{ - node = Node::doDsqlPass(scratch, node); -} - -// Execute a dynamic SQL statement. -void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, - IMessageMetadata* inMetadata, const UCHAR* inMsg, - IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) -{ - TraceDSQLExecute trace(req_dbb->dbb_attachment, this); - node->execute(tdbb, this, traHandle); - trace.finish(false, ITracePlugin::RESULT_SUCCESS); -} - - /** get_request_info @@ -1107,16 +390,16 @@ void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, @param buffer **/ -static ULONG get_request_info(thread_db* tdbb, dsql_req* request, ULONG buffer_length, UCHAR* buffer) +static ULONG get_request_info(thread_db* tdbb, DsqlRequest* dsqlRequest, ULONG buffer_length, UCHAR* buffer) { - if (!request->req_request) // DDL + if (!dsqlRequest->getRequest()) // DDL return 0; // get the info for the request from the engine try { - return INF_request_info(request->req_request, sizeof(record_info), record_info, + return INF_request_info(dsqlRequest->getRequest(), sizeof(record_info), record_info, buffer_length, buffer); } catch (Exception&) @@ -1144,8 +427,7 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment) return attachment->att_dsql_instance; MemoryPool& pool = *attachment->createPool(); - dsql_dbb* const database = FB_NEW_POOL(pool) dsql_dbb(pool); - database->dbb_attachment = attachment; + dsql_dbb* const database = FB_NEW_POOL(pool) dsql_dbb(pool, attachment); attachment->att_dsql_instance = database; INI_init_dsql(tdbb, database); @@ -1158,348 +440,45 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment) } -/** - - mapInOut - - @brief Map data from external world into message or - from message to external world. - - - @param request - @param toExternal - @param message - @param meta - @param dsql_msg_buf - @param in_dsql_msg_buf - - **/ -void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* message, - IMessageMetadata* meta, UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf) -{ - USHORT count = parseMetadata(meta, message->msg_parameters); - - // Sanity check - - if (count) - { - if (toExternal) - { - if (dsql_msg_buf == NULL) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_no_output_sqlda)); - } - } - else - { - if (in_dsql_msg_buf == NULL) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_no_input_sqlda)); - } - } - } - - USHORT count2 = 0; - - for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i) - { - dsql_par* parameter = message->msg_parameters[i]; - - if (parameter->par_index) - { - // Make sure the message given to us is long enough - - dsc desc; - if (!req_user_descs.get(parameter, desc)) - desc.clear(); - - /*** - ULONG length = (IPTR) desc.dsc_address + desc.dsc_length; - if (length > msg_length) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_random) << "Message buffer too short"); - } - ***/ - if (!desc.dsc_dtype) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_datatype_err) << - Arg::Gds(isc_dsql_sqlvar_index) << Arg::Num(parameter->par_index-1)); - } - - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - SSHORT* flag = NULL; - dsql_par* const null_ind = parameter->par_null; - if (null_ind != NULL) - { - dsc userNullDesc; - if (!req_user_descs.get(null_ind, userNullDesc)) - userNullDesc.clear(); - - const ULONG null_offset = (IPTR) userNullDesc.dsc_address; - - /*** - length = null_offset + sizeof(SSHORT); - if (length > msg_length) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) - << Arg::Gds(isc_random) << "Message buffer too short"); - } - ***/ - - dsc nullDesc = null_ind->par_desc; - nullDesc.dsc_address = msgBuffer + (IPTR) nullDesc.dsc_address; - - if (toExternal) - { - flag = reinterpret_cast(dsql_msg_buf + null_offset); - *flag = *reinterpret_cast(nullDesc.dsc_address); - } - else - { - flag = reinterpret_cast(nullDesc.dsc_address); - *flag = *reinterpret_cast(in_dsql_msg_buf + null_offset); - } - } - - const bool notNull = (!flag || *flag >= 0); - - dsc parDesc = parameter->par_desc; - parDesc.dsc_address = msgBuffer + (IPTR) parDesc.dsc_address; - - if (toExternal) - { - desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; - - if (notNull) - MOVD_move(tdbb, &parDesc, &desc); - else - memset(desc.dsc_address, 0, desc.dsc_length); - } - else if (notNull && !parDesc.isNull()) - { - // Safe cast because desc is used as source only. - desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; - MOVD_move(tdbb, &desc, &parDesc); - } - else - memset(parDesc.dsc_address, 0, parDesc.dsc_length); - - ++count2; - } - } - - if (count != count2) - { - ERRD_post( - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_wrong_param_num) << Arg::Num(count) <getParentDbKey()) && - (parameter = statement->getDbKey())) - { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL; - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - fb_assert(parentMsgBuffer); - - dsc parentDesc = dbkey->par_desc; - parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; - - dsc desc = parameter->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - MOVD_move(tdbb, &parentDesc, &desc); - - dsql_par* null_ind = parameter->par_null; - if (null_ind != NULL) - { - desc = null_ind->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - SSHORT* flag = (SSHORT*) desc.dsc_address; - *flag = 0; - } - } - - const dsql_par* rec_version; - if (!toExternal && (rec_version = statement->getParentRecVersion()) && - (parameter = statement->getRecVersion())) - { - UCHAR* parentMsgBuffer = statement->getParentRequest() ? - statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] : - NULL; - UCHAR* msgBuffer = req_msg_buffers[parameter->par_message->msg_buffer_number]; - - fb_assert(parentMsgBuffer); - - dsc parentDesc = rec_version->par_desc; - parentDesc.dsc_address = parentMsgBuffer + (IPTR) parentDesc.dsc_address; - - dsc desc = parameter->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - MOVD_move(tdbb, &parentDesc, &desc); - - dsql_par* null_ind = parameter->par_null; - if (null_ind != NULL) - { - desc = null_ind->par_desc; - desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - - SSHORT* flag = (SSHORT*) desc.dsc_address; - *flag = 0; - } - } -} - - -/** - - parseMetadata - - @brief Parse the message of a request. - - - @param request - @param meta - @param parameters_list - - **/ -USHORT dsql_req::parseMetadata(IMessageMetadata* meta, const Array& parameters_list) -{ - HalfStaticArray parameters; - - for (FB_SIZE_T i = 0; i < parameters_list.getCount(); ++i) - { - dsql_par* param = parameters_list[i]; - - if (param->par_index) - { - if (param->par_index > parameters.getCount()) - parameters.grow(param->par_index); - fb_assert(!parameters[param->par_index - 1]); - parameters[param->par_index - 1] = param; - } - } - - // If there's no metadata, then the format of the current message buffer - // is identical to the format of the previous one. - - if (!meta) - return parameters.getCount(); - - FbLocalStatus st; - unsigned count = meta->getCount(&st); - checkD(&st); - - unsigned count2 = parameters.getCount(); - - if (count != count2) - { - ERRD_post(Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_dsql_wrong_param_num) <getType(&st, index); - checkD(&st); - unsigned sqlLength = meta->getLength(&st, index); - checkD(&st); - - dsc desc; - desc.dsc_flags = 0; - - unsigned dataOffset, nullOffset, dtype, dlength; - offset = fb_utils::sqlTypeToDsc(offset, sqlType, sqlLength, - &dtype, &dlength, &dataOffset, &nullOffset); - desc.dsc_dtype = dtype; - desc.dsc_length = dlength; - - desc.dsc_scale = meta->getScale(&st, index); - checkD(&st); - desc.dsc_sub_type = meta->getSubType(&st, index); - checkD(&st); - unsigned textType = meta->getCharSet(&st, index); - checkD(&st); - desc.setTextType(textType); - desc.dsc_address = (UCHAR*)(IPTR) dataOffset; - - const dsql_par* const parameter = parameters[index]; - fb_assert(parameter); - - // ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been - // implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started - // failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using - // blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset - // here at the server as a way to make older (and not yet changed) client work - // correctly. - if (desc.isText() && desc.getTextType() == ttype_dynamic) - desc.setTextType(ttype_none); - - req_user_descs.put(parameter, desc); - - dsql_par* null = parameter->par_null; - if (null) - { - desc.clear(); - desc.dsc_dtype = dtype_short; - desc.dsc_scale = 0; - desc.dsc_length = sizeof(SSHORT); - desc.dsc_address = (UCHAR*)(IPTR) nullOffset; - - req_user_descs.put(null, desc); - } - } - - return count; -} - - -// raise error if one present -static void checkD(IStatus* st) -{ - if (st->getState() & IStatus::STATE_ERRORS) - { - ERRD_post(Arg::StatusVector(st)); - } -} - - -// Prepare a request for execution. Return SQL status code. +// Prepare a request for execution. // Note: caller is responsible for pool handling. -static dsql_req* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, +static DsqlRequest* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest) { - return prepareStatement(tdbb, database, transaction, textLength, text, clientDialect, isInternalRequest); + TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text); + + ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS; + try + { + auto statement = prepareStatement(tdbb, database, transaction, textLength, text, + clientDialect, isInternalRequest, &traceResult); + + auto dsqlRequest = statement->createRequest(tdbb, database); + + dsqlRequest->req_traced = true; + trace.setStatement(dsqlRequest); + trace.prepare(traceResult); + + return dsqlRequest; + } + catch (const Exception&) + { + trace.prepare(ITracePlugin::RESULT_FAILED); + throw; + } } -// Prepare a statement for execution. Return SQL status code. +// Prepare a statement for execution. // Note: caller is responsible for pool handling. -static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, - ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest) +static RefPtr prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction, + ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest, ntrace_result_t* traceResult) { Database* const dbb = tdbb->getDatabase(); if (text && textLength == 0) textLength = static_cast(strlen(text)); - TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text); - if (clientDialect > SQL_DIALECT_CURRENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << @@ -1533,29 +512,33 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* Arg::Gds(isc_sql_too_long) << Arg::Num(MAX_SQL_LENGTH)); } + string textStr(text, textLength); + const bool isStatementCacheActive = database->dbb_statement_cache->isActive(); + + RefPtr dsqlStatement; + + if (isStatementCacheActive) + { + dsqlStatement = database->dbb_statement_cache->getStatement(tdbb, textStr, clientDialect, isInternalRequest); + + if (dsqlStatement) + return dsqlStatement; + } + // allocate the statement block, then prepare the statement + MemoryPool* scratchPool = nullptr; + DsqlCompilerScratch* scratch = nullptr; MemoryPool* statementPool = database->createPool(); - MemoryPool* scratchPool = NULL; - dsql_req* request = NULL; Jrd::ContextPoolHolder statementContext(tdbb, statementPool); try { - DsqlCompiledStatement* statement = FB_NEW_POOL(*statementPool) DsqlCompiledStatement(*statementPool); - scratchPool = database->createPool(); if (!transaction) // Useful for session management statements transaction = database->dbb_attachment->getSysTransaction(); - DsqlCompilerScratch* scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database, - transaction, statement); - scratch->clientDialect = clientDialect; - - if (isInternalRequest) - scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST; - const auto dbDialect = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; @@ -1566,12 +549,19 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* { // scope to delete parser before the scratch pool is gone Jrd::ContextPoolHolder scratchContext(tdbb, scratchPool); - Parser parser(tdbb, *scratchPool, scratch, clientDialect, + scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database, transaction); + scratch->clientDialect = clientDialect; + + if (isInternalRequest) + scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST; + + Parser parser(tdbb, *scratchPool, statementPool, scratch, clientDialect, dbDialect, text, textLength, charSetId); // Parse the SQL statement. If it croaks, return - request = parser.parse(); - request->liveScratchPool = scratchPool; + dsqlStatement = parser.parse(); + + scratch->setDsqlStatement(dsqlStatement); if (parser.isStmtAmbiguous()) scratch->flags |= DsqlCompilerScratch::FLAG_AMBIGUOUS_STMT; @@ -1579,10 +569,6 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transformedText = parser.getTransformedString(); } - request->req_dbb = scratch->getAttachment(); - request->req_transaction = scratch->getTransaction(); - request->statement = scratch->getStatement(); - // If the attachment charset is NONE, replace non-ASCII characters by question marks, so // that engine internals doesn't receive non-mappeable data to UTF8. If an attachment // charset is used, validate the string. @@ -1614,59 +600,41 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transformedText.assign(temp.begin(), temp.getCount()); } - statement->setSqlText(FB_NEW_POOL(*statementPool) RefString(*statementPool, transformedText)); + dsqlStatement->setSqlText(FB_NEW_POOL(*statementPool) RefString(*statementPool, transformedText)); // allocate the send and receive messages - statement->setSendMsg(FB_NEW_POOL(*statementPool) dsql_msg(*statementPool)); + dsqlStatement->setSendMsg(FB_NEW_POOL(*statementPool) dsql_msg(*statementPool)); dsql_msg* message = FB_NEW_POOL(*statementPool) dsql_msg(*statementPool); - statement->setReceiveMsg(message); + dsqlStatement->setReceiveMsg(message); message->msg_number = 1; - statement->setType(DsqlCompiledStatement::TYPE_SELECT); + dsqlStatement->setType(DsqlStatement::TYPE_SELECT); + dsqlStatement->dsqlPass(tdbb, scratch, traceResult); - request->req_traced = true; - trace.setStatement(request); + if (!dsqlStatement->shouldPreserveScratch()) + database->deletePool(scratchPool); - ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS; - try + scratchPool = nullptr; + + if (!isInternalRequest && dsqlStatement->mustBeReplicated()) + dsqlStatement->setOrgText(text, textLength); + + if (isStatementCacheActive && dsqlStatement->isDml()) { - bool destroyScratchPool = false; - request->dsqlPass(tdbb, scratch, &destroyScratchPool, &traceResult); - - if (destroyScratchPool) - { - database->deletePool(scratchPool); - request->liveScratchPool = scratchPool = NULL; - } - } - catch (const Exception&) - { - trace.prepare(traceResult); - throw; + database->dbb_statement_cache->putStatement(tdbb, + textStr, clientDialect, isInternalRequest, dsqlStatement); } - if (!isInternalRequest && request->mustBeReplicated()) - statement->setOrgText(text, textLength); - - trace.prepare(traceResult); - - return request; + return dsqlStatement; } catch (const Exception&) { - trace.prepare(ITracePlugin::RESULT_FAILED); - - if (request) - { - request->req_traced = false; - dsql_req::destroy(tdbb, request, true); - } - else - { + if (scratchPool) database->deletePool(scratchPool); + + if (!dsqlStatement) database->deletePool(statementPool); - } throw; } @@ -1713,261 +681,14 @@ static UCHAR* put_item( UCHAR item, } -// Release a compiled statement. -static void release_statement(DsqlCompiledStatement* statement) -{ - if (statement->getParentRequest()) - { - dsql_req* parent = statement->getParentRequest(); - - FB_SIZE_T pos; - if (parent->cursors.find(statement, pos)) - parent->cursors.remove(pos); - - statement->setParentRequest(NULL); - } - - statement->setSqlText(NULL); - statement->setOrgText(NULL, 0); -} - - -// Class DsqlCompiledStatement - -void DsqlCompiledStatement::setOrgText(const char* ptr, ULONG len) -{ - if (!ptr || !len) - { - orgText = NULL; - return; - } - - const string text(ptr, len); - - if (text == *sqlText) - orgText = sqlText; - else - orgText = FB_NEW_POOL(getPool()) RefString(getPool(), text); -} - - -// Class dsql_req - -dsql_req::dsql_req(MemoryPool& pool) - : req_pool(pool), - statement(NULL), - liveScratchPool(NULL), - cursors(req_pool), - req_dbb(NULL), - req_transaction(NULL), - req_msg_buffers(req_pool), - req_cursor_name(req_pool), - req_cursor(NULL), - req_batch(NULL), - req_user_descs(req_pool), - req_traced(false), - req_timeout(0) -{ -} - -dsql_req::~dsql_req() -{ -} - -void dsql_req::setCursor(thread_db* /*tdbb*/, const TEXT* /*name*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); -} - -void dsql_req::setDelayedFormat(thread_db* /*tdbb*/, IMessageMetadata* /*metadata*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); -} - -bool dsql_req::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/) -{ - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_sqlda_err) << - Arg::Gds(isc_req_sync)); - - return false; // avoid warning -} - -unsigned int dsql_req::getTimeout() -{ - return req_timeout; -} - -unsigned int dsql_req::getActualTimeout() -{ - if (req_timer) - return req_timer->getValue(); - - return 0; -} - -void dsql_req::setTimeout(unsigned int timeOut) -{ - req_timeout = timeOut; -} - -TimeoutTimer* dsql_req::setupTimer(thread_db* tdbb) -{ - if (req_request) - { - if (req_request->hasInternalStatement()) - return req_timer; - - req_request->req_timeout = this->req_timeout; - - fb_assert(!req_request->req_caller); - if (req_request->req_caller) - { - if (req_timer) - req_timer->setup(0, 0); - return req_timer; - } - } - - Database* dbb = tdbb->getDatabase(); - Attachment* att = tdbb->getAttachment(); - - ISC_STATUS toutErr = isc_cfg_stmt_timeout; - unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000; - - if (req_timeout) - { - if (!timeOut || req_timeout < timeOut) - { - timeOut = req_timeout; - toutErr = isc_req_stmt_timeout; - } - } - else - { - const unsigned int attTout = att->getStatementTimeout(); - - if (!timeOut || attTout && attTout < timeOut) - { - timeOut = attTout; - toutErr = isc_att_stmt_timeout; - } - } - - if (!req_timer && timeOut) - { - req_timer = FB_NEW TimeoutTimer(); - req_request->req_timer = this->req_timer; - } - - if (req_timer) - { - req_timer->setup(timeOut, toutErr); - req_timer->start(); - } - - return req_timer; -} - -// Release a dynamic request. -void dsql_req::destroy(thread_db* tdbb, dsql_req* request, bool drop) -{ - SET_TDBB(tdbb); - - if (request->req_timer) - { - request->req_timer->stop(); - request->req_timer = NULL; - } - - // If request is parent, orphan the children and release a portion of their requests - - for (FB_SIZE_T i = 0; i < request->cursors.getCount(); ++i) - { - DsqlCompiledStatement* child = request->cursors[i]; - child->addFlags(DsqlCompiledStatement::FLAG_ORPHAN); - child->setParentRequest(NULL); - - // hvlad: lines below is commented out as - // - child is already unlinked from its parent request - // - we should not free child's sql text until its owner request is alive - // It seems to me we should destroy owner request here, not a child - // statement - as it always was before - - //Jrd::ContextPoolHolder context(tdbb, &child->getPool()); - //release_statement(child); - } - - // If the request had an open cursor, close it - - if (request->req_cursor) - DsqlCursor::close(tdbb, request->req_cursor); - - if (request->req_batch) - { - delete request->req_batch; - request->req_batch = nullptr; - } - - Jrd::Attachment* att = request->req_dbb->dbb_attachment; - const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); - if (need_trace_free) - { - TraceSQLStatementImpl stmt(request, NULL); - TraceManager::event_dsql_free(att, &stmt, DSQL_drop); - } - request->req_traced = false; - - if (request->req_cursor_name.hasData()) - { - request->req_dbb->dbb_cursors.remove(request->req_cursor_name); - request->req_cursor_name = ""; - } - - // If a request has been compiled, release it now - - if (request->req_request) - { - ThreadStatusGuard status_vector(tdbb); - - try - { - CMP_release(tdbb, request->req_request); - request->req_request = NULL; - } - catch (Exception&) - {} // no-op - } - - const DsqlCompiledStatement* statement = request->getStatement(); - release_statement(const_cast(statement)); - - // Release the entire request if explicitly asked for - - if (drop) - { - request->req_dbb->deletePool(request->liveScratchPool); - request->req_dbb->deletePool(&request->getPool()); - } -} - - // Return as UTF8 -string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const +string IntlString::toUtf8(jrd_tra* transaction) const { CHARSET_ID id = CS_dynamic; if (charset.hasData()) { - const dsql_intlsym* resolved = METD_get_charset(dsqlScratch->getTransaction(), - charset.length(), charset.c_str()); + const dsql_intlsym* resolved = METD_get_charset(transaction, charset.length(), charset.c_str()); if (!resolved) { @@ -1990,7 +711,7 @@ string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const @brief Return DSQL information buffer. - @param request + @param dsqlRequest @param item_length @param items @param info_length @@ -1999,7 +720,7 @@ string IntlString::toUtf8(DsqlCompilerScratch* dsqlScratch) const **/ static void sql_info(thread_db* tdbb, - dsql_req* request, + DsqlRequest* dsqlRequest, ULONG item_length, const UCHAR* items, ULONG info_length, @@ -2032,7 +753,7 @@ static void sql_info(thread_db* tdbb, bool messageFound = false; USHORT first_index = 0; - const DsqlCompiledStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); while (items < end_items && *items != isc_info_end && info < end_info) { @@ -2046,7 +767,7 @@ static void sql_info(thread_db* tdbb, case isc_info_sql_select: case isc_info_sql_bind: message = (item == isc_info_sql_select) ? - statement->getReceiveMsg() : statement->getSendMsg(); + dsqlStatement->getReceiveMsg() : dsqlStatement->getSendMsg(); messageFound = true; if (info + 1 >= end_info) { @@ -2058,16 +779,16 @@ static void sql_info(thread_db* tdbb, case isc_info_sql_stmt_flags: value = IStatement::FLAG_REPEAT_EXECUTE; - switch (statement->getType()) + switch (dsqlStatement->getType()) { - case DsqlCompiledStatement::TYPE_CREATE_DB: - case DsqlCompiledStatement::TYPE_DDL: + case DsqlStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_DDL: value &= ~IStatement::FLAG_REPEAT_EXECUTE; break; - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_RETURNING_CURSOR: value |= IStatement::FLAG_HAS_CURSOR; break; } @@ -2078,57 +799,57 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_stmt_type: - switch (statement->getType()) + switch (dsqlStatement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_RETURNING_CURSOR: number = isc_info_sql_stmt_select; break; - case DsqlCompiledStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_UPD: number = isc_info_sql_stmt_select_for_upd; break; - case DsqlCompiledStatement::TYPE_CREATE_DB: - case DsqlCompiledStatement::TYPE_DDL: + case DsqlStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_DDL: number = isc_info_sql_stmt_ddl; break; - case DsqlCompiledStatement::TYPE_COMMIT: - case DsqlCompiledStatement::TYPE_COMMIT_RETAIN: + case DsqlStatement::TYPE_COMMIT: + case DsqlStatement::TYPE_COMMIT_RETAIN: number = isc_info_sql_stmt_commit; break; - case DsqlCompiledStatement::TYPE_ROLLBACK: - case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN: + case DsqlStatement::TYPE_ROLLBACK: + case DsqlStatement::TYPE_ROLLBACK_RETAIN: number = isc_info_sql_stmt_rollback; break; - case DsqlCompiledStatement::TYPE_START_TRANS: + case DsqlStatement::TYPE_START_TRANS: number = isc_info_sql_stmt_start_trans; break; - case DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT: + case DsqlStatement::TYPE_SESSION_MANAGEMENT: number = isc_info_sql_stmt_ddl; // ????????????????? break; - case DsqlCompiledStatement::TYPE_INSERT: + case DsqlStatement::TYPE_INSERT: number = isc_info_sql_stmt_insert; break; - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_UPDATE_CURSOR: number = isc_info_sql_stmt_update; break; - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_DELETE_CURSOR: number = isc_info_sql_stmt_delete; break; - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_EXEC_PROCEDURE: number = isc_info_sql_stmt_exec_procedure; break; - case DsqlCompiledStatement::TYPE_SET_GENERATOR: + case DsqlStatement::TYPE_SET_GENERATOR: number = isc_info_sql_stmt_set_generator; break; - case DsqlCompiledStatement::TYPE_SAVEPOINT: + case DsqlStatement::TYPE_SAVEPOINT: number = isc_info_sql_stmt_savepoint; break; - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_EXEC_BLOCK: number = isc_info_sql_stmt_exec_procedure; break; - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT_BLOCK: number = isc_info_sql_stmt_select; break; default: @@ -2164,7 +885,7 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_batch_fetch: - if (statement->getFlags() & DsqlCompiledStatement::FLAG_NO_BATCH) + if (dsqlStatement->getFlags() & DsqlStatement::FLAG_NO_BATCH) number = 0; else number = 1; @@ -2174,14 +895,14 @@ static void sql_info(thread_db* tdbb, break; case isc_info_sql_records: - length = get_request_info(tdbb, request, sizeof(buffer), buffer); + length = get_request_info(tdbb, dsqlRequest, sizeof(buffer), buffer); if (length && !(info = put_item(item, length, buffer, info, end_info))) return; break; case isc_info_sql_stmt_timeout_user: case isc_info_sql_stmt_timeout_run: - value = (item == isc_info_sql_stmt_timeout_user) ? request->getTimeout() : request->getActualTimeout(); + value = (item == isc_info_sql_stmt_timeout_user) ? dsqlRequest->getTimeout() : dsqlRequest->getActualTimeout(); length = put_vax_long(buffer, value); if (!(info = put_item(item, length, buffer, info, end_info))) @@ -2201,7 +922,7 @@ static void sql_info(thread_db* tdbb, { const bool detailed = (item == isc_info_sql_explain_plan); string plan = tdbb->getAttachment()->stringToUserCharSet(tdbb, - OPT_get_plan(tdbb, request->req_request, detailed)); + Optimizer::getPlan(tdbb, dsqlRequest->getStatement(), detailed)); if (plan.hasData()) { @@ -2251,9 +972,9 @@ static void sql_info(thread_db* tdbb, { HalfStaticArray path; - if (request->req_request && request->req_request->getStatement()) + if (dsqlRequest->getStatement()) { - const auto& blr = request->req_request->getStatement()->blr; + const auto& blr = dsqlRequest->getStatement()->blr; if (blr.hasData()) { @@ -2323,7 +1044,7 @@ static void sql_info(thread_db* tdbb, } info = var_info(message, items, end_describe, info, end_info, first_index, - message == statement->getSendMsg()); + message == dsqlStatement->getSendMsg()); if (!info) return; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index a09cf22536..067ae93c61 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -35,6 +35,7 @@ #define DSQL_DSQL_H #include "../common/classes/array.h" +#include "../common/classes/fb_atomic.h" #include "../common/classes/GenericMap.h" #include "../jrd/MetaName.h" #include "../common/classes/stack.h" @@ -76,21 +77,18 @@ namespace Jrd class Attachment; class Database; class DsqlCompilerScratch; - class DdlNode; + class DsqlStatement; + class DsqlStatementCache; class RseNode; - class StmtNode; - class TransactionNode; - class SessionManagementNode; class ValueExprNode; class ValueListNode; class WindowClause; class jrd_tra; - class jrd_req; + class Request; class blb; struct bid; class dsql_ctx; - class dsql_msg; class dsql_par; class dsql_map; class dsql_intlsym; @@ -121,38 +119,21 @@ namespace Jrd { class dsql_dbb : public pool_alloc { public: - Firebird::GenericMap > > dbb_relations; // known relations in database - Firebird::GenericMap > > dbb_procedures; // known procedures in database - Firebird::GenericMap > > dbb_functions; // known functions in database - Firebird::GenericMap > > dbb_charsets; // known charsets in database - Firebird::GenericMap > > dbb_collations; // known collations in database - Firebird::GenericMap > > dbb_charsets_by_id; // charsets sorted by charset_id - Firebird::GenericMap > > dbb_cursors; // known cursors in database + Firebird::LeftPooledMap dbb_relations; // known relations in database + Firebird::LeftPooledMap dbb_procedures; // known procedures in database + Firebird::LeftPooledMap dbb_functions; // known functions in database + Firebird::LeftPooledMap dbb_charsets; // known charsets in database + Firebird::LeftPooledMap dbb_collations; // known collations in database + Firebird::NonPooledMap dbb_charsets_by_id; // charsets sorted by charset_id + Firebird::LeftPooledMap dbb_cursors; // known cursors in database + Firebird::AutoPtr dbb_statement_cache; MemoryPool& dbb_pool; // The current pool for the dbb Attachment* dbb_attachment; MetaName dbb_dfl_charset; bool dbb_no_charset; - explicit dsql_dbb(MemoryPool& p) - : dbb_relations(p), - dbb_procedures(p), - dbb_functions(p), - dbb_charsets(p), - dbb_collations(p), - dbb_charsets_by_id(p), - dbb_cursors(p), - dbb_pool(p), - dbb_dfl_charset(p) - {} - + dsql_dbb(MemoryPool& p, Attachment* attachment); ~dsql_dbb(); MemoryPool* createPool() @@ -176,13 +157,13 @@ public: { } - class dsql_fld* rel_fields; // Field block - //dsql_rel* rel_base_relation; // base relation for an updatable view - MetaName rel_name; // Name of relation - MetaName rel_owner; // Owner of relation - USHORT rel_id; // Relation id - USHORT rel_dbkey_length; - USHORT rel_flags; + class dsql_fld* rel_fields; // Field block + //dsql_rel* rel_base_relation; // base relation for an updatable view + MetaName rel_name; // Name of relation + MetaName rel_owner; // Owner of relation + USHORT rel_id; // Relation id + USHORT rel_dbkey_length; + USHORT rel_flags; }; // rel_flags bits @@ -451,320 +432,6 @@ enum intlsym_flags_vals { INTLSYM_dropped = 1 // intlsym has been dropped }; - -// Compiled statement - shared by multiple requests. -class DsqlCompiledStatement : public Firebird::PermanentStorage -{ -public: - enum Type // statement type - { - TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, - TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, - TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, - TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT, - TYPE_RETURNING_CURSOR - }; - - // Statement flags. - static const unsigned FLAG_ORPHAN = 0x01; - static const unsigned FLAG_NO_BATCH = 0x02; - //static const unsigned FLAG_BLR_VERSION4 = 0x04; - //static const unsigned FLAG_BLR_VERSION5 = 0x08; - static const unsigned FLAG_SELECTABLE = 0x10; - -public: - explicit DsqlCompiledStatement(MemoryPool& p) - : PermanentStorage(p), - type(TYPE_SELECT), - flags(0), - blrVersion(5), - sendMsg(NULL), - receiveMsg(NULL), - eof(NULL), - dbKey(NULL), - recVersion(NULL), - parentRecVersion(NULL), - parentDbKey(NULL), - parentRequest(NULL) - { - } - -public: - Type getType() const { return type; } - void setType(Type value) { type = value; } - - ULONG getFlags() const { return flags; } - void setFlags(ULONG value) { flags = value; } - void addFlags(ULONG value) { flags |= value; } - - unsigned getBlrVersion() const { return blrVersion; } - void setBlrVersion(unsigned value) { blrVersion = value; } - - Firebird::RefStrPtr& getSqlText() { return sqlText; } - const Firebird::RefStrPtr& getSqlText() const { return sqlText; } - void setSqlText(Firebird::RefString* value) { sqlText = value; } - - void setOrgText(const char* ptr, ULONG len); - const Firebird::string& getOrgText() const { return *orgText; } - - dsql_msg* getSendMsg() { return sendMsg; } - const dsql_msg* getSendMsg() const { return sendMsg; } - void setSendMsg(dsql_msg* value) { sendMsg = value; } - - dsql_msg* getReceiveMsg() { return receiveMsg; } - const dsql_msg* getReceiveMsg() const { return receiveMsg; } - void setReceiveMsg(dsql_msg* value) { receiveMsg = value; } - - dsql_par* getEof() { return eof; } - const dsql_par* getEof() const { return eof; } - void setEof(dsql_par* value) { eof = value; } - - dsql_par* getDbKey() { return dbKey; } - const dsql_par* getDbKey() const { return dbKey; } - void setDbKey(dsql_par* value) { dbKey = value; } - - dsql_par* getRecVersion() { return recVersion; } - const dsql_par* getRecVersion() const { return recVersion; } - void setRecVersion(dsql_par* value) { recVersion = value; } - - dsql_par* getParentRecVersion() { return parentRecVersion; } - const dsql_par* getParentRecVersion() const { return parentRecVersion; } - void setParentRecVersion(dsql_par* value) { parentRecVersion = value; } - - dsql_par* getParentDbKey() { return parentDbKey; } - const dsql_par* getParentDbKey() const { return parentDbKey; } - void setParentDbKey(dsql_par* value) { parentDbKey = value; } - - dsql_req* getParentRequest() const { return parentRequest; } - void setParentRequest(dsql_req* value) { parentRequest = value; } - -private: - Type type; // Type of statement - ULONG flags; // generic flag - unsigned blrVersion; - Firebird::RefStrPtr sqlText; - Firebird::RefStrPtr orgText; - dsql_msg* sendMsg; // Message to be sent to start request - dsql_msg* receiveMsg; // Per record message to be received - dsql_par* eof; // End of file parameter - dsql_par* dbKey; // Database key for current of - dsql_par* recVersion; // Record Version for current of - dsql_par* parentRecVersion; // parent record version - dsql_par* parentDbKey; // Parent database key for current of - dsql_req* parentRequest; // Source request, if cursor update -}; - -class dsql_req : public pool_alloc -{ -public: - explicit dsql_req(MemoryPool& pool); - -public: - MemoryPool& getPool() - { - return req_pool; - } - - jrd_tra* getTransaction() - { - return req_transaction; - } - - const DsqlCompiledStatement* getStatement() const - { - return statement; - } - - virtual bool mustBeReplicated() const - { - return false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult) = 0; - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton) = 0; - - virtual void setCursor(thread_db* tdbb, const TEXT* name); - - virtual bool fetch(thread_db* tdbb, UCHAR* buffer); - - virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); - - // Get session-level timeout, milliseconds - unsigned int getTimeout(); - - // Set session-level timeout, milliseconds - void setTimeout(unsigned int timeOut); - - // Get actual timeout, milliseconds - unsigned int getActualTimeout(); - - // Evaluate actual timeout value, consider config- and session-level timeout values, - // setup and start timer - TimeoutTimer* setupTimer(thread_db* tdbb); - - USHORT parseMetadata(Firebird::IMessageMetadata* meta, const Firebird::Array& parameters_list); - void mapInOut(Jrd::thread_db* tdbb, bool toExternal, const dsql_msg* message, Firebird::IMessageMetadata* meta, - UCHAR* dsql_msg_buf, const UCHAR* in_dsql_msg_buf = NULL); - - static void destroy(thread_db* tdbb, dsql_req* request, bool drop); - -private: - MemoryPool& req_pool; - -public: - const DsqlCompiledStatement* statement; - MemoryPool* liveScratchPool; - Firebird::Array cursors; // Cursor update statements - - dsql_dbb* req_dbb; // DSQL attachment - jrd_tra* req_transaction; // JRD transaction - jrd_req* req_request; // JRD request - - Firebird::Array req_msg_buffers; - Firebird::string req_cursor_name; // Cursor name, if any - DsqlCursor* req_cursor; // Open cursor, if any - DsqlBatch* req_batch; // Active batch, if any - Firebird::GenericMap > req_user_descs; // SQLDA data type - - Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time - SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time - SINT64 req_fetch_rowcount; // Total number of rows returned by this request - bool req_traced; // request is traced via TraceAPI - -protected: - unsigned int req_timeout; // query timeout in milliseconds, set by the user - Firebird::RefPtr req_timer; // timeout timer - - // Request should never be destroyed using delete. - // It dies together with it's pool in release_request(). - ~dsql_req(); - - // To avoid posix warning about missing public destructor declare - // MemoryPool as friend class. In fact IT releases request memory! - friend class Firebird::MemoryPool; -}; - -class DsqlDmlRequest : public dsql_req -{ -public: - explicit DsqlDmlRequest(MemoryPool& pool, StmtNode* aNode) - : dsql_req(pool), - node(aNode), - needDelayedFormat(false), - firstRowFetched(false) - { - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - virtual void setCursor(thread_db* tdbb, const TEXT* name); - - virtual bool fetch(thread_db* tdbb, UCHAR* buffer); - - virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata); - -private: - // True, if request could be restarted - bool needRestarts(); - - void doExecute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - // [Re]start part of "request restarts" algorithm - void executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton, bool exec, bool fetch); - - NestConst node; - bool needDelayedFormat; - bool firstRowFetched; -}; - -class DsqlDdlRequest : public dsql_req -{ -public: - explicit DsqlDdlRequest(MemoryPool& pool, DdlNode* aNode) - : dsql_req(pool), - node(aNode), - internalScratch(NULL) - { - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - - virtual bool mustBeReplicated() const; - -private: - // Rethrow an exception with isc_no_meta_update and prefix codes. - void rethrowDdlException(Firebird::status_exception& ex, bool metadataUpdate); - -private: - NestConst node; - DsqlCompilerScratch* internalScratch; -}; - -class DsqlTransactionRequest : public dsql_req -{ -public: - explicit DsqlTransactionRequest(MemoryPool& pool, TransactionNode* aNode) - : dsql_req(pool), - node(aNode) - { - req_traced = false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - -private: - NestConst node; -}; - -class DsqlSessionManagementRequest : public dsql_req -{ -public: - explicit DsqlSessionManagementRequest(MemoryPool& pool, SessionManagementNode* aNode) - : dsql_req(pool), - node(aNode) - { - req_traced = false; - } - - virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, bool* destroyScratchPool, - ntrace_result_t* traceResult); - - virtual void execute(thread_db* tdbb, jrd_tra** traHandle, - Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, - Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, - bool singleton); - -private: - NestConst node; -}; - //! Implicit (NATURAL and USING) joins class ImplicitJoin : public pool_alloc { @@ -819,8 +486,7 @@ public: Firebird::string ctx_internal_alias; // Alias as specified in query DsqlContextStack ctx_main_derived_contexts; // contexts used for blr_derived_expr DsqlContextStack ctx_childs_derived_table; // Childs derived table context - Firebird::GenericMap > > ctx_imp_join; // Map of USING fieldname to ImplicitJoin + Firebird::LeftPooledMap ctx_imp_join; // Map of USING fieldname to ImplicitJoin Firebird::Array ctx_win_maps; // Maps for window functions Firebird::GenericMap ctx_named_windows; @@ -984,7 +650,7 @@ public: s(p) { } - Firebird::string toUtf8(DsqlCompilerScratch*) const; + Firebird::string toUtf8(jrd_tra* transaction) const; const MetaName& getCharSet() const { diff --git a/src/dsql/dsql_proto.h b/src/dsql/dsql_proto.h index dfd44e6d4d..89a6e2b4c3 100644 --- a/src/dsql/dsql_proto.h +++ b/src/dsql/dsql_proto.h @@ -31,21 +31,19 @@ namespace Jrd { class Attachment; class jrd_tra; - class dsql_req; + class DsqlDmlRequest; + class DsqlRequest; } -void DSQL_execute(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*, +void DSQL_execute(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::DsqlRequest*, Firebird::IMessageMetadata*, const UCHAR*, Firebird::IMessageMetadata*, UCHAR*); void DSQL_execute_immediate(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra**, ULONG, const TEXT*, USHORT, Firebird::IMessageMetadata*, const UCHAR*, Firebird::IMessageMetadata*, UCHAR*, bool); -void DSQL_free_statement(Jrd::thread_db*, Jrd::dsql_req*, USHORT); -Jrd::DsqlCursor* DSQL_open(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*, - Firebird::IMessageMetadata*, const UCHAR*, - Firebird::IMessageMetadata*, ULONG); -Jrd::dsql_req* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*, +void DSQL_free_statement(Jrd::thread_db*, Jrd::DsqlRequest*, USHORT); +Jrd::DsqlRequest* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*, USHORT, unsigned, Firebird::Array*, Firebird::Array*, bool); -void DSQL_sql_info(Jrd::thread_db*, Jrd::dsql_req*, +void DSQL_sql_info(Jrd::thread_db*, Jrd::DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*); #endif // DSQL_DSQL_PROTO_H diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index 627a2c0edd..63536cff5d 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -77,14 +77,9 @@ void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch) * Emit BLR for hidden variables. * **************************************/ - if (dsqlScratch->hiddenVariables.isEmpty()) - return; - for (Array::const_iterator i = dsqlScratch->hiddenVariables.begin(); - i != dsqlScratch->hiddenVariables.end(); - ++i) + for (const auto var : dsqlScratch->hiddenVariables) { - const dsql_var* var = *i; dsqlScratch->appendUChar(blr_dcl_variable); dsqlScratch->appendUShort(var->number); GEN_descriptor(dsqlScratch, &var->desc, true); @@ -225,74 +220,64 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) message->msg_length = offset; - dsqlScratch->ports.add(message); + dsqlScratch->getDsqlStatement()->getPorts().add(message); } // Generate complete blr for a dsqlScratch. -void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) +void GEN_statement(DsqlCompilerScratch* scratch, DmlNode* node) { - DsqlCompiledStatement* statement = scratch->getStatement(); + DsqlStatement* statement = scratch->getDsqlStatement(); if (statement->getBlrVersion() == 4) scratch->appendUChar(blr_version4); else scratch->appendUChar(blr_version5); - if (statement->getType() == DsqlCompiledStatement::TYPE_SAVEPOINT) + const bool block = statement->getType() == DsqlStatement::TYPE_EXEC_BLOCK || + statement->getType() == DsqlStatement::TYPE_SELECT_BLOCK; + + // To parse sub-routines messages, they must not have that begin...end pair. + // And since it appears to be unnecessary for execute block too, do not generate them. + if (!block) + scratch->appendUChar(blr_begin); + + scratch->putOuterMaps(); + GEN_hidden_variables(scratch); + + switch (statement->getType()) { - // Do not generate BEGIN..END block around savepoint statement - // to avoid breaking of savepoint logic - statement->setSendMsg(NULL); - statement->setReceiveMsg(NULL); + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_SELECT_BLOCK: node->genBlr(scratch); - } - else - { - const bool block = statement->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK || - statement->getType() == DsqlCompiledStatement::TYPE_SELECT_BLOCK; + break; - // To parse sub-routines messages, they must not have that begin...end pair. - // And since it appears to be unnecessary for execute block too, do not generate them. - if (!block) - scratch->appendUChar(blr_begin); - - GEN_hidden_variables(scratch); - - switch (statement->getType()) + ///case DsqlStatement::TYPE_RETURNING_CURSOR: + default: { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: - node->genBlr(scratch); - break; - - ///case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - default: + dsql_msg* message = statement->getSendMsg(); + if (!message->msg_parameter) + statement->setSendMsg(NULL); + else { - dsql_msg* message = statement->getSendMsg(); - if (!message->msg_parameter) - statement->setSendMsg(NULL); - else - { - GEN_port(scratch, message); - scratch->appendUChar(blr_receive_batch); - scratch->appendUChar(message->msg_number); - } - message = statement->getReceiveMsg(); - if (!message->msg_parameter) - statement->setReceiveMsg(NULL); - else - GEN_port(scratch, message); - node->genBlr(scratch); + GEN_port(scratch, message); + scratch->appendUChar(blr_receive_batch); + scratch->appendUChar(message->msg_number); } + message = statement->getReceiveMsg(); + if (!message->msg_parameter) + statement->setReceiveMsg(NULL); + else + GEN_port(scratch, message); + node->genBlr(scratch); } - - if (!block) - scratch->appendUChar(blr_end); } + if (!block) + scratch->appendUChar(blr_end); + scratch->appendUChar(blr_eoc); } diff --git a/src/dsql/gen_proto.h b/src/dsql/gen_proto.h index 55cf5fb4fe..6301fda05d 100644 --- a/src/dsql/gen_proto.h +++ b/src/dsql/gen_proto.h @@ -35,7 +35,7 @@ void GEN_expr(Jrd::DsqlCompilerScratch*, Jrd::ExprNode*); void GEN_hidden_variables(Jrd::DsqlCompilerScratch* dsqlScratch); void GEN_parameter(Jrd::DsqlCompilerScratch*, const Jrd::dsql_par*); void GEN_port(Jrd::DsqlCompilerScratch*, Jrd::dsql_msg*); -void GEN_request(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*); +void GEN_statement(Jrd::DsqlCompilerScratch*, Jrd::DmlNode*); void GEN_rse(Jrd::DsqlCompilerScratch*, Jrd::RseNode*); void GEN_sort(Jrd::DsqlCompilerScratch*, UCHAR, Jrd::ValueListNode*); void GEN_stuff_context(Jrd::DsqlCompilerScratch*, const Jrd::dsql_ctx*); diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index 5eddf02f5c..d68df999fb 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -34,7 +34,7 @@ namespace Jrd { class TypeClause; class dsql_msg; class dsql_par; - class dsql_req; + class DsqlRequest; class DsqlCompilerScratch; class IntlString; class ExprNode; diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 9eb1ea8528..bfb47f644e 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -63,13 +63,6 @@ using namespace Firebird; DATABASE DB = STATIC "yachts.lnk"; -static const UCHAR blr_bpb[] = -{ - isc_bpb_version1, - isc_bpb_source_type, 1, isc_blob_blr, - isc_bpb_target_type, 1, isc_blob_blr -}; - static void convert_dtype(TypeClause*, SSHORT); static void free_relation(dsql_rel*); diff --git a/src/dsql/metd_proto.h b/src/dsql/metd_proto.h index b622c18400..fe9faa2c36 100644 --- a/src/dsql/metd_proto.h +++ b/src/dsql/metd_proto.h @@ -34,7 +34,7 @@ namespace Jrd { typedef Firebird::GenericMap MetaNamePairMap; - class dsql_req; + class DsqlRequest; class DsqlCompilerScratch; class jrd_tra; class dsql_intlsym; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 852889bfd5..55e590a41f 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -828,7 +828,7 @@ using namespace Firebird; Jrd::SetTransactionNode::RestrictionOption* setTransactionRestrictionClause; Jrd::DeclareSubProcNode* declareSubProcNode; Jrd::DeclareSubFuncNode* declareSubFuncNode; - Jrd::dsql_req* dsqlReq; + Jrd::DsqlStatement* dsqlStatement; Jrd::CreateAlterUserNode* createAlterUserNode; Jrd::MappingNode* mappingNode; Jrd::MappingNode::OP mappingOp; @@ -848,16 +848,23 @@ using namespace Firebird; // list of possible statements top - : statement { DSQL_parse = $1; } - | statement ';' { DSQL_parse = $1; } + : statement { parsedStatement = $1; } + | statement ';' { parsedStatement = $1; } ; -%type statement +%type statement statement - : dml_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlDmlRequest(getStatementPool(), $1); } - | ddl_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlDdlRequest(getStatementPool(), $1); } - | tra_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlTransactionRequest(getStatementPool(), $1); } - | mng_statement { $$ = FB_NEW_POOL(getStatementPool()) DsqlSessionManagementRequest(getStatementPool(), $1); } + : dml_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlDmlStatement(*statementPool, scratch->getAttachment(), $1); } + | ddl_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlDdlStatement(*statementPool, scratch->getAttachment(), $1); } + | tra_statement + { $$ = FB_NEW_POOL(*statementPool) DsqlTransactionStatement(*statementPool, scratch->getAttachment(), $1); } + | mng_statement + { + $$ = FB_NEW_POOL(*statementPool) DsqlSessionManagementStatement( + *statementPool, scratch->getAttachment(), $1); + } ; %type dml_statement @@ -1002,7 +1009,7 @@ grant0($node) | db_ddl_privileges(NOTRIAL(&$node->privileges)) DATABASE TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by { - $node->object = newNode(obj_database, get_object_name(obj_database)); + $node->object = newNode(obj_database, getSecurityClassName(obj_database)); $node->grantAdminOption = $5; $node->grantor = $6; $node->isDdl = true; @@ -1018,31 +1025,31 @@ grant0($node) %type object object : TABLE - { $$ = newNode(obj_relations, get_object_name(obj_relations)); } + { $$ = newNode(obj_relations, getSecurityClassName(obj_relations)); } | VIEW - { $$ = newNode(obj_views, get_object_name(obj_views)); } + { $$ = newNode(obj_views, getSecurityClassName(obj_views)); } | PROCEDURE - { $$ = newNode(obj_procedures, get_object_name(obj_procedures)); } + { $$ = newNode(obj_procedures, getSecurityClassName(obj_procedures)); } | FUNCTION - { $$ = newNode(obj_functions, get_object_name(obj_functions)); } + { $$ = newNode(obj_functions, getSecurityClassName(obj_functions)); } | PACKAGE - { $$ = newNode(obj_packages, get_object_name(obj_packages)); } + { $$ = newNode(obj_packages, getSecurityClassName(obj_packages)); } | GENERATOR - { $$ = newNode(obj_generators, get_object_name(obj_generators)); } + { $$ = newNode(obj_generators, getSecurityClassName(obj_generators)); } | SEQUENCE - { $$ = newNode(obj_generators, get_object_name(obj_generators)); } + { $$ = newNode(obj_generators, getSecurityClassName(obj_generators)); } | DOMAIN - { $$ = newNode(obj_domains, get_object_name(obj_domains)); } + { $$ = newNode(obj_domains, getSecurityClassName(obj_domains)); } | EXCEPTION - { $$ = newNode(obj_exceptions, get_object_name(obj_exceptions)); } + { $$ = newNode(obj_exceptions, getSecurityClassName(obj_exceptions)); } | ROLE - { $$ = newNode(obj_roles, get_object_name(obj_roles)); } + { $$ = newNode(obj_roles, getSecurityClassName(obj_roles)); } | CHARACTER SET - { $$ = newNode(obj_charsets, get_object_name(obj_charsets)); } + { $$ = newNode(obj_charsets, getSecurityClassName(obj_charsets)); } | COLLATION - { $$ = newNode(obj_collations, get_object_name(obj_collations)); } + { $$ = newNode(obj_collations, getSecurityClassName(obj_collations)); } | FILTER - { $$ = newNode(obj_filters, get_object_name(obj_filters)); } + { $$ = newNode(obj_filters, getSecurityClassName(obj_filters)); } ; table_noise @@ -1259,7 +1266,7 @@ revoke0($node) | rev_grant_option db_ddl_privileges(NOTRIAL(&$node->privileges)) DATABASE FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by { - $node->object = newNode(obj_database, get_object_name(obj_database)); + $node->object = newNode(obj_database, getSecurityClassName(obj_database)); $node->grantAdminOption = $1; $node->grantor = $6; $node->isDdl = true; @@ -7743,7 +7750,7 @@ sql_string %type utf_string utf_string : sql_string - { $$ = newString($1->toUtf8(scratch)); } + { $$ = newString($1->toUtf8(scratch->getTransaction())); } ; %type signed_short_integer diff --git a/src/extlib/ib_util.cpp b/src/extlib/ib_util.cpp index 114825ae85..614fc96d2d 100644 --- a/src/extlib/ib_util.cpp +++ b/src/extlib/ib_util.cpp @@ -19,21 +19,19 @@ */ #include -#include "ib_util.h" #include "firebird.h" - -typedef void* VoidPtr; +#include "ibase.h" // initialized by the engine static void* (*allocFunc)(long) = NULL; -extern "C" void FB_EXPORTED ib_util_init(void* (*aAllocFunc)(long)) +extern "C" FB_DLL_EXPORT void ib_util_init(void* (*aAllocFunc)(long)) { allocFunc = aAllocFunc; } -extern "C" VoidPtr FB_EXPORTED ib_util_malloc(long size) +extern "C" FB_DLL_EXPORT void* ib_util_malloc(long size) { return allocFunc ? allocFunc(size) : malloc(size); } diff --git a/src/gpre/int_cxx.cpp b/src/gpre/int_cxx.cpp index 87d8345c03..fabd63c477 100644 --- a/src/gpre/int_cxx.cpp +++ b/src/gpre/int_cxx.cpp @@ -583,7 +583,7 @@ static void gen_send( const gpre_req* request, const gpre_port* port, int column align(column); fprintf(gpreGlob.out_file, "if (ignore_perm)"); align(column); - fprintf(gpreGlob.out_file, "\trequest->getStatement()->flags |= JrdStatement::FLAG_IGNORE_PERM;"); + fprintf(gpreGlob.out_file, "\trequest->getStatement()->flags |= Statement::FLAG_IGNORE_PERM;"); } align(column); diff --git a/src/include/cross/android.arm64 b/src/include/cross/android.arm64 index 4a4e30160f..825570e26b 100644 --- a/src/include/cross/android.arm64 +++ b/src/include/cross/android.arm64 @@ -61,9 +61,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/cross/android.arme b/src/include/cross/android.arme index 70973d5cdf..5dcdba3a8b 100644 --- a/src/include/cross/android.arme +++ b/src/include/cross/android.arme @@ -67,9 +67,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/cross/android.x86 b/src/include/cross/android.x86 index 73bb83c51e..47503e2661 100644 --- a/src/include/cross/android.x86 +++ b/src/include/cross/android.x86 @@ -61,9 +61,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/cross/android.x86_64 b/src/include/cross/android.x86_64 index 2910dd421a..cc5d8844ed 100644 --- a/src/include/cross/android.x86_64 +++ b/src/include/cross/android.x86_64 @@ -61,9 +61,6 @@ /* message files DIR (PREFIX) */ #define FB_MSGDIR "" -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* plugins DIR (PREFIX) */ #define FB_PLUGDIR "" diff --git a/src/include/firebird.h b/src/include/firebird.h index 1bd7f2d4b3..391d7707ce 100644 --- a/src/include/firebird.h +++ b/src/include/firebird.h @@ -43,13 +43,6 @@ #define DEBUG_GDS_ALLOC #endif -#if defined(WIN_NT) -#define FB_DLL_EXPORT __declspec(dllexport) -#elif defined(DARWIN) -#define FB_DLL_EXPORT API_ROUTINE -#else -#define FB_DLL_EXPORT -#endif //#if defined(SOLX86) // this pragmas is used only with gcc 2.95! //#define __PRAGMA_REDEFINE_EXTNAME diff --git a/src/include/firebird/UdrCppEngine.h b/src/include/firebird/UdrCppEngine.h index 26ef5a0e90..d9aac7e5d3 100644 --- a/src/include/firebird/UdrCppEngine.h +++ b/src/include/firebird/UdrCppEngine.h @@ -43,7 +43,7 @@ } \ } \ \ - extern "C" FB_BOOLEAN* FB_UDR_PLUGIN_ENTRY_POINT(::Firebird::IStatus* status, \ + extern "C" FB_DLL_EXPORT FB_BOOLEAN* FB_UDR_PLUGIN_ENTRY_POINT(::Firebird::IStatus* status, \ FB_BOOLEAN* theirUnloadFlag, ::Firebird::IUdrPlugin* udrPlugin) \ { \ ::Firebird::Udr::FactoryRegistration::finish(status, udrPlugin); \ diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index 9b61310e0c..3ad0d2a650 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -193,8 +193,8 @@ #define blr_agg_min (unsigned char)85 #define blr_agg_total (unsigned char)86 #define blr_agg_average (unsigned char)87 -#define blr_parameter3 (unsigned char)88 /* same as Rdb definition */ /* unsupported +#define blr_parameter3 (unsigned char)88 #define blr_run_max (unsigned char)89 #define blr_run_min (unsigned char)90 #define blr_run_total (unsigned char)91 @@ -221,7 +221,7 @@ // unused codes: 111..117 -#define blr_run_count (unsigned char)118 /* changed from 88 to avoid conflict with blr_parameter3 */ +///#define blr_run_count (unsigned char)118 #define blr_rs_stream (unsigned char)119 #define blr_exec_proc (unsigned char)120 @@ -452,4 +452,8 @@ #define blr_local_table_truncate (unsigned char) 219 #define blr_local_table_id (unsigned char) 220 +#define blr_outer_map (unsigned char) 221 +#define blr_outer_map_message (unsigned char) 1 +#define blr_outer_map_variable (unsigned char) 2 + #endif // FIREBIRD_IMPL_BLR_H diff --git a/src/include/firebird/impl/consts_pub.h b/src/include/firebird/impl/consts_pub.h index 2802d23c08..1cdc2fe54e 100644 --- a/src/include/firebird/impl/consts_pub.h +++ b/src/include/firebird/impl/consts_pub.h @@ -128,6 +128,7 @@ #define isc_dpb_set_bind 93 #define isc_dpb_decfloat_round 94 #define isc_dpb_decfloat_traps 95 +#define isc_dpb_clear_map 96 /**************************************************/ @@ -160,8 +161,7 @@ ::= "TCPv4" | "TCPv6" | - "XNET" | - "WNET" | + "XNET" .... ::= diff --git a/src/include/firebird/impl/msg/dsql.h b/src/include/firebird/impl/msg/dsql.h index c909b42ed7..54a5339418 100644 --- a/src/include/firebird/impl/msg/dsql.h +++ b/src/include/firebird/impl/msg/dsql.h @@ -19,7 +19,7 @@ FB_IMPL_MSG(DSQL, 19, dsql_cursor_not_found, -502, "34", "000", "Cursor @1 is no FB_IMPL_MSG(DSQL, 20, dsql_cursor_exists, -502, "24", "000", "Cursor @1 already exists in the current context") FB_IMPL_MSG(DSQL, 21, dsql_cursor_rel_ambiguous, -502, "34", "000", "Relation @1 is ambiguous in cursor @2") FB_IMPL_MSG(DSQL, 22, dsql_cursor_rel_not_found, -502, "34", "000", "Relation @1 is not found in cursor @2") -FB_IMPL_MSG(DSQL, 23, dsql_cursor_not_open, -502, "24", "000", "Cursor is not open") +FB_IMPL_MSG(DSQL, 23, dsql_cursor_not_open, -504, "24", "000", "Cursor is not open") FB_IMPL_MSG(DSQL, 24, dsql_type_not_supp_ext_tab, -607, "HY", "004", "Data type @1 is not supported for EXTERNAL TABLES. Relation '@2', field '@3'") FB_IMPL_MSG(DSQL, 25, dsql_feature_not_supported_ods, -804, "0A", "000", "Feature not supported on ODS version older than @1.@2") FB_IMPL_MSG(DSQL, 26, primary_key_required, -660, "22", "000", "Primary key required on table @1") diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 33b6f9831f..808e2d6f56 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -956,3 +956,5 @@ FB_IMPL_MSG(JRD, 954, tom_key_length, -901, "22", "023", "Invalid key length @1, FB_IMPL_MSG(JRD, 955, inf_invalid_args, -901, "HY", "000", "Invalid information arguments") FB_IMPL_MSG(JRD, 956, sysf_invalid_null_empty, -901, "22", "023", "Empty or NULL parameter @1 is not accepted") FB_IMPL_MSG(JRD, 957, bad_loctab_num, -901, "HY", "000", "Undefined local table number @1") +FB_IMPL_MSG(JRD, 958, quoted_str_bad, -901, "22", "024", "Invalid text <@1> after quoted string") +FB_IMPL_MSG(JRD, 959, quoted_str_miss, -901, "22", "024", "Missing terminating quote <@1> in the end of quoted string") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 0cd751cc9b..ef262aa6c0 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -3853,6 +3853,7 @@ const isc_dpb_set_bind = byte(93); isc_dpb_decfloat_round = byte(94); isc_dpb_decfloat_traps = byte(95); + isc_dpb_clear_map = byte(96); isc_dpb_address = byte(1); isc_dpb_addr_protocol = byte(1); isc_dpb_addr_endpoint = byte(2); @@ -5156,6 +5157,8 @@ const isc_inf_invalid_args = 335545275; isc_sysf_invalid_null_empty = 335545276; isc_bad_loctab_num = 335545277; + isc_quoted_str_bad = 335545278; + isc_quoted_str_miss = 335545279; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/include/gen/autoconfig.h.in b/src/include/gen/autoconfig.h.in index d3d6ac8161..de0489a3bd 100644 --- a/src/include/gen/autoconfig.h.in +++ b/src/include/gen/autoconfig.h.in @@ -94,9 +94,6 @@ /* log file name within log dir */ #define FB_LOGFILENAME "@FB_LOGFILENAME@" -/* Wnet pipe name */ -#define FB_PIPE_NAME "@FB_PIPE_NAME@" - /* Installation path prefix */ #define FB_PREFIX "@FB_PREFIX@" diff --git a/src/include/gen/autoconfig_msvc.h b/src/include/gen/autoconfig_msvc.h index d7ba71254b..1e25478c64 100644 --- a/src/include/gen/autoconfig_msvc.h +++ b/src/include/gen/autoconfig_msvc.h @@ -285,9 +285,6 @@ #define FB_SERVICE_NAME "gds_db" #define FB_SERVICE_PORT 3050 -/* Wnet pipe name */ -#define FB_PIPE_NAME "interbas" - /* Xnet objects name */ #define FB_IPC_NAME "FIREBIRD" diff --git a/src/include/ibase.h b/src/include/ibase.h index 6f7e593b2c..957afbb2d3 100644 --- a/src/include/ibase.h +++ b/src/include/ibase.h @@ -64,6 +64,18 @@ #define FB_API_DEPRECATED #endif +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +#define FB_DLL_EXPORT __declspec(dllexport) +#elif defined __has_attribute +#if __has_attribute (visibility) +#define FB_DLL_EXPORT __attribute__ ((visibility("default"))) +#else +#define FB_DLL_EXPORT +#endif +#else +#define FB_DLL_EXPORT +#endif + #include "./firebird/impl/types_pub.h" /***********************/ diff --git a/src/intl/lc_ascii.cpp b/src/intl/lc_ascii.cpp index 22cc9b9d9d..e70eadd900 100644 --- a/src/intl/lc_ascii.cpp +++ b/src/intl/lc_ascii.cpp @@ -523,8 +523,6 @@ USHORT famasc_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInChar, U { fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); - fb_assert(iInLen <= LANGASCII_MAX_KEY); - fb_assert(iOutLen <= LANGASCII_MAX_KEY); fb_assert(iOutLen >= famasc_key_length(obj, iInLen)); // point inbuff at last character diff --git a/src/intl/lc_icu.cpp b/src/intl/lc_icu.cpp index e5eced3a14..2b6e157136 100644 --- a/src/intl/lc_icu.cpp +++ b/src/intl/lc_icu.cpp @@ -51,15 +51,11 @@ static bool texttype_default_init(texttype* tt, ULONG specificAttributesLength) //const ASCII* configInfo) { - charset cs; + AutoPtr cs(FB_NEW charset); memset(&cs, 0, sizeof(cs)); // test if that ICU charset exist - if (CSICU_charset_init(&cs, charSetName)) - { - IntlUtil::finiCharset(&cs); - } - else + if (!CSICU_charset_init(cs, charSetName)) return false; if ((attributes & ~TEXTTYPE_ATTR_PAD_SPACE) || @@ -72,7 +68,7 @@ static bool texttype_default_init(texttype* tt, } // name comes from stack. Copy it. - ASCII* p = FB_NEW_POOL(*getDefaultMemoryPool()) ASCII[strlen(name) + 1]; + ASCII* p = FB_NEW ASCII[strlen(name) + 1]; strcpy(p, name); tt->texttype_name = p; @@ -93,23 +89,24 @@ static bool texttype_unicode_init(texttype* tt, ULONG specificAttributesLength, const ASCII* configInfo) { - charset* cs = FB_NEW_POOL(*getDefaultMemoryPool()) charset; + AutoPtr cs(FB_NEW charset); memset(cs, 0, sizeof(*cs)); // test if that charset exist if (!LD_lookup_charset(cs, charSetName, configInfo)) - { - Firebird::SimpleDelete::clear(cs); return false; - } Firebird::UCharBuffer specificAttributesBuffer; memcpy(specificAttributesBuffer.getBuffer(specificAttributesLength), specificAttributes, specificAttributesLength); - // ASF: Don't free "cs". It'will be used in the collation. - return Firebird::IntlUtil::initUnicodeCollation(tt, cs, name, + auto ret = Firebird::IntlUtil::initUnicodeCollation(tt, cs, name, attributes, specificAttributesBuffer, configInfo); + + // ASF: Don't free "cs". It'will be used in the collation. + cs.release(); + + return ret; } @@ -120,7 +117,7 @@ bool LCICU_setup_attributes(const ASCII* name, const ASCII* charSetName, const A if (len > 8 && strcmp(name + len - 8, "_UNICODE") == 0) { - AutoPtr cs(FB_NEW_POOL(*getDefaultMemoryPool()) charset); + AutoPtr cs(FB_NEW charset); memset(cs, 0, sizeof(*cs)); // test if that charset exist diff --git a/src/intl/lc_ksc.cpp b/src/intl/lc_ksc.cpp index 54ed5fc8d1..e24d5a1869 100644 --- a/src/intl/lc_ksc.cpp +++ b/src/intl/lc_ksc.cpp @@ -139,8 +139,6 @@ static USHORT LCKSC_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInC { fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); - fb_assert(iInLen <= LANGKSC_MAX_KEY); - fb_assert(iOutLen <= LANGKSC_MAX_KEY); fb_assert(iOutLen >= LCKSC_key_length(obj, iInLen)); const BYTE* inbuff = pInChar + iInLen - 1; diff --git a/src/intl/lc_narrow.cpp b/src/intl/lc_narrow.cpp index f4e4943e4d..f391f7fc5b 100644 --- a/src/intl/lc_narrow.cpp +++ b/src/intl/lc_narrow.cpp @@ -168,8 +168,6 @@ USHORT LC_NARROW_string_to_key(texttype* obj, USHORT iInLen, const BYTE* pInChar { fb_assert(pOutChar != NULL); fb_assert(pInChar != NULL); - // fb_assert (iInLen <= LANGFAM2_MAX_KEY); - fb_assert(iOutLen <= LANGFAM2_MAX_KEY); fb_assert(iOutLen >= LC_NARROW_key_length(obj, iInLen)); TextTypeImpl* impl = static_cast(obj->texttype_impl); diff --git a/src/intl/ld.cpp b/src/intl/ld.cpp index 1056921ee3..eaf8f0f22e 100644 --- a/src/intl/ld.cpp +++ b/src/intl/ld.cpp @@ -28,6 +28,7 @@ #include "../intl/ld_proto.h" #include "../intl/cs_icu.h" #include "../intl/lc_icu.h" +#include "../common/utils_proto.h" #include "fb_exception.h" #ifdef HAVE_SYS_PARAM_H @@ -468,7 +469,7 @@ struct }; -INTL_BOOL FB_DLL_EXPORT LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* /*config_info*/) +FB_DLL_EXPORT INTL_BOOL LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* /*config_info*/) { // ASF: We can't read config_info if version < INTL_VERSION_2, // since it wasn't pushed in the stack by the engine. @@ -491,20 +492,12 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_charset(charset* cs, const ASCII* name, const } -INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info) +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype_with_status(char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info) { - const ASCII* configInfo; - - // ASF: We can't read config_info if version < INTL_VERSION_2, - // since it wasn't pushed in the stack by the engine. - if (version >= INTL_VERSION_2) - configInfo = config_info; - else - configInfo = ""; - if (ignore_attributes) { attributes = TEXTTYPE_ATTR_PAD_SPACE; @@ -512,6 +505,8 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_n specific_attributes_length = 0; } + string errorMsg; + try { for (int i = 0; collations[i].collationName; ++i) @@ -527,7 +522,7 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_n { if (strcmp(charSets[j].charSetName, charset_name) == 0) { - if (LD_lookup_charset(&cs, charset_name, configInfo)) + if (LD_lookup_charset(&cs, charset_name, config_info)) break; return false; @@ -547,17 +542,54 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_n return LCICU_texttype_init( tt, texttype_name, charset_name, attributes, specific_attributes, - specific_attributes_length, configInfo); + specific_attributes_length, config_info); } - catch (const Firebird::BadAlloc&) + catch (const Firebird::status_exception& ex) + { + auto status = ex.value(); + TEXT temp[BUFFER_LARGE]; + + while (fb_interpret(temp, sizeof(temp), &status)) + { + if (errorMsg.hasData()) + errorMsg += "\n-"; + + errorMsg += temp; + } + } + catch (...) { fb_assert(false); - return false; + errorMsg = "Uncaught exception"; } + + if (status_buffer_length) + fb_utils::copy_terminate(status_buffer, errorMsg.c_str(), status_buffer_length); + + return false; } -ULONG FB_DLL_EXPORT LD_setup_attributes( +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info) +{ + const ASCII* configInfo; + + // ASF: We can't read config_info if version < INTL_VERSION_2, + // since it wasn't pushed in the stack by the engine. + if (version >= INTL_VERSION_2) + configInfo = config_info; + else + configInfo = ""; + + return LD_lookup_texttype_with_status(nullptr, 0, tt, texttype_name, charset_name, attributes, specific_attributes, + specific_attributes_length, ignore_attributes, configInfo); +} + + +FB_DLL_EXPORT ULONG LD_setup_attributes( const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst) { @@ -583,7 +615,7 @@ ULONG FB_DLL_EXPORT LD_setup_attributes( } -void FB_DLL_EXPORT LD_version(USHORT* version) +FB_DLL_EXPORT void LD_version(USHORT* version) { // We support version 1 and 2. if (*version != INTL_VERSION_1) diff --git a/src/intl/ld.h b/src/intl/ld.h index 0ee001f733..0fc0237bad 100644 --- a/src/intl/ld.h +++ b/src/intl/ld.h @@ -55,14 +55,6 @@ #define UINT16 USHORT -#if defined(WIN_NT) -#define FB_DLL_EXPORT __declspec(dllexport) -#elif defined(DARWIN) -#define FB_DLL_EXPORT API_ROUTINE -#else -#define FB_DLL_EXPORT -#endif - /* Following this line is LD.H from Borland Language Driver Kit */ diff --git a/src/intl/ld_proto.h b/src/intl/ld_proto.h index 6a6636dfa1..20f50d193f 100644 --- a/src/intl/ld_proto.h +++ b/src/intl/ld_proto.h @@ -38,13 +38,21 @@ struct CsConvertImpl extern USHORT version; -INTL_BOOL FB_DLL_EXPORT LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* config_info); -INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info); -void FB_DLL_EXPORT LD_version(USHORT* version); -ULONG FB_DLL_EXPORT LD_setup_attributes( +FB_DLL_EXPORT INTL_BOOL LD_lookup_charset(charset* cs, const ASCII* name, const ASCII* config_info); + +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype_with_status(char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info); + +FB_DLL_EXPORT INTL_BOOL LD_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info); + +FB_DLL_EXPORT void LD_version(USHORT* version); +FB_DLL_EXPORT ULONG LD_setup_attributes( const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst); diff --git a/src/isql/InputDevices.cpp b/src/isql/InputDevices.cpp index 24e8ae1e08..faae0a84e7 100644 --- a/src/isql/InputDevices.cpp +++ b/src/isql/InputDevices.cpp @@ -25,6 +25,8 @@ #if defined(DARWIN) && !defined(IOS) #if defined(i386) || defined(__x86_64__) #include +#elif defined(__aarch64__) +#include #else #include #endif diff --git a/src/isql/extract.epp b/src/isql/extract.epp index af51723ab9..31ca3d2cce 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -519,8 +519,24 @@ int EXTRACT_list_table(const SCHAR* relation_name, (RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_BY_DEFAULT ? "BY DEFAULT" : RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_ALWAYS ? "ALWAYS" : "")); - if (!GEN.RDB$INITIAL_VALUE.NULL && GEN.RDB$INITIAL_VALUE != 0) - isqlGlob.printf(" (START WITH %" SQUADFORMAT ")", GEN.RDB$INITIAL_VALUE); + const bool printInitial = !GEN.RDB$INITIAL_VALUE.NULL && GEN.RDB$INITIAL_VALUE != 0; + const bool printIncrement = !GEN.RDB$GENERATOR_INCREMENT.NULL && GEN.RDB$GENERATOR_INCREMENT != 1; + + if (printInitial || printIncrement) + { + isqlGlob.printf(" ("); + + if (printInitial) + { + isqlGlob.printf("START WITH %" SQUADFORMAT "%s", + GEN.RDB$INITIAL_VALUE, (printIncrement ? " " : "")); + } + + if (printIncrement) + isqlGlob.printf("INCREMENT %" SLONGFORMAT, GEN.RDB$GENERATOR_INCREMENT); + + isqlGlob.printf(")"); + } } END_FOR ON_ERROR @@ -1404,10 +1420,13 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi ***/ // Process DDL permissions - for (int i = obj_database; i < obj_type_MAX; i++) + for (SSHORT obj = obj_database; obj < obj_type_MAX; obj++) { + if (!isDdlObject(obj)) + continue; + const processing_state rc = - SHOW_grants2(get_object_name(i), terminator, i, first ? banner : 0, mangle); + SHOW_grants2(getSecurityClassName(obj), terminator, obj, first ? banner : 0, mangle); if (rc == SKIP) first = false; } diff --git a/src/isql/show.epp b/src/isql/show.epp index c4511d11ed..85a6a76168 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -655,7 +655,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle, } -processing_state SHOW_grants(const SCHAR* object, const SCHAR* terminator, USHORT obj_type) +processing_state SHOW_grants(const SCHAR* object, const SCHAR* terminator, ObjectType obj_type) { /************************************** * @@ -786,7 +786,7 @@ static void set_grantee(int user_type, const char* SQL_identifier, char* user_st processing_state SHOW_grants2 (const SCHAR* object, const SCHAR* terminator, - USHORT obj_type, + ObjectType obj_type, const TEXT* optional_msg, bool mangle) { @@ -837,7 +837,7 @@ processing_state SHOW_grants2 (const SCHAR* object, USHORT priv_flags = 0; SSHORT prev_field_null = -1; - if (obj_type == obj_relation || obj_type == 255) + if (obj_type == obj_relation || obj_type == obj_any) { // Find the user specified relation and show its privileges @@ -1020,7 +1020,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } // No relation called "object" was found, try procedure "object" - if (obj_type == obj_procedure || obj_type == 255) + if (obj_type == obj_procedure || obj_type == obj_any) { FOR FIRST 1 P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ object AND @@ -1099,7 +1099,7 @@ processing_state SHOW_grants2 (const SCHAR* object, // No procedure called "object" was found, try role "object" SCHAR role_name[BUFFER_LENGTH256]; - if (obj_type == obj_sql_role || obj_type == 255) + if (obj_type == obj_sql_role || obj_type == obj_any) { // No procedure called "object" was found, try role "object" // CVC: This code could be superseded by SHOW_grant_roles() below @@ -1164,7 +1164,7 @@ processing_state SHOW_grants2 (const SCHAR* object, return (SKIP); } - if (obj_type == obj_package_header || obj_type == 255) + if (obj_type == obj_package_header || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1238,7 +1238,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } } - if (obj_type == obj_udf || obj_type == 255) + if (obj_type == obj_udf || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1316,7 +1316,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } } - if (obj_type == obj_generator || obj_type == 255) + if (obj_type == obj_generator || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1390,7 +1390,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } } - if (obj_type == obj_exception || obj_type == 255) + if (obj_type == obj_exception || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1688,7 +1688,7 @@ processing_state SHOW_grants2 (const SCHAR* object, } ***/ - if (obj_type >= obj_database || obj_type == 255) + if (isDdlObject(obj_type) || obj_type == obj_any) { if (isqlGlob.major_ods >= ODS_VERSION12) { @@ -1698,6 +1698,10 @@ processing_state SHOW_grants2 (const SCHAR* object, PRV.RDB$RELATION_NAME EQ object SORTED BY PRV.RDB$USER, PRV.RDB$GRANT_OPTION + // Double check if the object is DDL one. + if (!isDdlObject(PRV.RDB$OBJECT_TYPE)) + continue; + if (first && optional_msg) isqlGlob.prints(optional_msg); @@ -1753,60 +1757,7 @@ processing_state SHOW_grants2 (const SCHAR* object, set_grantee(PRV.RDB$USER_TYPE, SQL_identifier, user_string); - switch (PRV.RDB$OBJECT_TYPE) - { - case obj_database: - strcpy(obj_string, "DATABASE"); - break; - - case obj_relations: - strcpy(obj_string, "TABLE"); - break; - - case obj_views: - strcpy(obj_string, "VIEW"); - break; - - case obj_procedures: - strcpy(obj_string, "PROCEDURE"); - break; - - case obj_functions: - strcpy(obj_string, "FUNCTION"); - break; - - case obj_packages: - strcpy(obj_string, "PACKAGE"); - break; - - case obj_generators: - strcpy(obj_string, "GENERATOR"); - break; - - case obj_domains: - strcpy(obj_string, "DOMAIN"); - break; - - case obj_exceptions: - strcpy(obj_string, "EXCEPTION"); - break; - - case obj_roles: - strcpy(obj_string, "ROLE"); - break; - - case obj_charsets: - strcpy(obj_string, "CHARACTER SET"); - break; - - case obj_collations: - strcpy(obj_string, "COLLATION"); - break; - - case obj_filters: - strcpy(obj_string, "FILTER"); - break; - } + strcpy(obj_string, getDdlObjectName(PRV.RDB$OBJECT_TYPE)); if (PRV.RDB$GRANT_OPTION) strcpy(with_option, " WITH GRANT OPTION"); @@ -1824,7 +1775,7 @@ processing_state SHOW_grants2 (const SCHAR* object, return ps_ERR; END_ERROR - if (obj_type == obj_database || obj_type == 255) + if (obj_type == obj_database || obj_type == obj_any) { FOR PRV IN SEC$DB_CREATORS SORTED BY PRV.SEC$USER_TYPE, PRV.SEC$USER @@ -2448,7 +2399,7 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) } else strcpy(SQL_id_for_grant, cmd[2]); - ret = SHOW_grants(SQL_id_for_grant, "", 255); + ret = SHOW_grants(SQL_id_for_grant, "", obj_any); } else { diff --git a/src/isql/show_proto.h b/src/isql/show_proto.h index 0184db3b39..8a03884be2 100644 --- a/src/isql/show_proto.h +++ b/src/isql/show_proto.h @@ -26,11 +26,12 @@ #include "../common/classes/fb_string.h" #include +#include "../jrd/obj.h" void SHOW_comments(bool force); bool SHOW_dbb_parameters (Firebird::IAttachment*, SCHAR*, const UCHAR*, unsigned, bool, const char*); -processing_state SHOW_grants (const SCHAR*, const SCHAR*, USHORT); -processing_state SHOW_grants2 (const SCHAR*, const SCHAR*, USHORT, const TEXT*, bool); +processing_state SHOW_grants (const SCHAR*, const SCHAR*, ObjectType); +processing_state SHOW_grants2 (const SCHAR*, const SCHAR*, ObjectType, const TEXT*, bool); void SHOW_grant_roles (const SCHAR*, bool*); void SHOW_grant_roles2 (const SCHAR*, bool*, const TEXT*, bool); void SHOW_print_metadata_text_blob(FILE*, ISC_QUAD*, bool escape_squote = false, diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index d3f5530447..fcb946a9b6 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -145,6 +145,8 @@ void Jrd::Attachment::destroy(Attachment* const attachment) MemoryPool* Jrd::Attachment::createPool() { MemoryPool* const pool = MemoryPool::createPool(att_pool, att_memory_stats); + auto stats = FB_NEW_POOL(*pool) MemoryStats(&att_memory_stats); + pool->setStatsGroup(*stats); att_pools.add(pool); return pool; } @@ -154,9 +156,7 @@ void Jrd::Attachment::deletePool(MemoryPool* pool) { if (pool) { - FB_SIZE_T pos; - if (att_pools.find(pool, pos)) - att_pools.remove(pos); + att_pools.findAndRemove(pool); #ifdef DEBUG_LCK_LIST // hvlad: this could be slow, use only when absolutely necessary @@ -225,6 +225,7 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb, JProvider* provider att_ss_user(NULL), att_user_ids(*pool), att_active_snapshots(*pool), + att_statements(*pool), att_requests(*pool), att_lock_owner_id(Database::getLockOwnerId()), att_backup_state_counter(0), @@ -645,7 +646,7 @@ bool Attachment::hasActiveRequests() const for (const jrd_tra* transaction = att_transactions; transaction; transaction = transaction->tra_next) { - for (const jrd_req* request = transaction->tra_requests; + for (const Request* request = transaction->tra_requests; request; request = request->req_tra_next) { if (request->req_transaction && (request->req_flags & req_active)) @@ -658,7 +659,7 @@ bool Attachment::hasActiveRequests() const // Find an inactive incarnation of a system request. If necessary, clone it. -jrd_req* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT which) +Request* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT which) { static const int MAX_RECURSION = 100; @@ -668,7 +669,7 @@ jrd_req* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT w fb_assert(which == IRQ_REQUESTS || which == DYN_REQUESTS); - JrdStatement* statement = (which == IRQ_REQUESTS ? att_internal[id] : att_dyn_req[id]); + Statement* statement = (which == IRQ_REQUESTS ? att_internal[id] : att_dyn_req[id]); if (!statement) return NULL; @@ -684,7 +685,7 @@ jrd_req* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT w // Msg363 "request depth exceeded. (Recursive definition?)" } - jrd_req* clone = statement->getRequest(tdbb, n); + Request* clone = statement->getRequest(tdbb, n); if (!(clone->req_flags & (req_active | req_reserved))) { @@ -845,13 +846,13 @@ void Jrd::Attachment::releaseLocks(thread_db* tdbb) // And release the system requests - for (JrdStatement** itr = att_internal.begin(); itr != att_internal.end(); ++itr) + for (Statement** itr = att_internal.begin(); itr != att_internal.end(); ++itr) { if (*itr) (*itr)->release(tdbb); } - for (JrdStatement** itr = att_dyn_req.begin(); itr != att_dyn_req.end(); ++itr) + for (Statement** itr = att_dyn_req.begin(); itr != att_dyn_req.end(); ++itr) { if (*itr) (*itr)->release(tdbb); diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 563940ffd6..7be7939088 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -68,7 +68,7 @@ namespace Jrd class thread_db; class Database; class jrd_tra; - class jrd_req; + class Request; class Lock; class jrd_file; class Format; @@ -94,7 +94,7 @@ namespace Jrd class Trigger; class TrigVector; class Function; - class JrdStatement; + class Statement; class Validation; class Applier; @@ -545,7 +545,8 @@ private: StableAttachmentPart* att_stable; public: - Firebird::SortedArray att_requests; // Requests belonging to attachment + Firebird::SortedArray att_statements; // Statements belonging to attachment + Firebird::SortedArray att_requests; // Requests belonging to attachment Lock* att_id_lock; // Attachment lock (if any) AttNumber att_attachment_id; // Attachment ID Lock* att_cancel_lock; // Lock to cancel the active request @@ -619,12 +620,12 @@ public: Firebird::Array att_functions; // User defined functions GeneratorFinder att_generators; - Firebird::Array att_internal; // internal statements - Firebird::Array att_dyn_req; // internal dyn statements + Firebird::Array att_internal; // internal statements + Firebird::Array att_dyn_req; // internal dyn statements Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt Firebird::DecimalStatus att_dec_status; // error handling and rounding - jrd_req* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); + Request* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); Firebird::Array att_charsets; // intl character set descriptions Firebird::GenericMapgetRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); delete impure->table; @@ -112,7 +112,7 @@ bool ConfigTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation, RecordBuffer* ConfigTableScan::getRecords(thread_db* tdbb, jrd_rel* relation) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->table) diff --git a/src/jrd/ConfigTable.h b/src/jrd/ConfigTable.h index 24df4bfd1f..61601b7fde 100644 --- a/src/jrd/ConfigTable.h +++ b/src/jrd/ConfigTable.h @@ -45,7 +45,7 @@ private: }; -class ConfigTableScan : public VirtualTableScan +class ConfigTableScan final : public VirtualTableScan { public: ConfigTableScan(CompilerScratch* csb, const Firebird::string& alias, diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index f0c3d291de..afc67c24db 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -262,7 +262,7 @@ private: static const int BIG_VALUE = 1000000; }; -class CryptoManager FB_FINAL : public Firebird::PermanentStorage, public BarSync::IBar +class CryptoManager final : public Firebird::PermanentStorage, public BarSync::IBar { public: typedef Firebird::GetPlugins Factory; @@ -327,7 +327,7 @@ private: class DbInfo; friend class DbInfo; - class DbInfo FB_FINAL : public Firebird::RefCntIface > + class DbInfo final : public Firebird::RefCntIface > { public: DbInfo(CryptoManager* cm) diff --git a/src/jrd/Database.h b/src/jrd/Database.h index 236bd4e64d..64a46534ce 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -59,6 +59,7 @@ #include "../jrd/RandomGenerator.h" #include "../common/os/guid.h" #include "../common/os/os_utils.h" +#include "../jrd/ods.h" #include "../jrd/sbm.h" #include "../jrd/flu.h" #include "../jrd/RuntimeStatistics.h" @@ -370,7 +371,7 @@ public: bool exist; }; - class Linger FB_FINAL : + class Linger final : public Firebird::RefCntIface > { public: @@ -582,6 +583,11 @@ public: return (dbb_replica_mode == mode); } + USHORT getEncodedOdsVersion() const + { + return ENCODE_ODS(dbb_ods_version, dbb_minor_version); + } + private: Database(MemoryPool* p, Firebird::IPluginConfig* pConf, bool shared) : dbb_permanent(p), diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 0e8a60511b..9f9cae11bc 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -35,8 +35,8 @@ class blb; class jrd_tra; class DsqlCursor; class DsqlBatch; -class dsql_req; -class JrdStatement; +class DsqlRequest; +class Statement; class StableAttachmentPart; class Attachment; class Service; @@ -48,7 +48,7 @@ class JStatement; class JAttachment; class JProvider; -class JBlob FB_FINAL : +class JBlob final : public Firebird::RefCntIface > { public: @@ -92,7 +92,7 @@ private: void internalClose(Firebird::CheckStatusWrapper* status); }; -class JTransaction FB_FINAL : +class JTransaction final : public Firebird::RefCntIface > { public: @@ -151,7 +151,7 @@ private: void internalDisconnect(Firebird::CheckStatusWrapper* status); }; -class JResultSet FB_FINAL : +class JResultSet final : public Firebird::RefCntIface > { public: @@ -196,7 +196,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JBatch FB_FINAL : +class JBatch final : public Firebird::RefCntIface > { public: @@ -242,7 +242,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JReplicator FB_FINAL : +class JReplicator final : public Firebird::RefCntIface > { public: @@ -277,7 +277,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JStatement FB_FINAL : +class JStatement final : public Firebird::RefCntIface > { public: @@ -308,27 +308,27 @@ public: unsigned parLength, const unsigned char* par) override; public: - JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array& meta); + JStatement(DsqlRequest* handle, StableAttachmentPart* sa, Firebird::Array& meta); StableAttachmentPart* getAttachment() { return sAtt; } - dsql_req* getHandle() throw() + DsqlRequest* getHandle() throw() { return statement; } private: - dsql_req* statement; + DsqlRequest* statement; Firebird::RefPtr sAtt; Firebird::StatementMetadata metadata; void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JRequest FB_FINAL : +class JRequest final : public Firebird::RefCntIface > { public: @@ -349,26 +349,26 @@ public: void deprecatedFree(Firebird::CheckStatusWrapper* status) override; public: - JRequest(JrdStatement* handle, StableAttachmentPart* sa); + JRequest(Statement* handle, StableAttachmentPart* sa); StableAttachmentPart* getAttachment() { return sAtt; } - JrdStatement* getHandle() throw() + Statement* getHandle() throw() { return rq; } private: - JrdStatement* rq; + Statement* rq; Firebird::RefPtr sAtt; void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JEvents FB_FINAL : public Firebird::RefCntIface > +class JEvents final : public Firebird::RefCntIface > { public: // IEvents implementation @@ -397,7 +397,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JAttachment FB_FINAL : +class JAttachment final : public Firebird::RefCntIface > { public: @@ -495,7 +495,7 @@ private: void internalDropDatabase(Firebird::CheckStatusWrapper* status); }; -class JService FB_FINAL : +class JService final : public Firebird::RefCntIface > { public: @@ -518,7 +518,7 @@ private: void freeEngineData(Firebird::CheckStatusWrapper* status); }; -class JProvider FB_FINAL : +class JProvider final : public Firebird::StdPlugin > { public: diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index 93a0cefa01..1fbe449a67 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -134,9 +134,9 @@ namespace return type_alignments[desc->dsc_dtype]; } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { // Clear the message. This is important for external routines. UCHAR* msg = request->getImpure(impureOffset); @@ -152,7 +152,7 @@ namespace // Initialize output parameters with their domains default value or NULL. // Kind of blr_init_variable, but for parameters. - class InitParameterNode : public TypedNode + class InitParameterNode final : public TypedNode { public: InitParameterNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, @@ -204,9 +204,9 @@ namespace return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { dsc* defaultDesc = NULL; @@ -242,7 +242,7 @@ namespace MOV_move(tdbb, &temp, &desc); } - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; @@ -322,9 +322,9 @@ namespace { } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { UCHAR* inMsg = extInMessageNode ? request->getImpure(extInMessageNode->impureOffset) : NULL; UCHAR* outMsg = request->getImpure(extOutMessageNode->impureOffset); @@ -361,7 +361,7 @@ namespace statements.add(FB_NEW_POOL(pool) StallNode(pool)); } - virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const { impure_state* const impure = request->getImpure(impureOffset); ExtEngineManager::ResultSet*& resultSet = request->req_ext_resultset; @@ -373,7 +373,7 @@ namespace switch (request->req_operation) { - case jrd_req::req_evaluate: + case Request::req_evaluate: fb_assert(!resultSet); resultSet = procedure->open(tdbb, extInMsg, extOutMsg); @@ -386,8 +386,8 @@ namespace *eof = -1; // fall into - case jrd_req::req_proceed: - case jrd_req::req_sync: + case Request::req_proceed: + case Request::req_sync: if (resultSet) { if (resultSet->fetch(tdbb) && (request->req_flags & req_proc_fetch)) @@ -401,10 +401,10 @@ namespace } impure->sta_state = 0; // suspend node - request->req_operation = jrd_req::req_sync; + request->req_operation = Request::req_sync; break; - case jrd_req::req_unwind: + case Request::req_unwind: delete resultSet; resultSet = NULL; break; @@ -421,7 +421,7 @@ namespace }; // External trigger node. - class ExtTriggerNode : public TypedNode + class ExtTriggerNode final : public TypedNode { public: ExtTriggerNode(MemoryPool& pool, const ExtEngineManager::Trigger* aTrigger) @@ -450,21 +450,21 @@ namespace return this; } - const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* /*exeState*/) const + const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { trigger->execute(tdbb, request, request->req_trigger_action, getRpb(request, 0), getRpb(request, 1)); - request->req_operation = jrd_req::req_return; + request->req_operation = Request::req_return; } return parentStmt; } private: - static record_param* getRpb(jrd_req* request, USHORT n) + static record_param* getRpb(Request* request, USHORT n) { return request->req_rpb.getCount() > n && request->req_rpb[n].rpb_number.isValid() ? &request->req_rpb[n] : NULL; @@ -893,7 +893,7 @@ ExtEngineManager::Trigger::~Trigger() } -void ExtEngineManager::Trigger::execute(thread_db* tdbb, jrd_req* request, unsigned action, +void ExtEngineManager::Trigger::execute(thread_db* tdbb, Request* request, unsigned action, record_param* oldRpb, record_param* newRpb) const { EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine); @@ -1039,7 +1039,7 @@ void ExtEngineManager::Trigger::setupComputedFields(thread_db* tdbb, MemoryPool& } -void ExtEngineManager::Trigger::setValues(thread_db* tdbb, jrd_req* request, Array& msgBuffer, +void ExtEngineManager::Trigger::setValues(thread_db* tdbb, Request* request, Array& msgBuffer, record_param* rpb) const { if (!rpb || !rpb->rpb_record) @@ -1247,7 +1247,7 @@ void ExtEngineManager::closeAttachment(thread_db* tdbb, Attachment* attachment) FbLocalStatus status; engine->closeAttachment(&status, attInfo->context); //// FIXME: log status - // Check whether the engine is used by other attachments. + // Check whether the engine is used by other attachments. // If no one uses, release it. bool close = true; WriteLockGuard writeGuard(enginesLock, FB_FUNCTION); @@ -1298,6 +1298,9 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: metadata->inputParameters.assignRefNoIncr(Routine::createMetadata(udf->getInputFields(), true)); metadata->outputParameters.assignRefNoIncr(Routine::createMetadata(udf->getOutputFields(), true)); + udf->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); + udf->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); + FbLocalStatus status; RefPtr inBuilder(REF_NO_INCR, metadata->inputParameters->getBuilder(&status)); @@ -1314,12 +1317,23 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: externalFunction = attInfo->engine->makeFunction(&status, attInfo->context, metadata, inBuilder, outBuilder); - status.check(); - if (!externalFunction) + try { - status_exception::raise( - Arg::Gds(isc_eem_func_not_returned) << udf->getName().toString() << engine); + status.check(); + + if (!externalFunction) + { + status_exception::raise( + Arg::Gds(isc_eem_func_not_returned) << udf->getName().toString() << engine); + } + } + catch (const Exception&) + { + if (tdbb->getAttachment()->isGbak()) + return; + else + throw; } extInputParameters.assignRefNoIncr(inBuilder->getMetadata(&status)); @@ -1329,9 +1343,6 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: status.check(); } - udf->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); - udf->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); - const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false); const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true); @@ -1387,7 +1398,7 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: extFunctionNode->statement = FB_NEW_POOL(csbPool) MessageMoverNode( csbPool, extOutMessageNode, intOutMessageNode); - JrdStatement* statement = udf->getStatement(); + Statement* statement = udf->getStatement(); PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); udf->setStatement(statement); } @@ -1423,6 +1434,9 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ metadata->inputParameters.assignRefNoIncr(Routine::createMetadata(prc->getInputFields(), true)); metadata->outputParameters.assignRefNoIncr(Routine::createMetadata(prc->getOutputFields(), true)); + prc->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); + prc->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); + FbLocalStatus status; RefPtr inBuilder(REF_NO_INCR, metadata->inputParameters->getBuilder(&status)); @@ -1439,13 +1453,24 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ externalProcedure = attInfo->engine->makeProcedure(&status, attInfo->context, metadata, inBuilder, outBuilder); - status.check(); - if (!externalProcedure) + try { - status_exception::raise( - Arg::Gds(isc_eem_proc_not_returned) << - prc->getName().toString() << engine); + status.check(); + + if (!externalProcedure) + { + status_exception::raise( + Arg::Gds(isc_eem_proc_not_returned) << + prc->getName().toString() << engine); + } + } + catch (const Exception&) + { + if (tdbb->getAttachment()->isGbak()) + return; + else + throw; } extInputParameters.assignRefNoIncr(inBuilder->getMetadata(&status)); @@ -1455,9 +1480,6 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ status.check(); } - prc->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); - prc->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); - const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false); const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true); @@ -1519,7 +1541,7 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ extInMessageNode, extOutMessageNode, intOutMessageNode, prc->getExternal()); mainNode->statements.add(extProcedureNode); - JrdStatement* statement = prc->getStatement(); + Statement* statement = prc->getStatement(); PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); prc->setStatement(statement); } diff --git a/src/jrd/ExtEngineManager.h b/src/jrd/ExtEngineManager.h index e124cc628c..031358103d 100644 --- a/src/jrd/ExtEngineManager.h +++ b/src/jrd/ExtEngineManager.h @@ -40,7 +40,7 @@ namespace Jrd { class thread_db; class jrd_prc; -class jrd_req; +class Request; class jrd_tra; class Attachment; class CompilerScratch; @@ -55,14 +55,14 @@ struct impure_value; struct record_param; -class ExtEngineManager FB_FINAL : public Firebird::PermanentStorage +class ExtEngineManager final : public Firebird::PermanentStorage { private: class AttachmentImpl; template class ContextManager; class TransactionImpl; - class RoutineMetadata FB_FINAL : + class RoutineMetadata final : public Firebird::VersionedIface >, public Firebird::PermanentStorage { @@ -280,12 +280,12 @@ public: Firebird::IExternalTrigger* aTrigger, const Jrd::Trigger* aTrg); ~Trigger(); - void execute(thread_db* tdbb, jrd_req* request, unsigned action, + void execute(thread_db* tdbb, Request* request, unsigned action, record_param* oldRpb, record_param* newRpb) const; private: void setupComputedFields(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb); - void setValues(thread_db* tdbb, jrd_req* request, Firebird::Array& msgBuffer, record_param* rpb) const; + void setValues(thread_db* tdbb, Request* request, Firebird::Array& msgBuffer, record_param* rpb) const; public: Firebird::Array> computedStatements; diff --git a/src/jrd/Function.epp b/src/jrd/Function.epp index 9396bd786b..cc54bdbd19 100644 --- a/src/jrd/Function.epp +++ b/src/jrd/Function.epp @@ -460,7 +460,7 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT throw; } - fb_assert(function->getStatement()->function == function); + fb_assert(!function->isDefined() || function->getStatement()->function == function); } else { diff --git a/src/jrd/Function.h b/src/jrd/Function.h index 8f746628e8..5ddb9acd40 100644 --- a/src/jrd/Function.h +++ b/src/jrd/Function.h @@ -65,7 +65,7 @@ namespace Jrd virtual SLONG getSclType() const { - return SCL_object_function; + return obj_functions; } virtual bool checkCache(thread_db* tdbb) const; diff --git a/src/jrd/IntlManager.cpp b/src/jrd/IntlManager.cpp index ccc9857e1d..9e09e54936 100644 --- a/src/jrd/IntlManager.cpp +++ b/src/jrd/IntlManager.cpp @@ -625,7 +625,7 @@ bool IntlManager::lookupCharSet(const string& charSetName, charset* cs) } -bool IntlManager::lookupCollation(const string& collationName, +void IntlManager::lookupCollation(const string& collationName, const string& charSetName, USHORT attributes, const UCHAR* specificAttributes, ULONG specificAttributesLen, bool ignoreAttributes, @@ -633,32 +633,56 @@ bool IntlManager::lookupCollation(const string& collationName, { ExternalInfo charSetExternalInfo; ExternalInfo collationExternalInfo; + char statusBuffer[BUFFER_LARGE] = ""; if (charSetCollations->get(charSetName + ":" + charSetName, charSetExternalInfo) && charSetCollations->get(charSetName + ":" + collationName, collationExternalInfo)) { - pfn_INTL_lookup_texttype lookupFunction = NULL; + ModuleLoader::Module* module = nullptr; + + if (collationExternalInfo.moduleName.hasData()) + modules->get(collationExternalInfo.moduleName, module); + + pfn_INTL_lookup_texttype_with_status lookupStatusFunction = nullptr; if (collationExternalInfo.moduleName.isEmpty()) - lookupFunction = INTL_builtin_lookup_texttype; - else - { - ModuleLoader::Module* module; + lookupStatusFunction = INTL_builtin_lookup_texttype_status; + else if (module) + module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_WITH_STATUS_ENTRYPOINT), lookupStatusFunction); - if (modules->get(collationExternalInfo.moduleName, module) && module) - module->findSymbol(NULL, STRINGIZE(TEXTTYPE_ENTRYPOINT), lookupFunction); + if (lookupStatusFunction) + { + if ((*lookupStatusFunction)(statusBuffer, sizeof(statusBuffer), + tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), + attributes, specificAttributes, specificAttributesLen, ignoreAttributes, + collationExternalInfo.configInfo.c_str())) + { + return; + } } - - if (lookupFunction && - (*lookupFunction)(tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), - attributes, specificAttributes, specificAttributesLen, ignoreAttributes, - collationExternalInfo.configInfo.c_str())) + else if (module) { - return true; + pfn_INTL_lookup_texttype lookupFunction = nullptr; + module->findSymbol(nullptr, STRINGIZE(TEXTTYPE_ENTRYPOINT), lookupFunction); + + if (lookupFunction && + (*lookupFunction)(tt, collationExternalInfo.name.c_str(), charSetExternalInfo.name.c_str(), + attributes, specificAttributes, specificAttributesLen, ignoreAttributes, + collationExternalInfo.configInfo.c_str())) + { + return; + } } } - return false; + if (statusBuffer[0]) + { + (Arg::Gds(isc_collation_not_installed) << collationName << charSetName << + Arg::Gds(isc_random) << statusBuffer + ).raise(); + } + else + (Arg::Gds(isc_collation_not_installed) << collationName << charSetName).raise(); } diff --git a/src/jrd/IntlManager.h b/src/jrd/IntlManager.h index 26698497ac..c35ecf9705 100644 --- a/src/jrd/IntlManager.h +++ b/src/jrd/IntlManager.h @@ -47,7 +47,7 @@ public: static bool lookupCharSet(const Firebird::string& charSetName, charset* cs); - static bool lookupCollation(const Firebird::string& collationName, + static void lookupCollation(const Firebird::string& collationName, const Firebird::string& charSetName, USHORT attributes, const UCHAR* specificAttributes, ULONG specificAttributesLen, bool ignoreAttributes, diff --git a/src/jrd/KeywordsTable.h b/src/jrd/KeywordsTable.h index 06ab0e094c..0aeb424a13 100644 --- a/src/jrd/KeywordsTable.h +++ b/src/jrd/KeywordsTable.h @@ -45,7 +45,7 @@ public: }; -class KeywordsTableScan : public VirtualTableScan +class KeywordsTableScan final : public VirtualTableScan { public: KeywordsTableScan(CompilerScratch* csb, const Firebird::string& alias, diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index e4e1047c08..ed7b9d1222 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -636,7 +636,7 @@ public: static const ULONG FLAG_DELIVER = 0x2; }; -class MappingIpc FB_FINAL : public Firebird::IpcObject +class MappingIpc final : public Firebird::IpcObject { static const USHORT MAPPING_VERSION = 1; static const size_t DEFAULT_SIZE = 1024 * 1024; @@ -867,13 +867,13 @@ private: { MappingHeader* sMem = sharedMemory->getHeader(); resetMap(sMem->databaseForReset, sMem->resetIndex); + p->flags &= ~MappingHeader::FLAG_DELIVER; MappingHeader::Process* cur = &sMem->process[sMem->currentProcess]; if (sharedMemory->eventPost(&cur->callbackEvent) != FB_SUCCESS) { (Arg::Gds(isc_map_event) << "POST").raise(); } - p->flags &= ~MappingHeader::FLAG_DELIVER; } if (startup) @@ -1235,20 +1235,11 @@ InitInstance spCache; void resetMap(const char* db, ULONG index) { - switch(index) - { - case Mapping::MAPPING_CACHE: + if (index & Mapping::MAPPING_CACHE) resetMap(db); - break; - case Mapping::SYSTEM_PRIVILEGES_CACHE: + if (index & Mapping::SYSTEM_PRIVILEGES_CACHE) spCache().invalidate(db); - break; - - default: - fb_assert(false); - break; - } } } // anonymous namespace diff --git a/src/jrd/Mapping.h b/src/jrd/Mapping.h index 64dccddd30..5ff436676e 100644 --- a/src/jrd/Mapping.h +++ b/src/jrd/Mapping.h @@ -77,8 +77,9 @@ public: void clearMainHandle(); // possible clearCache() flags - static const USHORT MAPPING_CACHE = 0; - static const USHORT SYSTEM_PRIVILEGES_CACHE = 1; + static const USHORT MAPPING_CACHE = 0x01; + static const USHORT SYSTEM_PRIVILEGES_CACHE = 0x02; + static const USHORT ALL_CACHE = MAX_USHORT; // Helper statuc functions to perform cleanup & shutdown. static void clearCache(const char* dbName, USHORT id); static void shutdownIpc(); diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index 17b6767982..ece65b9d30 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -39,7 +39,6 @@ #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/cvt_proto.h" #include "../jrd/CryptoManager.h" @@ -47,6 +46,7 @@ #include "../jrd/RecordBuffer.h" #include "../jrd/Monitoring.h" #include "../jrd/Function.h" +#include "../jrd/optimizer/Optimizer.h" #ifdef WIN_NT #include @@ -446,6 +446,9 @@ MonitoringSnapshot::MonitoringSnapshot(thread_db* tdbb, MemoryPool& pool) RecordBuffer* const dbb_buffer = allocBuffer(tdbb, pool, rel_mon_database); RecordBuffer* const att_buffer = allocBuffer(tdbb, pool, rel_mon_attachments); RecordBuffer* const tra_buffer = allocBuffer(tdbb, pool, rel_mon_transactions); + RecordBuffer* const cmp_stmt_buffer = dbb->getEncodedOdsVersion() >= ODS_13_1 ? + allocBuffer(tdbb, pool, rel_mon_compiled_statements) : + nullptr; RecordBuffer* const stmt_buffer = allocBuffer(tdbb, pool, rel_mon_statements); RecordBuffer* const call_buffer = allocBuffer(tdbb, pool, rel_mon_calls); RecordBuffer* const io_stat_buffer = allocBuffer(tdbb, pool, rel_mon_io_stats); @@ -561,6 +564,9 @@ MonitoringSnapshot::MonitoringSnapshot(thread_db* tdbb, MemoryPool& pool) case rel_mon_transactions: buffer = tra_buffer; break; + case rel_mon_compiled_statements: + buffer = cmp_stmt_buffer; + break; case rel_mon_statements: buffer = stmt_buffer; break; @@ -1039,7 +1045,7 @@ void Monitoring::putAttachment(SnapshotData::DumpRecord& record, const Jrd::Atta // statement timeout, milliseconds record.storeInteger(f_mon_att_stmt_timeout, attachment->getStatementTimeout()); - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_version) >= ODS_13_1) + if (dbb->getEncodedOdsVersion() >= ODS_13_1) { char timeZoneBuffer[TimeZoneUtil::MAX_SIZE]; TimeZoneUtil::format(timeZoneBuffer, sizeof(timeZoneBuffer), attachment->att_current_timezone); @@ -1072,7 +1078,7 @@ void Monitoring::putTransaction(SnapshotData::DumpRecord& record, const jrd_tra* record.reset(rel_mon_transactions); int temp = mon_state_idle; - for (const jrd_req* request = transaction->tra_requests; + for (const Request* request = transaction->tra_requests; request; request = request->req_tra_next) { if (request->req_transaction && (request->req_flags & req_active)) @@ -1134,11 +1140,55 @@ void Monitoring::putTransaction(SnapshotData::DumpRecord& record, const jrd_tra* } -void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* request, +void Monitoring::putStatement(SnapshotData::DumpRecord& record, const Statement* statement, const string& plan) +{ + fb_assert(statement); + + record.reset(rel_mon_compiled_statements); + + // compiled statement id + record.storeInteger(f_mon_cmp_stmt_id, statement->getStatementId()); + + // sql text + if (statement->sqlText) + record.storeString(f_mon_cmp_stmt_sql_text, *statement->sqlText); + + // explained plan + if (plan.hasData()) + record.storeString(f_mon_cmp_stmt_expl_plan, plan); + + // object name/type + if (const auto routine = statement->getRoutine()) + { + if (routine->getName().package.hasData()) + record.storeString(f_mon_cmp_stmt_pkg_name, routine->getName().package); + + record.storeString(f_mon_cmp_stmt_name, routine->getName().identifier); + record.storeInteger(f_mon_cmp_stmt_type, routine->getObjectType()); + } + else if (!statement->triggerName.isEmpty()) + { + record.storeString(f_mon_cmp_stmt_name, statement->triggerName); + record.storeInteger(f_mon_cmp_stmt_type, obj_trigger); + } + + // statistics + const int stat_id = fb_utils::genUniqueId(); + record.storeGlobalId(f_mon_cmp_stmt_stat_id, getGlobalId(stat_id)); + + record.write(); + + putMemoryUsage(record, statement->pool->getStatsGroup(), stat_id, stat_cmp_statement); +} + + +void Monitoring::putRequest(SnapshotData::DumpRecord& record, const Request* request, const string& plan) { fb_assert(request); + const auto dbb = request->req_attachment->att_database; + record.reset(rel_mon_statements); // request id @@ -1165,7 +1215,7 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req else record.storeInteger(f_mon_stmt_state, mon_state_idle); - const JrdStatement* const statement = request->getStatement(); + const Statement* const statement = request->getStatement(); // sql text if (statement->sqlText) @@ -1181,6 +1231,10 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req // statement timeout, milliseconds record.storeInteger(f_mon_stmt_timeout, request->req_timeout); + + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + record.storeInteger(f_mon_stmt_cmp_stmt_id, statement->getStatementId()); + record.write(); putStatistics(record, request->req_stats, stat_id, stat_statement); @@ -1188,11 +1242,13 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req } -void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* request) +void Monitoring::putCall(SnapshotData::DumpRecord& record, const Request* request) { fb_assert(request); - const jrd_req* initialRequest = request->req_caller; + const auto dbb = request->req_attachment->att_database; + const Request* initialRequest = request->req_caller; + while (initialRequest->req_caller) { initialRequest = initialRequest->req_caller; @@ -1209,7 +1265,7 @@ void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* reques if (initialRequest != request->req_caller) record.storeInteger(f_mon_call_caller_id, request->req_caller->getRequestId()); - const JrdStatement* statement = request->getStatement(); + const Statement* statement = request->getStatement(); const Routine* routine = statement->getRoutine(); // object name/type @@ -1241,6 +1297,9 @@ void Monitoring::putCall(SnapshotData::DumpRecord& record, const jrd_req* reques record.storeInteger(f_mon_call_src_column, request->req_src_column); } + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + record.storeInteger(f_mon_call_cmp_stmt_id, statement->getStatementId()); + // statistics const int stat_id = fb_utils::genUniqueId(); record.storeGlobalId(f_mon_call_stat_id, getGlobalId(stat_id)); @@ -1417,14 +1476,14 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) for (transaction = attachment->att_transactions; transaction; transaction = transaction->tra_next) { - for (jrd_req* request = transaction->tra_requests; + for (Request* request = transaction->tra_requests; request && (request->req_flags & req_active) && (request->req_transaction == transaction); request = request->req_caller) { request->adjustCallerStats(); if (!(request->getStatement()->flags & - (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER)) && + (Statement::FLAG_INTERNAL | Statement::FLAG_SYS_TRIGGER)) && request->req_caller) { putCall(record, request); @@ -1432,18 +1491,29 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) } } + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + { + // Statement information + + for (const auto statement : attachment->att_statements) + { + if (!(statement->flags & (Statement::FLAG_INTERNAL | Statement::FLAG_SYS_TRIGGER))) + { + const string plan = Optimizer::getPlan(tdbb, statement, true); + putStatement(record, statement, plan); + } + } + } + // Request information - for (const jrd_req* const* i = attachment->att_requests.begin(); - i != attachment->att_requests.end(); - ++i) + for (const auto request : attachment->att_requests) { - const jrd_req* const request = *i; + const auto statement = request->getStatement(); - if (!(request->getStatement()->flags & - (JrdStatement::FLAG_INTERNAL | JrdStatement::FLAG_SYS_TRIGGER))) + if (!(statement->flags & (Statement::FLAG_INTERNAL | Statement::FLAG_SYS_TRIGGER))) { - const string plan = OPT_get_plan(tdbb, request, true); + const string plan = Optimizer::getPlan(tdbb, statement, true); putRequest(record, request, plan); } } diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index 7e8d266c61..649e88484f 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -244,7 +244,7 @@ struct MonitoringHeader : public Firebird::MemoryHeader }; -class MonitoringData FB_FINAL : public Firebird::PermanentStorage, public Firebird::IpcObject +class MonitoringData final : public Firebird::PermanentStorage, public Firebird::IpcObject { static const USHORT MONITOR_VERSION = 5; static const ULONG DEFAULT_SIZE = 1048576; @@ -389,8 +389,9 @@ private: static void putAttachment(SnapshotData::DumpRecord&, const Attachment*); static void putTransaction(SnapshotData::DumpRecord&, const jrd_tra*); - static void putRequest(SnapshotData::DumpRecord&, const jrd_req*, const Firebird::string&); - static void putCall(SnapshotData::DumpRecord&, const jrd_req*); + static void putStatement(SnapshotData::DumpRecord&, const Statement*, const Firebird::string&); + static void putRequest(SnapshotData::DumpRecord&, const Request*, const Firebird::string&); + static void putCall(SnapshotData::DumpRecord&, const Request*); static void putStatistics(SnapshotData::DumpRecord&, const RuntimeStatistics&, int, int); static void putContextVars(SnapshotData::DumpRecord&, const Firebird::StringMap&, SINT64, bool); static void putMemoryUsage(SnapshotData::DumpRecord&, const Firebird::MemoryStats&, int, int); diff --git a/src/jrd/Optimizer.cpp b/src/jrd/Optimizer.cpp deleted file mode 100644 index 4c1cd6a94c..0000000000 --- a/src/jrd/Optimizer.cpp +++ /dev/null @@ -1,3255 +0,0 @@ -/* - * PROGRAM: Client/Server Common Code - * MODULE: Optimizer.cpp - * DESCRIPTION: Optimizer - * - * The contents of this file are subject to the Initial - * Developer's Public License Version 1.0 (the "License"); - * you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. - * - * Software distributed under the License is distributed AS IS, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. - * See the License for the specific language governing rights - * and limitations under the License. - * - * The Original Code was created by Arno Brinkman - * for the Firebird Open Source RDBMS project. - * - * Copyright (c) 2004 Arno Brinkman - * and all contributors signed below. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - * Adriano dos Santos Fernandes - * - */ - -#include "firebird.h" - -#include "../jrd/jrd.h" -#include "../jrd/exe.h" -#include "../jrd/btr.h" -#include "../jrd/intl.h" -#include "../jrd/Collation.h" -#include "../jrd/rse.h" -#include "../jrd/ods.h" -#include "../jrd/Optimizer.h" -#include "../jrd/RecordSourceNodes.h" -#include "../jrd/recsrc/RecordSource.h" -#include "../dsql/BoolNodes.h" -#include "../dsql/ExprNodes.h" -#include "../dsql/StmtNodes.h" - -#include "../jrd/btr_proto.h" -#include "../jrd/cch_proto.h" -#include "../jrd/cmp_proto.h" -#include "../jrd/dpm_proto.h" -#include "../jrd/exe_proto.h" -#include "../jrd/ext_proto.h" -#include "../jrd/intl_proto.h" -#include "../jrd/met_proto.h" -#include "../jrd/mov_proto.h" -#include "../jrd/par_proto.h" - -using namespace Firebird; -using namespace Jrd; - -namespace -{ - // Check the index for being an expression one and - // matching both the given stream and the given expression tree - bool checkExpressionIndex(CompilerScratch* csb, const index_desc* idx, - ValueExprNode* node, StreamType stream) - { - fb_assert(idx); - - if (idx->idx_expression) - { - // The desired expression can be hidden inside a derived expression node, - // so try to recover it (see CORE-4118). - while (!idx->idx_expression->sameAs(node, true)) - { - DerivedExprNode* const derivedExpr = nodeAs(node); - CastNode* const cast = nodeAs(node); - - if (derivedExpr) - node = derivedExpr->arg; - else if (cast && cast->artificial) - node = cast->source; - else - return false; - } - - SortedStreamList exprStreams, nodeStreams; - idx->idx_expression->collectStreams(exprStreams); - node->collectStreams(nodeStreams); - - if (exprStreams.getCount() == 1 && exprStreams[0] == 0 && - nodeStreams.getCount() == 1 && nodeStreams[0] == stream) - { - return true; - } - } - - return false; - } - - ValueExprNode* injectCast(CompilerScratch* csb, - ValueExprNode* value, CastNode*& cast, - const dsc& desc) - { - // If the indexed column is of type int64, then we need to inject - // an extra cast to deliver the scale value to the BTR level - - if (value && desc.dsc_dtype == dtype_int64) - { - if (!cast) - { - cast = FB_NEW_POOL(csb->csb_pool) CastNode(csb->csb_pool); - cast->source = value; - cast->castDesc = desc; - cast->impureOffset = csb->allocImpure(); - } - - value = cast; - } - - return value; - } - - ValueExprNode* invertBoolValue(CompilerScratch* csb, ValueExprNode* value) - { - // Having a condition ( != ), - // invert it by making ( == ( == FALSE)), - // so that an index lookup could be possible - - static const UCHAR falseValue = '\0'; - LiteralNode* const falseLiteral = - FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); - falseLiteral->litDesc.makeBoolean(const_cast(&falseValue)); - - ComparativeBoolNode* const cmpNode = - FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode(csb->csb_pool, blr_eql); - cmpNode->arg1 = value; - cmpNode->arg2 = falseLiteral; - - // Recreate the boolean expression as a value - BoolAsValueNode* const newValue = - FB_NEW_POOL(csb->csb_pool) BoolAsValueNode(csb->csb_pool); - newValue->boolean = cmpNode; - - newValue->impureOffset = csb->allocImpure(); - - return newValue; - } - -} // namespace - -namespace Jrd -{ - string OPT_make_alias(const CompilerScratch* csb, StreamType stream) - { - // Make an alias string suitable for printing as part of the plan. - // For views, this means multiple aliases to distinguish the base table. - - string alias; - - const CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; - - if (csb_tail->csb_view || csb_tail->csb_alias) - { - ObjectsArray alias_list; - - while (csb_tail) - { - if (csb_tail->csb_alias) - alias_list.push(*csb_tail->csb_alias); - else if (csb_tail->csb_relation) - alias_list.push(csb_tail->csb_relation->rel_name.c_str()); - - if (!csb_tail->csb_view) - break; - - csb_tail = &csb->csb_rpt[csb_tail->csb_view_stream]; - } - - while (alias_list.hasData()) - { - alias += alias_list.pop(); - - if (alias_list.hasData()) - alias += ' '; - } - } - else if (csb_tail->csb_relation) - alias = csb_tail->csb_relation->rel_name.c_str(); - else if (csb_tail->csb_procedure) - alias = csb_tail->csb_procedure->getName().toString(); - //// TODO: LocalTableSourceNode - else - fb_assert(false); - - return alias; - } -} - -IndexScratchSegment::IndexScratchSegment(MemoryPool& p) : - matches(p) -{ -/************************************** - * - * I n d e x S c r a t c h S e g m e n t - * - ************************************** - * - * Functional description - * - **************************************/ - lowerValue = NULL; - upperValue = NULL; - excludeLower = false; - excludeUpper = false; - scope = 0; - scanType = segmentScanNone; -} - -IndexScratchSegment::IndexScratchSegment(MemoryPool& p, IndexScratchSegment* segment) : - matches(p) -{ -/************************************** - * - * I n d e x S c r a t c h S e g m e n t - * - ************************************** - * - * Functional description - * - **************************************/ - lowerValue = segment->lowerValue; - upperValue = segment->upperValue; - excludeLower = segment->excludeLower; - excludeUpper = segment->excludeUpper; - scope = segment->scope; - scanType = segment->scanType; - - for (FB_SIZE_T i = 0; i < segment->matches.getCount(); i++) { - matches.add(segment->matches[i]); - } -} - -IndexScratch::IndexScratch(MemoryPool& p, thread_db* tdbb, index_desc* ix, - CompilerScratch::csb_repeat* csb_tail) : - idx(ix), segments(p) -{ -/************************************** - * - * I n d e x S c r a t c h - * - ************************************** - * - * Functional description - * - **************************************/ - // Allocate needed segments - selectivity = MAXIMUM_SELECTIVITY; - candidate = false; - scopeCandidate = false; - lowerCount = 0; - upperCount = 0; - nonFullMatchedSegments = 0; - fuzzy = false; - - segments.grow(idx->idx_count); - - IndexScratchSegment** segment = segments.begin(); - for (FB_SIZE_T i = 0; i < segments.getCount(); i++) { - segment[i] = FB_NEW_POOL(p) IndexScratchSegment(p); - } - - const int length = ROUNDUP(BTR_key_length(tdbb, csb_tail->csb_relation, idx), sizeof(SLONG)); - - // AB: Calculate the cardinality which should reflect the total number - // of index pages for this index. - // We assume that the average index-key can be compressed by a factor 0.5 - // In the future the average key-length should be stored and retrieved - // from a system table (RDB$INDICES for example). - // Multiplying the selectivity with this cardinality gives the estimated - // number of index pages that are read for the index retrieval. - double factor = 0.5; - if (segments.getCount() > 1) - { - // Compound indexes are generally less compressed. - factor = 0.7; - } - - const Database* const dbb = tdbb->getDatabase(); - cardinality = (csb_tail->csb_cardinality * (2 + (length * factor))) / (dbb->dbb_page_size - BTR_SIZE); - cardinality = MAX(cardinality, MINIMUM_CARDINALITY); -} - -IndexScratch::IndexScratch(MemoryPool& p, const IndexScratch& scratch) : - segments(p) -{ -/************************************** - * - * I n d e x S c r a t c h - * - ************************************** - * - * Functional description - * - **************************************/ - selectivity = scratch.selectivity; - cardinality = scratch.cardinality; - candidate = scratch.candidate; - scopeCandidate = scratch.scopeCandidate; - lowerCount = scratch.lowerCount; - upperCount = scratch.upperCount; - nonFullMatchedSegments = scratch.nonFullMatchedSegments; - fuzzy = scratch.fuzzy; - idx = scratch.idx; - - // Allocate needed segments - segments.grow(scratch.segments.getCount()); - - IndexScratchSegment* const* scratchSegment = scratch.segments.begin(); - IndexScratchSegment** segment = segments.begin(); - for (FB_SIZE_T i = 0; i < segments.getCount(); i++) { - segment[i] = FB_NEW_POOL(p) IndexScratchSegment(p, scratchSegment[i]); - } -} - -IndexScratch::~IndexScratch() -{ -/************************************** - * - * ~I n d e x S c r a t c h - * - ************************************** - * - * Functional description - * - **************************************/ - IndexScratchSegment** segment = segments.begin(); - for (FB_SIZE_T i = 0; i < segments.getCount(); i++) { - delete segment[i]; - } -} - -InversionCandidate::InversionCandidate(MemoryPool& p) : - matches(p), dbkeyRanges(p), dependentFromStreams(p) -{ -/************************************** - * - * I n v e r s i o n C a n d i d a t e - * - ************************************** - * - * Functional description - * - **************************************/ - selectivity = MAXIMUM_SELECTIVITY; - cost = 0; - indexes = 0; - dependencies = 0; - nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; - matchedSegments = 0; - boolean = NULL; - condition = NULL; - inversion = NULL; - scratch = NULL; - used = false; - unique = false; - navigated = false; -} - -OptimizerRetrieval::OptimizerRetrieval(MemoryPool& p, OptimizerBlk* opt, - StreamType streamNumber, bool outer, - bool inner, SortNode* sortNode) - : pool(p), alias(p), indexScratches(p), inversionCandidates(p) -{ -/************************************** - * - * O p t i m i z e r R e t r i e v a l - * - ************************************** - * - * Functional description - * - **************************************/ - createIndexScanNodes = false; - setConjunctionsMatched = false; - - tdbb = NULL; - SET_TDBB(tdbb); - - this->database = tdbb->getDatabase(); - this->stream = streamNumber; - this->optimizer = opt; - this->csb = this->optimizer->opt_csb; - this->innerFlag = inner; - this->outerFlag = outer; - this->sort = sortNode; - this->navigationCandidate = NULL; - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[this->stream]; - relation = csb_tail->csb_relation; - - // Allocate needed indexScratches - - index_desc* idx = csb_tail->csb_idx->items; - for (int i = 0; i < csb_tail->csb_indices; ++i, ++idx) - indexScratches.add(IndexScratch(p, tdbb, idx, csb_tail)); -} - -OptimizerRetrieval::~OptimizerRetrieval() -{ -/************************************** - * - * ~O p t i m i z e r R e t r i e v a l - * - ************************************** - * - * Functional description - * - **************************************/ - if (navigationCandidate) - delete navigationCandidate; - for (FB_SIZE_T i = 0; i < inversionCandidates.getCount(); ++i) - delete inversionCandidates[i]; -} - -InversionNode* OptimizerRetrieval::composeInversion(InversionNode* node1, InversionNode* node2, - InversionNode::Type node_type) const -{ -/************************************** - * - * c o m p o s e I n v e r s i o n - * - ************************************** - * - * Functional description - * Melt two inversions together by the - * type given in node_type. - * - **************************************/ - - if (!node2) - return node1; - - if (!node1) - return node2; - - if (node_type == InversionNode::TYPE_OR) - { - if (node1->type == InversionNode::TYPE_INDEX && - node2->type == InversionNode::TYPE_INDEX && - node1->retrieval->irb_index == node2->retrieval->irb_index) - { - node_type = InversionNode::TYPE_IN; - } - else if (node1->type == InversionNode::TYPE_IN && - node2->type == InversionNode::TYPE_INDEX && - node1->node2->retrieval->irb_index == node2->retrieval->irb_index) - { - node_type = InversionNode::TYPE_IN; - } - } - - return FB_NEW_POOL(pool) InversionNode(node_type, node1, node2); -} - -const string& OptimizerRetrieval::getAlias() -{ -/************************************** - * - * g e t A l i a s - * - ************************************** - * - * Functional description - * - **************************************/ - if (alias.isEmpty()) - alias = OPT_make_alias(csb, this->stream); - - return alias; -} - -InversionCandidate* OptimizerRetrieval::generateInversion() -{ -/************************************** - * - * g e n e r a t e I n v e r s i o n - * - ************************************** - * - * Functional description - * - **************************************/ - OptimizerBlk::opt_conjunct* const opt_begin = - optimizer->opt_conjuncts.begin() + (outerFlag ? optimizer->opt_base_parent_conjuncts : 0); - - const OptimizerBlk::opt_conjunct* const opt_end = - innerFlag ? optimizer->opt_conjuncts.begin() + optimizer->opt_base_missing_conjuncts : - optimizer->opt_conjuncts.end(); - - InversionCandidate* invCandidate = NULL; - - if (relation && !relation->rel_file && !relation->isVirtual()) - { - InversionCandidateList inversions; - - // First, handle "AND" comparisons (all nodes except OR) - for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - BinaryBoolNode* booleanNode = nodeAs(node); - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && node && - (!booleanNode || booleanNode->blrOp != blr_or)) - { - invCandidate = matchOnIndexes(&indexScratches, node, 1); - - if (invCandidate) - inversions.add(invCandidate); - } - } - - getInversionCandidates(&inversions, &indexScratches, 1); - - // Second, handle "OR" comparisons - for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - BinaryBoolNode* booleanNode = nodeAs(node); - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && node && - (booleanNode && booleanNode->blrOp == blr_or)) - { - invCandidate = matchOnIndexes(&indexScratches, node, 1); - - if (invCandidate) - { - invCandidate->boolean = node; - inversions.add(invCandidate); - } - } - } - - if (sort) - analyzeNavigation(inversions); - -#ifdef OPT_DEBUG_RETRIEVAL - // Debug - printCandidates(&inversions); -#endif - - invCandidate = makeInversion(&inversions); - - // Clean up inversion list - InversionCandidate** inversion = inversions.begin(); - for (FB_SIZE_T i = 0; i < inversions.getCount(); i++) - if (inversion[i] != navigationCandidate) - delete inversion[i]; - } - - if (!invCandidate) - { - // No index will be used, thus create a dummy inversion candidate - // representing the natural table access. All the necessary properties - // (selectivity: 1.0, cost: 0, unique: false) are set up by the constructor. - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - } - - if (invCandidate->unique) - { - // Set up the unique retrieval cost to be fixed and not dependent on - // possibly outdated statistics. It includes N index scans plus one data page fetch. - invCandidate->cost = DEFAULT_INDEX_COST * invCandidate->indexes + 1; - } - else - { - // Add the records retrieval cost to the priorly calculated index scan cost - invCandidate->cost += csb->csb_rpt[stream].csb_cardinality * invCandidate->selectivity; - } - - // Adjust the effective selectivity by treating computable conjunctions as filters - for (const OptimizerBlk::opt_conjunct* tail = optimizer->opt_conjuncts.begin(); - tail < optimizer->opt_conjuncts.end(); tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - node->computable(csb, stream, true) && - !invCandidate->matches.exist(node)) - { - const ComparativeBoolNode* const cmpNode = nodeAs(node); - - const double factor = (cmpNode && cmpNode->blrOp == blr_eql) ? - REDUCE_SELECTIVITY_FACTOR_EQUALITY : REDUCE_SELECTIVITY_FACTOR_INEQUALITY; - invCandidate->selectivity *= factor; - } - } - - // Add the streams where this stream is depending on. - for (FB_SIZE_T i = 0; i < invCandidate->matches.getCount(); i++) - invCandidate->matches[i]->findDependentFromStreams(this, &invCandidate->dependentFromStreams); - - if (setConjunctionsMatched) - { - SortedArray matches; - - // AB: Putting a unsorted array in a sorted array directly by join isn't - // very safe at the moment, but in our case Array holds a sorted list. - // However SortedArray class should be updated to handle join right! - - matches.join(invCandidate->matches); - - for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) - { - if (!(tail->opt_conjunct_flags & opt_conjunct_used)) - { - if (matches.exist(tail->opt_conjunct_node)) - tail->opt_conjunct_flags |= opt_conjunct_matched; - } - } - } - -#ifdef OPT_DEBUG_RETRIEVAL - // Debug - printFinalCandidate(invCandidate); -#endif - - return invCandidate; -} - -IndexTableScan* OptimizerRetrieval::getNavigation() -{ -/************************************** - * - * g e t N a v i g a t i o n - * - ************************************** - * - * Functional description - * - **************************************/ - if (!navigationCandidate) - return NULL; - - IndexScratch* const scratch = navigationCandidate->scratch; - - // Looks like we can do a navigational walk. Flag that - // we have used this index for navigation, and allocate - // a navigational rsb for it. - scratch->idx->idx_runtime_flags |= idx_navigate; - - const USHORT key_length = - ROUNDUP(BTR_key_length(tdbb, relation, scratch->idx), sizeof(SLONG)); - - InversionNode* const index_node = makeIndexScanNode(scratch); - - return FB_NEW_POOL(*tdbb->getDefaultPool()) - IndexTableScan(csb, getAlias(), stream, relation, index_node, key_length); -} - -void OptimizerRetrieval::analyzeNavigation(const InversionCandidateList& inversions) -{ -/************************************** - * - * a n a l y z e N a v i g a t i o n - * - ************************************** - * - * Functional description - * - **************************************/ - fb_assert(sort); - - for (FB_SIZE_T i = 0; i < indexScratches.getCount(); ++i) - { - IndexScratch* const indexScratch = &indexScratches[i]; - const index_desc* const idx = indexScratch->idx; - - // if the number of fields in the sort is greater than the number of - // fields in the index, the index will not be used to optimize the - // sort--note that in the case where the first field is unique, this - // could be optimized, since the sort will be performed correctly by - // navigating on a unique index on the first field--deej - if (sort->expressions.getCount() > idx->idx_count) - continue; - - // if the user-specified access plan for this request didn't - // mention this index, forget it - if ((idx->idx_runtime_flags & idx_plan_dont_use) && - !(idx->idx_runtime_flags & idx_plan_navigate)) - { - continue; - } - - // only a single-column ORDER BY clause can be mapped to - // an expression index - if (idx->idx_flags & idx_expressn) - { - if (sort->expressions.getCount() != 1) - continue; - } - - // check to see if the fields in the sort match the fields in the index - // in the exact same order - - const IndexScratchSegment* const* segment = indexScratch->segments.begin(); - const IndexScratchSegment* const* const end_segment = - segment + MIN(indexScratch->lowerCount, indexScratch->upperCount); - int equalSegments = 0; - - for (; segment < end_segment; segment++) - { - if ((*segment)->scanType == segmentScanEqual || - (*segment)->scanType == segmentScanEquivalent || - (*segment)->scanType == segmentScanMissing) - { - equalSegments++; - } - } - - bool usableIndex = true; - const index_desc::idx_repeat* idx_tail = idx->idx_rpt; - const index_desc::idx_repeat* const idx_end = idx_tail + idx->idx_count; - NestConst* ptr = sort->expressions.begin(); - const SortDirection* direction = sort->direction.begin(); - const NullsPlacement* nullOrder = sort->nullOrder.begin(); - - for (const NestConst* const end = sort->expressions.end(); - ptr != end; - ++ptr, ++direction, ++nullOrder, ++idx_tail) - { - ValueExprNode* const orgNode = *ptr; - FieldNode* fieldNode; - bool nodeMatched = false; - - // Collect nodes equivalent to the given sort node - - HalfStaticArray nodes; - nodes.add(orgNode); - - for (const OptimizerBlk::opt_conjunct* tail = optimizer->opt_conjuncts.begin(); - tail < optimizer->opt_conjuncts.end(); tail++) - { - BoolExprNode* const boolean = tail->opt_conjunct_node; - fb_assert(boolean); - - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - - if (cmpNode && (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) - { - ValueExprNode* const node1 = cmpNode->arg1; - ValueExprNode* const node2 = cmpNode->arg2; - - if (node1->sameAs(orgNode, false)) - nodes.add(node2); - - if (node2->sameAs(orgNode, false)) - nodes.add(node1); - } - } - - // Check whether any of the equivalent nodes is suitable for index navigation - - for (ValueExprNode** iter = nodes.begin(); iter != nodes.end(); ++iter) - { - ValueExprNode* const node = *iter; - - if (idx->idx_flags & idx_expressn) - { - if (!checkExpressionIndex(csb, idx, node, stream)) - continue; - } - else if (!(fieldNode = nodeAs(node)) || fieldNode->fieldStream != stream) - { - continue; - } - else - { - for (; idx_tail < idx_end && fieldNode->fieldId != idx_tail->idx_field; idx_tail++) - { - const int segmentNumber = idx_tail - idx->idx_rpt; - - if (segmentNumber >= equalSegments) - break; - } - - if (idx_tail >= idx_end || fieldNode->fieldId != idx_tail->idx_field) - continue; - } - - if ((*direction == ORDER_DESC && !(idx->idx_flags & idx_descending)) || - (*direction == ORDER_ASC && (idx->idx_flags & idx_descending)) || - ((*nullOrder == NULLS_FIRST && *direction == ORDER_DESC) || - (*nullOrder == NULLS_LAST && *direction == ORDER_ASC))) - { - continue; - } - - dsc desc; - node->getDesc(tdbb, csb, &desc); - - // ASF: "desc.dsc_ttype() > ttype_last_internal" is to avoid recursion - // when looking for charsets/collations - - if (DTYPE_IS_TEXT(desc.dsc_dtype) && desc.dsc_ttype() > ttype_last_internal) - { - const TextType* const tt = INTL_texttype_lookup(tdbb, desc.dsc_ttype()); - - if (idx->idx_flags & idx_unique) - { - if (tt->getFlags() & TEXTTYPE_UNSORTED_UNIQUE) - continue; - } - else - { - // ASF: We currently can't use non-unique index for GROUP BY and DISTINCT with - // multi-level and insensitive collation. In NAV, keys are verified with memcmp - // but there we don't know length of each level. - if (sort->unique && (tt->getFlags() & TEXTTYPE_SEPARATE_UNIQUE)) - continue; - } - } - - nodeMatched = true; - break; - } - - if (!nodeMatched) - { - usableIndex = false; - break; - } - } - - if (!usableIndex) - continue; - - // Lookup the inversion candidate matching our navigational index - - InversionCandidate* candidate = NULL; - - for (InversionCandidate* const* iter = inversions.begin(); - iter != inversions.end(); ++iter) - { - if ((*iter)->scratch == indexScratch) - { - candidate = *iter; - break; - } - } - - // Check whether the navigational index has any matches shared with other inversion - // candidates. If so, compare inversions and decide whether navigation is acceptable. - // However, if the user-specified access plan mentions this index, - // then don't consider any (possibly better) alternatives. - // Another exception is when the FIRST ROWS optimization strategy is applied. - - if (candidate && !optimizer->favorFirstRows && - !(indexScratch->idx->idx_runtime_flags & idx_plan_navigate)) - { - for (const InversionCandidate* const* iter = inversions.begin(); - iter != inversions.end(); ++iter) - { - const InversionCandidate* const otherCandidate = *iter; - - if (otherCandidate != candidate) - { - for (BoolExprNode* const* iter2 = otherCandidate->matches.begin(); - iter2 != otherCandidate->matches.end(); ++iter2) - { - if (candidate->matches.exist(*iter2) && - betterInversion(otherCandidate, candidate, true)) - { - usableIndex = false; - break; - } - } - } - - if (!usableIndex) - break; - } - } - - if (!usableIndex) - continue; - - // Looks like we can do a navigational walk. Remember this candidate - // and compare it against other possible candidates. - - if (!candidate) - { - // If no inversion candidate is found, create a fake one representing full index scan - - candidate = FB_NEW_POOL(pool) InversionCandidate(pool); - candidate->cost = indexScratch->cardinality; - candidate->indexes = 1; - candidate->scratch = indexScratch; - candidate->nonFullMatchedSegments = (int) indexScratch->segments.getCount(); - } - - if (!navigationCandidate || - betterInversion(candidate, navigationCandidate, false)) - { - navigationCandidate = candidate; - } - } -} - -bool OptimizerRetrieval::betterInversion(const InversionCandidate* inv1, - const InversionCandidate* inv2, - bool ignoreUnmatched) const -{ - // Return true if inversion1 is *better* than inversion2. - // It's mostly about the retrieval cost, but other aspects are also taken into account. - - if (inv1->unique && !inv2->unique) - { - // A unique full equal match is better than anything else. - return true; - } - - if (inv1->unique == inv2->unique) - { - if (inv1->dependencies > inv2->dependencies) - { - // Index used for a relationship must be always prefered to - // the filtering ones, otherwise the nested loop join has - // no chances to be better than a sort merge. - // An alternative (simplified) condition might be: - // currentInv->dependencies > 0 - // && bestCandidate->dependencies == 0 - // but so far I tend to think that the current one is better. - return true; - } - - if (inv1->dependencies == inv2->dependencies) - { - const double cardinality = - MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); - - const double cost1 = - inv1->cost + (inv1->selectivity * cardinality); - const double cost2 = - inv2->cost + (inv2->selectivity * cardinality); - - // Do we have very similar costs? - double diffCost = 0; - if (!cost1 && !cost2) - { - // Two zero costs should be handled as being the same - // (other comparison criteria should be applied, see below). - diffCost = 1; - } - else if (cost1) - { - // Calculate the difference - diffCost = cost2 / cost1; - } - - if ((diffCost >= 0.98) && (diffCost <= 1.02)) - { - // If the "same" costs then compare with the nr of unmatched segments, - // how many indexes and matched segments. First compare number of indexes. - int compareSelectivity = (inv1->indexes - inv2->indexes); - - if (compareSelectivity == 0) - { - // For the same number of indexes compare number of matched segments. - // Note the inverted condition: the more matched segments the better. - compareSelectivity = (inv2->matchedSegments - inv1->matchedSegments); - - if (compareSelectivity == 0 && !ignoreUnmatched) - { - // For the same number of matched segments - // compare ones that aren't full matched - compareSelectivity = - (inv1->nonFullMatchedSegments - inv2->nonFullMatchedSegments); - } - } - - if (compareSelectivity < 0) - return true; - } - else if (cost1 < cost2) - return true; - } - } - - return false; -} - -void OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversions, - IndexScratchList* fromIndexScratches, USHORT scope) const -{ -/************************************** - * - * g e t I n v e r s i o n C a n d i d a t e s - * - ************************************** - * - * Functional description - * - **************************************/ - const double cardinality = csb->csb_rpt[stream].csb_cardinality; - - // Walk through indexes to calculate selectivity / candidate - Array matches; - FB_SIZE_T i = 0; - - for (i = 0; i < fromIndexScratches->getCount(); i++) - { - IndexScratch& scratch = (*fromIndexScratches)[i]; - scratch.scopeCandidate = false; - scratch.lowerCount = 0; - scratch.upperCount = 0; - scratch.nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; - scratch.fuzzy = false; - - if (scratch.candidate) - { - matches.clear(); - scratch.selectivity = MAXIMUM_SELECTIVITY; - - bool unique = false; - - for (int j = 0; j < scratch.idx->idx_count; j++) - { - const IndexScratchSegment* const segment = scratch.segments[j]; - - if (segment->scope == scope) - scratch.scopeCandidate = true; - - if (segment->scanType != segmentScanMissing && !(scratch.idx->idx_flags & idx_unique)) - { - const USHORT iType = scratch.idx->idx_rpt[j].idx_itype; - - if (iType >= idx_first_intl_string) - { - TextType* textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(iType)); - - if (textType->getFlags() & TEXTTYPE_SEPARATE_UNIQUE) - { - // ASF: Order is more precise than equivalence class. - // We can't use the next segments, and we'll need to use - // INTL_KEY_PARTIAL to construct the last segment's key. - scratch.fuzzy = true; - } - } - } - - // Check if this is the last usable segment - if (!scratch.fuzzy && - (segment->scanType == segmentScanEqual || - segment->scanType == segmentScanEquivalent || - segment->scanType == segmentScanMissing)) - { - // This is a perfect usable segment thus update root selectivity - scratch.lowerCount++; - scratch.upperCount++; - scratch.selectivity = scratch.idx->idx_rpt[j].idx_selectivity; - scratch.nonFullMatchedSegments = scratch.idx->idx_count - (j + 1); - // Add matches for this segment to the main matches list - matches.join(segment->matches); - - // An equality scan for any unique index cannot retrieve more - // than one row. The same is true for an equivalence scan for - // any primary index. - const bool single_match = - (segment->scanType == segmentScanEqual && - (scratch.idx->idx_flags & idx_unique)) || - (segment->scanType == segmentScanEquivalent && - (scratch.idx->idx_flags & idx_primary)); - - // dimitr: IS NULL scan against primary key is guaranteed - // to return zero rows. Do we need yet another - // special case here? - - if (single_match && ((j + 1) == scratch.idx->idx_count)) - { - // We have found a full equal matching index and it's unique, - // so we can stop looking further, because this is the best - // one we can get. - unique = true; - break; - } - - // dimitr: number of nulls is not reflected by our selectivity, - // so IS NOT DISTINCT and IS NULL scans may retrieve - // much bigger bitmap than expected here. I think - // appropriate reduce selectivity factors are required - // to be applied here. - } - else - { - // This is our last segment that we can use, - // estimate the selectivity - double selectivity = scratch.selectivity; - double factor = 1; - - switch (segment->scanType) - { - case segmentScanBetween: - scratch.lowerCount++; - scratch.upperCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; - factor = REDUCE_SELECTIVITY_FACTOR_BETWEEN; - break; - - case segmentScanLess: - scratch.upperCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; - factor = REDUCE_SELECTIVITY_FACTOR_LESS; - break; - - case segmentScanGreater: - scratch.lowerCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; - factor = REDUCE_SELECTIVITY_FACTOR_GREATER; - break; - - case segmentScanStarting: - case segmentScanEqual: - case segmentScanEquivalent: - scratch.lowerCount++; - scratch.upperCount++; - selectivity = scratch.idx->idx_rpt[j].idx_selectivity; - factor = REDUCE_SELECTIVITY_FACTOR_STARTING; - break; - - default: - fb_assert(segment->scanType == segmentScanNone); - break; - } - - // Adjust the compound selectivity using the reduce factor. - // It should be better than the previous segment but worse - // than a full match. - const double diffSelectivity = scratch.selectivity - selectivity; - selectivity += (diffSelectivity * factor); - fb_assert(selectivity <= scratch.selectivity); - scratch.selectivity = selectivity; - - if (segment->scanType != segmentScanNone) - { - matches.join(segment->matches); - scratch.nonFullMatchedSegments = scratch.idx->idx_count - j; - } - break; - } - } - - if (scratch.scopeCandidate) - { - // When selectivity is zero the statement is prepared on an - // empty table or the statistics aren't updated. - // For an unique index, estimate the selectivity via the stream cardinality. - // For a non-unique one, assume 1/10 of the maximum selectivity, so that - // at least some indexes could be chosen by the optimizer. - double selectivity = scratch.selectivity; - - if (selectivity <= 0) - { - if (unique && cardinality) - selectivity = 1 / cardinality; - else - selectivity = DEFAULT_SELECTIVITY; - } - - InversionCandidate* invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - invCandidate->unique = unique; - invCandidate->selectivity = selectivity; - // Calculate the cost (only index pages) for this index. - invCandidate->cost = DEFAULT_INDEX_COST + scratch.selectivity * scratch.cardinality; - invCandidate->nonFullMatchedSegments = scratch.nonFullMatchedSegments; - invCandidate->matchedSegments = MAX(scratch.lowerCount, scratch.upperCount); - invCandidate->indexes = 1; - invCandidate->scratch = &scratch; - invCandidate->matches.join(matches); - - for (FB_SIZE_T k = 0; k < invCandidate->matches.getCount(); k++) - { - invCandidate->matches[k]->findDependentFromStreams(this, - &invCandidate->dependentFromStreams); - } - - invCandidate->dependencies = (int) invCandidate->dependentFromStreams.getCount(); - inversions->add(invCandidate); - } - } - } -} - -ValueExprNode* OptimizerRetrieval::findDbKey(ValueExprNode* dbkey, SLONG* position) const -{ -/************************************** - * - * f i n d D b K e y - * - ************************************** - * - * Functional description - * Search a dbkey (possibly a concatenated one) for - * a dbkey for specified stream. - * - **************************************/ - - const RecordKeyNode* keyNode = nodeAs(dbkey); - - if (keyNode && keyNode->blrOp == blr_dbkey) - { - if (keyNode->recStream == stream) - return dbkey; - - ++*position; - return NULL; - } - - ConcatenateNode* concatNode = nodeAs(dbkey); - - if (concatNode) - { - ValueExprNode* dbkey_temp = findDbKey(concatNode->arg1, position); - - if (dbkey_temp) - return dbkey_temp; - - dbkey_temp = findDbKey(concatNode->arg2, position); - - if (dbkey_temp) - return dbkey_temp; - } - - return NULL; -} - - -InversionNode* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) const -{ -/************************************** - * - * m a k e I n d e x S c a n N o d e - * - ************************************** - * - * Functional description - * Build node for index scan. - * - **************************************/ - - if (!createIndexScanNodes) - return NULL; - - index_desc* const idx = indexScratch->idx; - - // Check whether this is during a compile or during a SET INDEX operation - if (csb) - CMP_post_resource(&csb->csb_resources, relation, Resource::rsc_index, idx->idx_id); - else - { - CMP_post_resource(&tdbb->getRequest()->getStatement()->resources, relation, - Resource::rsc_index, idx->idx_id); - } - - // For external requests, determine index name (to be reported in plans) - MetaName indexName; - if (!(csb->csb_g_flags & csb_internal)) - MET_lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1); - - IndexRetrieval* const retrieval = - FB_NEW_POOL(pool) IndexRetrieval(pool, relation, idx, indexName); - - // Pick up lower bound segment values - ValueExprNode** lower = retrieval->irb_value; - ValueExprNode** upper = retrieval->irb_value + idx->idx_count; - retrieval->irb_lower_count = indexScratch->lowerCount; - retrieval->irb_upper_count = indexScratch->upperCount; - - if (idx->idx_flags & idx_descending) - { - // switch upper/lower information - upper = retrieval->irb_value; - lower = retrieval->irb_value + idx->idx_count; - retrieval->irb_lower_count = indexScratch->upperCount; - retrieval->irb_upper_count = indexScratch->lowerCount; - retrieval->irb_generic |= irb_descending; - } - - int i = 0; - bool ignoreNullsOnScan = true; - IndexScratchSegment** segment = indexScratch->segments.begin(); - - for (i = 0; i < MAX(indexScratch->lowerCount, indexScratch->upperCount); i++) - { - if (segment[i]->scanType == segmentScanMissing) - { - *lower++ = *upper++ = NullNode::instance(); - ignoreNullsOnScan = false; - } - else - { - if (i < indexScratch->lowerCount) - *lower++ = segment[i]->lowerValue; - - if (i < indexScratch->upperCount) - *upper++ = segment[i]->upperValue; - - if (segment[i]->scanType == segmentScanEquivalent) - ignoreNullsOnScan = false; - } - } - - i = MAX(indexScratch->lowerCount, indexScratch->upperCount) - 1; - - if (i >= 0) - { - if (segment[i]->scanType == segmentScanStarting) - retrieval->irb_generic |= irb_starting; - - if (segment[i]->excludeLower) - retrieval->irb_generic |= irb_exclude_lower; - - if (segment[i]->excludeUpper) - retrieval->irb_generic |= irb_exclude_upper; - } - - if (indexScratch->fuzzy) - retrieval->irb_generic |= irb_starting; // Flag the need to use INTL_KEY_PARTIAL in btr. - - // This index is never used for IS NULL, thus we can ignore NULLs - // already at index scan. But this rule doesn't apply to nod_equiv - // which requires NULLs to be found in the index. - // A second exception is when this index is used for navigation. - if (ignoreNullsOnScan && !(idx->idx_runtime_flags & idx_navigate)) - retrieval->irb_generic |= irb_ignore_null_value_key; - - // Check to see if this is really an equality retrieval - if (retrieval->irb_lower_count == retrieval->irb_upper_count) - { - retrieval->irb_generic |= irb_equality; - segment = indexScratch->segments.begin(); - - for (i = 0; i < retrieval->irb_lower_count; i++) - { - if (segment[i]->lowerValue != segment[i]->upperValue) - { - retrieval->irb_generic &= ~irb_equality; - break; - } - } - } - - // If we are matching less than the full index, this is a partial match - if (idx->idx_flags & idx_descending) - { - if (retrieval->irb_lower_count < idx->idx_count) - retrieval->irb_generic |= irb_partial; - } - else - { - if (retrieval->irb_upper_count < idx->idx_count) - retrieval->irb_generic |= irb_partial; - } - - // mark the index as utilized for the purposes of this compile - idx->idx_runtime_flags |= idx_used; - - const ULONG impure = csb ? csb->allocImpure() : 0; - return FB_NEW_POOL(pool) InversionNode(retrieval, impure); -} - -InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* inversions) const -{ -/************************************** - * - * m a k e I n v e r s i o n - * - ************************************** - * - * Select best available inversion candidates - * and compose them to 1 inversion. - * This was never implemented: - * If top is true the datapages-cost is - * also used in the calculation (only needed - * for top InversionNode generation). - * - **************************************/ - - if (inversions->isEmpty() && !navigationCandidate) - return NULL; - - const double streamCardinality = - MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); - - // Prepared statements could be optimized against an almost empty table - // and then cached (such as in the restore process), thus causing slowdown - // when the table grows. In this case, let's consider all available indices. - const bool smallTable = (streamCardinality <= THRESHOLD_CARDINALITY); - - // These flags work around our smart index selection algorithm. Any explicit - // (i.e. user specified) plan requires all existing indices to be considered - // for a retrieval. Internal (system) requests used by the engine itself are - // often optimized using zero or non-actual statistics, so they are processed - // using somewhat relaxed rules. - const bool customPlan = csb->csb_rpt[stream].csb_plan; - const bool sysRequest = (csb->csb_g_flags & csb_internal); - - double totalSelectivity = MAXIMUM_SELECTIVITY; // worst selectivity - double totalIndexCost = 0; - - // The upper limit to use an index based retrieval is five indexes + almost all datapages - const double maximumCost = (DEFAULT_INDEX_COST * 5) + (streamCardinality * 0.95); - const double minimumSelectivity = 1 / streamCardinality; - - double previousTotalCost = maximumCost; - - // Force to always choose at least one index - bool firstCandidate = true; - - InversionCandidate* invCandidate = NULL; - - for (InversionCandidate** iter = inversions->begin(); - iter != inversions->end(); ++iter) - { - InversionCandidate* const inversion = *iter; - const IndexScratch* const indexScratch = inversion->scratch; - - // If the explicit plan doesn't mention this index, fake it as used - // thus excluding it from the cost-based algorithm. Otherwise, - // given this index is suitable for navigation, also mark it as used. - - if ((indexScratch && - (indexScratch->idx->idx_runtime_flags & idx_plan_dont_use)) || - (!customPlan && inversion == navigationCandidate)) - { - inversion->used = true; - } - } - - // The matches returned in this inversion are always sorted. - SortedArray matches; - - if (navigationCandidate) - { - matches.join(navigationCandidate->matches); - - // Reset the selectivity/cost prerequisites to account the navigational candidate - totalSelectivity = navigationCandidate->selectivity; - totalIndexCost = navigationCandidate->cost; - previousTotalCost = totalIndexCost + totalSelectivity * streamCardinality; - - if (navigationCandidate->matchedSegments) - firstCandidate = false; - } - - for (FB_SIZE_T i = 0; i < inversions->getCount(); i++) - { - // Initialize vars before walking through candidates - InversionCandidate* bestCandidate = NULL; - bool restartLoop = false; - - for (FB_SIZE_T currentPosition = 0; currentPosition < inversions->getCount(); ++currentPosition) - { - InversionCandidate* currentInv = (*inversions)[currentPosition]; - if (!currentInv->used) - { - // If this is a unique full equal matched inversion we're done, so - // we can make the inversion and return it. - if (currentInv->unique && currentInv->dependencies && !currentInv->condition) - { - if (!invCandidate) - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - - if (!currentInv->inversion && currentInv->scratch) - invCandidate->inversion = makeIndexScanNode(currentInv->scratch); - else - invCandidate->inversion = currentInv->inversion; - - invCandidate->dbkeyRanges.assign(currentInv->dbkeyRanges); - invCandidate->unique = currentInv->unique; - invCandidate->selectivity = currentInv->selectivity; - invCandidate->cost = currentInv->cost; - invCandidate->indexes = currentInv->indexes; - invCandidate->nonFullMatchedSegments = 0; - invCandidate->matchedSegments = currentInv->matchedSegments; - invCandidate->dependencies = currentInv->dependencies; - matches.clear(); - - for (FB_SIZE_T j = 0; j < currentInv->matches.getCount(); j++) - { - if (!matches.exist(currentInv->matches[j])) - matches.add(currentInv->matches[j]); - } - - if (currentInv->boolean) - { - if (!matches.exist(currentInv->boolean)) - matches.add(currentInv->boolean); - } - - invCandidate->matches.join(matches); - if (customPlan) - continue; - - return invCandidate; - } - - // Look if a match is already used by previous matches. - bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false; - for (FB_SIZE_T k = 0; k < currentInv->matches.getCount(); k++) - { - if (matches.exist(currentInv->matches[k])) - { - anyMatchAlreadyUsed = true; - - if (navigationCandidate && - navigationCandidate->matches.exist(currentInv->matches[k])) - { - matchUsedByNavigation = true; - } - - break; - } - } - - 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 (FB_SIZE_T j = 0; j < currentInv->matches.getCount(); j++) - { - if (!matches.exist(currentInv->matches[j])) - matches.add(currentInv->matches[j]); - } - - // Restart loop, because other indexes could also be excluded now. - restartLoop = true; - break; - } - - if (!bestCandidate) - { - // The first candidate - bestCandidate = currentInv; - } - else - { - // Prefer unconditional inversions - if (currentInv->condition) - { - currentInv->used = true; - restartLoop = true; - break; - } - - if (bestCandidate->condition) - { - bestCandidate = currentInv; - restartLoop = true; - break; - } - - if (betterInversion(currentInv, bestCandidate, false)) - bestCandidate = currentInv; - } - } - } - - if (restartLoop) - continue; - - // If we have a candidate which is interesting build the inversion - // else we're done. - if (bestCandidate) - { - // AB: Here we test if our new candidate is interesting enough to be added for - // index retrieval. - - // AB: For now i'll use the calculation that's often used for and-ing selectivities (S1 * S2). - // I think this calculation is not right for many cases. - // For example two "good" selectivities will result in a very good selectivity, but - // mostly a filter is made by adding criteria's where every criteria is an extra filter - // compared to the previous one. Thus with the second criteria in _most_ cases still - // records are returned. (Think also on the segment-selectivity in compound indexes) - // Assume a table with 100000 records and two selectivities of 0.001 (100 records) which - // are both AND-ed (with S1 * S2 => 0.001 * 0.001 = 0.000001 => 0.1 record). - // - // A better formula could be where the result is between "Sbest" and "Sbest * factor" - // The reducing factor should be between 0 and 1 (Sbest = best selectivity) - // - // Example: - /* - double newTotalSelectivity = 0; - double bestSel = bestCandidate->selectivity; - double worstSel = totalSelectivity; - if (bestCandidate->selectivity > totalSelectivity) - { - worstSel = bestCandidate->selectivity; - bestSel = totalSelectivity; - } - - if (bestSel >= MAXIMUM_SELECTIVITY) { - newTotalSelectivity = MAXIMUM_SELECTIVITY; - } - else if (bestSel == 0) { - newTotalSelectivity = 0; - } - else { - newTotalSelectivity = bestSel - ((1 - worstSel) * (bestSel - (bestSel * 0.01))); - } - */ - - const double newTotalSelectivity = bestCandidate->selectivity * totalSelectivity; - const double newTotalDataCost = newTotalSelectivity * streamCardinality; - const double newTotalIndexCost = totalIndexCost + bestCandidate->cost; - const double totalCost = newTotalDataCost + newTotalIndexCost; - - // Test if the new totalCost will be higher than the previous totalCost - // and if the current selectivity (without the bestCandidate) is already good enough. - if (customPlan || sysRequest || smallTable || firstCandidate || - (totalCost < previousTotalCost && totalSelectivity > minimumSelectivity)) - { - // Exclude index from next pass - bestCandidate->used = true; - - firstCandidate = false; - - previousTotalCost = totalCost; - totalIndexCost = newTotalIndexCost; - totalSelectivity = newTotalSelectivity; - - if (!invCandidate) - { - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - if (!bestCandidate->inversion && bestCandidate->scratch) { - invCandidate->inversion = makeIndexScanNode(bestCandidate->scratch); - } - else { - invCandidate->inversion = bestCandidate->inversion; - } - invCandidate->dbkeyRanges.assign(bestCandidate->dbkeyRanges); - invCandidate->unique = bestCandidate->unique; - invCandidate->selectivity = bestCandidate->selectivity; - invCandidate->cost = bestCandidate->cost; - invCandidate->indexes = bestCandidate->indexes; - invCandidate->nonFullMatchedSegments = 0; - invCandidate->matchedSegments = bestCandidate->matchedSegments; - invCandidate->dependencies = bestCandidate->dependencies; - invCandidate->condition = bestCandidate->condition; - - for (FB_SIZE_T j = 0; j < bestCandidate->matches.getCount(); j++) - { - if (!matches.exist(bestCandidate->matches[j])) - matches.add(bestCandidate->matches[j]); - } - if (bestCandidate->boolean) - { - if (!matches.exist(bestCandidate->boolean)) - matches.add(bestCandidate->boolean); - } - } - 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); - } - - invCandidate->dbkeyRanges.join(bestCandidate->dbkeyRanges); - invCandidate->unique = (invCandidate->unique || bestCandidate->unique); - invCandidate->selectivity = totalSelectivity; - invCandidate->cost += bestCandidate->cost; - invCandidate->indexes += bestCandidate->indexes; - invCandidate->nonFullMatchedSegments = 0; - invCandidate->matchedSegments = - MAX(bestCandidate->matchedSegments, invCandidate->matchedSegments); - invCandidate->dependencies += bestCandidate->dependencies; - - for (FB_SIZE_T j = 0; j < bestCandidate->matches.getCount(); j++) - { - if (!matches.exist(bestCandidate->matches[j])) - matches.add(bestCandidate->matches[j]); - } - - if (bestCandidate->boolean) - { - if (!matches.exist(bestCandidate->boolean)) - matches.add(bestCandidate->boolean); - } - } - - if (invCandidate->unique) - { - // Single unique full equal match is enough - if (!customPlan) - break; - } - } - else - { - // We're done - break; - } - } - else { - break; - } - } - - // If we have no index used for filtering, but there's a navigational walk, - // set up the inversion candidate appropriately. - - if (navigationCandidate) - { - if (!invCandidate) - invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - - invCandidate->unique = navigationCandidate->unique; - invCandidate->selectivity *= navigationCandidate->selectivity; - invCandidate->cost += navigationCandidate->cost; - ++invCandidate->indexes; - invCandidate->navigated = true; - } - - if (invCandidate) - invCandidate->matches.join(matches); - - return invCandidate; -} - -bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* boolean, - USHORT scope) const -{ -/************************************** - * - * m a t c h B o o l e a n - * - ************************************** - * - * Functional description - * - **************************************/ - if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) - return false; - - ComparativeBoolNode* cmpNode = nodeAs(boolean); - MissingBoolNode* missingNode = nodeAs(boolean); - NotBoolNode* notNode = nodeAs(boolean); - RseBoolNode* rseNode = nodeAs(boolean); - bool forward = true; - ValueExprNode* value = NULL; - ValueExprNode* match = NULL; - - if (cmpNode) - { - match = cmpNode->arg1; - value = cmpNode->arg2; - } - else if (missingNode) - match = missingNode->arg; - else if (notNode || rseNode) - return false; - else - { - fb_assert(false); - return false; - } - - ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ? - cmpNode->arg3 : NULL; - - if (indexScratch->idx->idx_flags & idx_expressn) - { - // see if one side or the other is matchable to the index expression - - fb_assert(indexScratch->idx->idx_expression != NULL); - - if (!checkExpressionIndex(csb, indexScratch->idx, match, stream) || - (value && !value->computable(csb, stream, false))) - { - if ((!cmpNode || cmpNode->blrOp != blr_starting) && value && - checkExpressionIndex(csb, indexScratch->idx, value, stream) && - match->computable(csb, stream, false)) - { - ValueExprNode* temp = match; - match = value; - value = temp; - forward = false; - } - else - return false; - } - } - else - { - // If left side is not a field, swap sides. - // If left side is still not a field, give up - - FieldNode* fieldNode; - - if (!(fieldNode = nodeAs(match)) || - fieldNode->fieldStream != stream || - (value && !value->computable(csb, stream, false))) - { - ValueExprNode* temp = match; - match = value; - value = temp; - - if ((!match || !(fieldNode = nodeAs(match))) || - fieldNode->fieldStream != stream || - !value->computable(csb, stream, false)) - { - return false; - } - - forward = false; - } - } - - // check datatypes to ensure that the index scan is guaranteed - // to deliver correct results - - bool excludeBound = cmpNode && (cmpNode->blrOp == blr_gtr || cmpNode->blrOp == blr_lss); - - dsc matchDesc, valueDesc; - - if (value) - { - match->getDesc(tdbb, csb, &matchDesc); - value->getDesc(tdbb, csb, &valueDesc); - - if (!BTR_types_comparable(matchDesc, valueDesc)) - return false; - - if (matchDesc.dsc_dtype == dtype_sql_date && - valueDesc.dsc_dtype == dtype_timestamp) - { - // for "DATE TIMESTAMP" we need to include the boundary value - excludeBound = false; - } - } - - // match the field to an index, if possible, and save the value to be matched - // as either the lower or upper bound for retrieval, or both - - FieldNode* const fieldNode = nodeAs(match); - - if (!(indexScratch->idx->idx_flags & idx_expressn)) - fb_assert(fieldNode); - - const bool isDesc = (indexScratch->idx->idx_flags & idx_descending); - - // Needed for int64 matches, see injectCast() function - CastNode *cast = NULL, *cast2 = NULL; - - fb_assert(indexScratch->segments.getCount() == indexScratch->idx->idx_count); - - for (int i = 0; i < indexScratch->idx->idx_count; i++) - { - if (!(indexScratch->idx->idx_flags & idx_expressn) && - fieldNode->fieldId != indexScratch->idx->idx_rpt[i].idx_field) - { - continue; - } - - IndexScratchSegment* const segment = indexScratch->segments[i]; - - if (cmpNode) - { - switch (cmpNode->blrOp) - { - case blr_between: - if (!forward || !value2->computable(csb, stream, false)) - return false; - segment->matches.add(boolean); - // AB: If we have already an exact match don't - // override it with worser matches. - if (!((segment->scanType == segmentScanEqual) || - (segment->scanType == segmentScanEquivalent))) - { - segment->lowerValue = injectCast(csb, value, cast, matchDesc); - segment->upperValue = injectCast(csb, value2, cast2, matchDesc); - segment->scanType = segmentScanBetween; - segment->excludeLower = false; - segment->excludeUpper = false; - } - break; - - case blr_equiv: - segment->matches.add(boolean); - // AB: If we have already an exact match don't - // override it with worser matches. - if (!(segment->scanType == segmentScanEqual)) - { - segment->lowerValue = segment->upperValue = - injectCast(csb, value, cast, matchDesc); - segment->scanType = segmentScanEquivalent; - segment->excludeLower = false; - segment->excludeUpper = false; - } - break; - - case blr_eql: - segment->matches.add(boolean); - segment->lowerValue = segment->upperValue = - injectCast(csb, value, cast, matchDesc); - segment->scanType = segmentScanEqual; - segment->excludeLower = false; - segment->excludeUpper = false; - break; - - // ASF: Make "NOT boolean" work with indices. - case blr_neq: - { - if (valueDesc.dsc_dtype != dtype_boolean) - return false; - - // Invert the value and use it with "segmentScanEqual" for the index lookup - segment->matches.add(boolean); - segment->lowerValue = segment->upperValue = - invertBoolValue(csb, value); - segment->scanType = segmentScanEqual; - segment->excludeLower = false; - segment->excludeUpper = false; - break; - } - - case blr_gtr: - case blr_geq: - segment->matches.add(boolean); - if (!((segment->scanType == segmentScanEqual) || - (segment->scanType == segmentScanEquivalent) || - (segment->scanType == segmentScanBetween))) - { - if (forward != isDesc) // (forward && !isDesc || !forward && isDesc) - segment->excludeLower = excludeBound; - else - segment->excludeUpper = excludeBound; - - if (forward) - { - segment->lowerValue = injectCast(csb, value, cast, matchDesc); - if (segment->scanType == segmentScanLess) - segment->scanType = segmentScanBetween; - else - segment->scanType = segmentScanGreater; - } - else - { - segment->upperValue = injectCast(csb, value, cast, matchDesc); - if (segment->scanType == segmentScanGreater) - segment->scanType = segmentScanBetween; - else - segment->scanType = segmentScanLess; - } - } - break; - - case blr_lss: - case blr_leq: - segment->matches.add(boolean); - if (!((segment->scanType == segmentScanEqual) || - (segment->scanType == segmentScanEquivalent) || - (segment->scanType == segmentScanBetween))) - { - if (forward != isDesc) - segment->excludeUpper = excludeBound; - else - segment->excludeLower = excludeBound; - - if (forward) - { - segment->upperValue = injectCast(csb, value, cast, matchDesc); - if (segment->scanType == segmentScanGreater) - segment->scanType = segmentScanBetween; - else - segment->scanType = segmentScanLess; - } - else - { - segment->lowerValue = injectCast(csb, value, cast, matchDesc); - if (segment->scanType == segmentScanLess) - segment->scanType = segmentScanBetween; - else - segment->scanType = segmentScanGreater; - } - } - break; - - case blr_starting: - // Check if validate for using index - if (!forward || !validateStarts(indexScratch, cmpNode, i)) - return false; - segment->matches.add(boolean); - if (!((segment->scanType == segmentScanEqual) || - (segment->scanType == segmentScanEquivalent))) - { - segment->lowerValue = segment->upperValue = - injectCast(csb, value, cast, matchDesc); - segment->scanType = segmentScanStarting; - segment->excludeLower = false; - segment->excludeUpper = false; - } - break; - - default: - return false; - } - } - else if (missingNode) - { - segment->matches.add(boolean); - if (!((segment->scanType == segmentScanEqual) || - (segment->scanType == segmentScanEquivalent))) - { - segment->lowerValue = segment->upperValue = NULL; - segment->scanType = segmentScanMissing; - segment->excludeLower = false; - segment->excludeUpper = false; - } - } - else - { - fb_assert(false); - return false; - } - - // A match could be made - if (segment->scope < scope) - segment->scope = scope; - - if (i == 0) - { - // If this is the first segment, then this index is a candidate. - indexScratch->candidate = true; - } - - return true; - } - - return false; -} - -InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const -{ -/************************************** - * - * m a t c h D b K e y - * - ************************************** - * - * Functional description - * Check whether a boolean is a DB_KEY based comparison. - * - **************************************/ - // If this isn't an equality, it isn't even interesting - - ComparativeBoolNode* cmpNode = nodeAs(boolean); - - if (!cmpNode) - return NULL; - - switch (cmpNode->blrOp) - { - case blr_equiv: - case blr_eql: - case blr_gtr: - case blr_geq: - case blr_lss: - case blr_leq: - case blr_between: - break; - - default: - return NULL; - } - - // Find the side of the equality that is potentially a dbkey. - // If neither, make the obvious deduction. - - SLONG n = 0; - int dbkeyArg = 1; - auto dbkey = findDbKey(cmpNode->arg1, &n); - - if (!dbkey) - { - n = 0; - dbkeyArg = 2; - dbkey = findDbKey(cmpNode->arg2, &n); - } - - if (!dbkey && (cmpNode->blrOp == blr_between)) - { - n = 0; - dbkeyArg = 3; - dbkey = findDbKey(cmpNode->arg3, &n); - } - - if (!dbkey) - return NULL; - - // Make sure we have the correct stream - - const auto keyNode = nodeAs(dbkey); - - if (!keyNode || keyNode->blrOp != blr_dbkey || keyNode->recStream != stream) - return NULL; - - // If this is a dbkey for the appropriate stream, it's invertable - - const double cardinality = - MAX(csb->csb_rpt[stream].csb_cardinality, MINIMUM_CARDINALITY); - - bool unique = false; - double selectivity = 0; - - ValueExprNode* lower = NULL; - ValueExprNode* upper = NULL; - - switch (cmpNode->blrOp) - { - case blr_eql: - case blr_equiv: - unique = true; - selectivity = 1 / cardinality; - lower = upper = (dbkeyArg == 1) ? cmpNode->arg2 : cmpNode->arg1; - break; - - case blr_gtr: - case blr_geq: - selectivity = REDUCE_SELECTIVITY_FACTOR_GREATER; - if (dbkeyArg == 1) - lower = cmpNode->arg2; // dbkey > arg2 - else - upper = cmpNode->arg1; // arg1 < dbkey - break; - - case blr_lss: - case blr_leq: - selectivity = REDUCE_SELECTIVITY_FACTOR_LESS; - if (dbkeyArg == 1) - upper = cmpNode->arg2; // dbkey < arg2 - else - lower = cmpNode->arg1; // arg1 < dbkey - break; - - case blr_between: - if (dbkeyArg == 1) // dbkey between arg2 and arg3 - { - selectivity = REDUCE_SELECTIVITY_FACTOR_BETWEEN; - lower = cmpNode->arg2; - upper = cmpNode->arg3; - } - else if (dbkeyArg == 2) // arg1 between dbkey and arg3, or dbkey <= arg1 and arg1 <= arg3 - { - selectivity = REDUCE_SELECTIVITY_FACTOR_LESS; - upper = cmpNode->arg1; - } - else if (dbkeyArg == 3) // arg1 between arg2 and dbkey, or arg2 <= arg1 and arg1 <= dbkey - { - selectivity = REDUCE_SELECTIVITY_FACTOR_GREATER; - lower = cmpNode->arg1; - } - break; - - default: - return NULL; - } - - if (lower && !lower->computable(csb, stream, false)) - return NULL; - - if (upper && !upper->computable(csb, stream, false)) - return NULL; - - InversionCandidate* const invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - invCandidate->unique = unique; - invCandidate->selectivity = selectivity; - invCandidate->cost = 0; - invCandidate->matches.add(boolean); - boolean->findDependentFromStreams(this, &invCandidate->dependentFromStreams); - invCandidate->dependencies = (int) invCandidate->dependentFromStreams.getCount(); - - if (createIndexScanNodes) - { - if (unique) - { - fb_assert(lower == upper); - - InversionNode* const inversion = FB_NEW_POOL(pool) InversionNode(lower, n); - inversion->impure = csb->allocImpure(); - invCandidate->inversion = inversion; - } - else - { - fb_assert(n == 0); - fb_assert(lower || upper); - - DbKeyRangeNode* const dbkeyRange = FB_NEW_POOL(pool) DbKeyRangeNode(lower, upper); - invCandidate->dbkeyRanges.add(dbkeyRange); - } - } - - return invCandidate; -} - -InversionCandidate* OptimizerRetrieval::matchOnIndexes( - IndexScratchList* inputIndexScratches, BoolExprNode* boolean, USHORT scope) const -{ -/************************************** - * - * m a t c h O n I n d e x e s - * - ************************************** - * - * Functional description - * Try to match boolean on every index. - * If the boolean is an "OR" node then a - * inversion candidate could be returned. - * - **************************************/ - BinaryBoolNode* binaryNode = nodeAs(boolean); - - // Handle the "OR" case up front - if (binaryNode && binaryNode->blrOp == blr_or) - { - InversionCandidateList inversions; - - // Make list for index matches - IndexScratchList indexOrScratches; - - // Copy information from caller - - FB_SIZE_T i = 0; - - for (; i < inputIndexScratches->getCount(); i++) - { - IndexScratch& scratch = (*inputIndexScratches)[i]; - indexOrScratches.add(scratch); - } - - // We use a scope variable to see on how - // deep we are in a nested or conjunction. - scope++; - - InversionCandidate* invCandidate1 = - matchOnIndexes(&indexOrScratches, binaryNode->arg1, scope); - - if (invCandidate1) - inversions.add(invCandidate1); - - BinaryBoolNode* childBoolNode = nodeAs(binaryNode->arg1); - - // Get usable inversions based on indexOrScratches and scope - if (!childBoolNode || childBoolNode->blrOp != blr_or) - getInversionCandidates(&inversions, &indexOrScratches, scope); - - invCandidate1 = makeInversion(&inversions); - - // Clear list to remove previously matched conjunctions - indexOrScratches.clear(); - - // Copy information from caller - - i = 0; - - for (; i < inputIndexScratches->getCount(); i++) - { - IndexScratch& scratch = (*inputIndexScratches)[i]; - indexOrScratches.add(scratch); - } - - // Clear inversion list - inversions.clear(); - - InversionCandidate* invCandidate2 = matchOnIndexes( - &indexOrScratches, binaryNode->arg2, scope); - - if (invCandidate2) - inversions.add(invCandidate2); - - childBoolNode = nodeAs(binaryNode->arg2); - - // Make inversion based on indexOrScratches and scope - if (!childBoolNode || childBoolNode->blrOp != blr_or) - getInversionCandidates(&inversions, &indexOrScratches, scope); - - invCandidate2 = makeInversion(&inversions); - - if (invCandidate1 && invCandidate2 && - (invCandidate1->indexes || invCandidate1->unique) && - (invCandidate2->indexes || invCandidate2->unique)) - { - InversionCandidate* invCandidate = FB_NEW_POOL(pool) InversionCandidate(pool); - invCandidate->inversion = composeInversion(invCandidate1->inversion, - invCandidate2->inversion, InversionNode::TYPE_OR); - invCandidate->selectivity = invCandidate1->selectivity + invCandidate2->selectivity - - invCandidate1->selectivity * invCandidate2->selectivity; - invCandidate->cost = invCandidate1->cost + invCandidate2->cost; - invCandidate->indexes = invCandidate1->indexes + invCandidate2->indexes; - invCandidate->nonFullMatchedSegments = 0; - invCandidate->matchedSegments = - MIN(invCandidate1->matchedSegments, invCandidate2->matchedSegments); - invCandidate->dependencies = invCandidate1->dependencies + invCandidate2->dependencies; - - if (invCandidate1->condition && invCandidate2->condition) - { - BinaryBoolNode* const newNode = - FB_NEW_POOL(*tdbb->getDefaultPool()) BinaryBoolNode(*tdbb->getDefaultPool(), blr_or); - newNode->arg1 = invCandidate1->condition; - newNode->arg2 = invCandidate2->condition; - invCandidate->condition = newNode; - } - else if (invCandidate1->condition) - { - invCandidate->condition = invCandidate1->condition; - } - else if (invCandidate2->condition) - { - invCandidate->condition = invCandidate2->condition; - } - - // Add matches conjunctions that exists in both left and right inversion - if ((invCandidate1->matches.getCount()) && (invCandidate2->matches.getCount())) - { - SortedArray matches; - - for (FB_SIZE_T j = 0; j < invCandidate1->matches.getCount(); j++) - matches.add(invCandidate1->matches[j]); - - for (FB_SIZE_T j = 0; j < invCandidate2->matches.getCount(); j++) - { - if (matches.exist(invCandidate2->matches[j])) - invCandidate->matches.add(invCandidate2->matches[j]); - } - } - - return invCandidate; - } - - if (invCandidate1) - { - BoolExprNode* condition = binaryNode->arg2; - - if (condition->computable(csb, stream, false)) - { - if (invCandidate1->condition) - { - BinaryBoolNode* const newNode = - FB_NEW_POOL(*tdbb->getDefaultPool()) - BinaryBoolNode(*tdbb->getDefaultPool(), blr_or); - newNode->arg1 = invCandidate1->condition; - newNode->arg2 = condition; - condition = newNode; - } - - invCandidate1->condition = condition; - return invCandidate1; - } - } - - if (invCandidate2) - { - BoolExprNode* condition = binaryNode->arg1; - - if (condition->computable(csb, stream, false)) - { - if (invCandidate2->condition) - { - BinaryBoolNode* const newNode = - FB_NEW_POOL(*tdbb->getDefaultPool()) - BinaryBoolNode(*tdbb->getDefaultPool(), blr_or); - newNode->arg1 = invCandidate2->condition; - newNode->arg2 = condition; - condition = newNode; - } - - invCandidate2->condition = condition; - return invCandidate2; - } - } - - return NULL; - } - - if (binaryNode && binaryNode->blrOp == blr_and) - { - // Recursively call this procedure for every boolean - // and finally get candidate inversions. - // Normally we come here from within a OR conjunction. - InversionCandidateList inversions; - - InversionCandidate* invCandidate = matchOnIndexes( - inputIndexScratches, binaryNode->arg1, scope); - - if (invCandidate) - inversions.add(invCandidate); - - invCandidate = matchOnIndexes(inputIndexScratches, binaryNode->arg2, scope); - - if (invCandidate) - inversions.add(invCandidate); - - return makeInversion(&inversions); - } - - // Check for DB_KEY comparison - InversionCandidate* const invCandidate = matchDbKey(boolean); - - // Walk through indexes - for (FB_SIZE_T i = 0; i < inputIndexScratches->getCount(); i++) - { - IndexScratch& indexScratch = (*inputIndexScratches)[i]; - - // Try to match the boolean against a index. - if (!(indexScratch.idx->idx_runtime_flags & idx_plan_dont_use) || - (indexScratch.idx->idx_runtime_flags & idx_plan_navigate)) - { - matchBoolean(&indexScratch, boolean, scope); - } - } - - return invCandidate; -} - - -#ifdef OPT_DEBUG_RETRIEVAL -void OptimizerRetrieval::printCandidate(const InversionCandidate* candidate) const -{ -/************************************** - * - * p r i n t C a n d i d a t e - * - ************************************** - * - * Functional description - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " cost(%1.2f), selectivity(%1.10f), indexes(%d), matched(%d, %d)", - candidate->cost, candidate->selectivity, candidate->indexes, candidate->matchedSegments, - candidate->nonFullMatchedSegments); - if (candidate->unique) - fprintf(opt_debug_file, ", unique"); - int depFromCount = candidate->dependentFromStreams.getCount(); - if (depFromCount >= 1) - { - fprintf(opt_debug_file, ", dependent from "); - for (int i = 0; i < depFromCount; i++) - { - if (i == 0) - fprintf(opt_debug_file, "%d", candidate->dependentFromStreams[i]); - else - fprintf(opt_debug_file, ", %d", candidate->dependentFromStreams[i]); - } - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerRetrieval::printCandidates(const InversionCandidateList* inversions) const -{ -/************************************** - * - * p r i n t C a n d i d a t e s - * - ************************************** - * - * Functional description - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " retrieval candidates:\n"); - fclose(opt_debug_file); - const InversionCandidate* const* inversion = inversions->begin(); - for (int i = 0; i < inversions->getCount(); i++) - { - const InversionCandidate* candidate = inversion[i]; - printCandidate(candidate); - } -} - -void OptimizerRetrieval::printFinalCandidate(const InversionCandidate* candidate) const -{ -/************************************** - * - * p r i n t F i n a l C a n d i d a t e - * - ************************************** - * - * Functional description - * - **************************************/ - - if (candidate) - { - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " final candidate: "); - fclose(opt_debug_file); - printCandidate(candidate); - } -} -#endif - - -bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeBoolNode* cmpNode, - USHORT segment) const -{ -/************************************** - * - * v a l i d a t e S t a r t s - * - ************************************** - * - * Functional description - * Check if the boolean is valid for - * using it against the given index segment. - * - **************************************/ - - fb_assert(cmpNode && cmpNode->blrOp == blr_starting); - if (!cmpNode || cmpNode->blrOp != blr_starting) - return false; - - ValueExprNode* field = cmpNode->arg1; - ValueExprNode* value = cmpNode->arg2; - - if (indexScratch->idx->idx_flags & idx_expressn) - { - // AB: What if the expression contains a number/float etc.. and - // we use starting with against it? Is that allowed? - fb_assert(indexScratch->idx->idx_expression != NULL); - - if (!(checkExpressionIndex(csb, indexScratch->idx, field, stream) || - (value && !value->computable(csb, stream, false)))) - { - // AB: Can we swap de left and right sides by a starting with? - // X STARTING WITH 'a' that is never the same as 'a' STARTING WITH X - if (value && - checkExpressionIndex(csb, indexScratch->idx, value, stream) && - field->computable(csb, stream, false)) - { - field = value; - value = cmpNode->arg1; - } - else - return false; - } - } - else - { - FieldNode* fieldNode = nodeAs(field); - - if (!fieldNode) - { - // dimitr: any idea how we can use an index in this case? - // The code below produced wrong results. - // AB: I don't think that it would be effective, because - // this must include many matches (think about empty string) - return false; - /* - if (!nodeIs(value)) - return NULL; - field = value; - value = cmpNode->arg1; - */ - } - - // Every string starts with an empty string so don't bother using an index in that case. - LiteralNode* literal = nodeAs(value); - - if (literal) - { - if ((literal->litDesc.dsc_dtype == dtype_text && literal->litDesc.dsc_length == 0) || - (literal->litDesc.dsc_dtype == dtype_varying && - literal->litDesc.dsc_length == sizeof(USHORT))) - { - return false; - } - } - - // AB: Check if the index-segment is usable for using starts. - // Thus it should be of type string, etc... - if (fieldNode->fieldStream != stream || - fieldNode->fieldId != indexScratch->idx->idx_rpt[segment].idx_field || - !(indexScratch->idx->idx_rpt[segment].idx_itype == idx_string || - indexScratch->idx->idx_rpt[segment].idx_itype == idx_byte_array || - indexScratch->idx->idx_rpt[segment].idx_itype == idx_metadata || - indexScratch->idx->idx_rpt[segment].idx_itype >= idx_first_intl_string) || - !value->computable(csb, stream, false)) - { - return false; - } - } - - return true; -} - - -OptimizerInnerJoin::OptimizerInnerJoin(MemoryPool& p, OptimizerBlk* opt, const StreamList& streams, - SortNode* sort_clause, PlanNode* plan_clause) - : pool(p), innerStreams(p) -{ -/************************************** - * - * O p t i m i z e r I n n e r J o i n - * - ************************************** - * - * Initialize - * - **************************************/ - tdbb = NULL; - SET_TDBB(tdbb); - this->database = tdbb->getDatabase(); - this->optimizer = opt; - this->csb = this->optimizer->opt_csb; - this->sort = sort_clause; - this->plan = plan_clause; - this->remainingStreams = 0; - - innerStreams.grow(streams.getCount()); - InnerJoinStreamInfo** innerStream = innerStreams.begin(); - for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) - { - innerStream[i] = FB_NEW_POOL(p) InnerJoinStreamInfo(p); - innerStream[i]->stream = streams[i]; - } - - calculateStreamInfo(); -} - -OptimizerInnerJoin::~OptimizerInnerJoin() -{ -/************************************** - * - * ~O p t i m i z e r I n n e r J o i n - * - ************************************** - * - * Finish with giving back memory. - * - **************************************/ - - for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) - { - for (FB_SIZE_T j = 0; j < innerStreams[i]->indexedRelationships.getCount(); j++) - delete innerStreams[i]->indexedRelationships[j]; - - delete innerStreams[i]; - } -} - -void OptimizerInnerJoin::calculateStreamInfo() -{ -/************************************** - * - * c a l c u l a t e S t r e a m I n f o - * - ************************************** - * - * Calculate the needed information for - * all streams. - * - **************************************/ - - FB_SIZE_T i = 0; - // First get the base cost without any relation to an other inner join stream. - for (i = 0; i < innerStreams.getCount(); i++) - { - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[innerStreams[i]->stream]; - csb_tail->activate(); - - OptimizerRetrieval optimizerRetrieval(pool, optimizer, innerStreams[i]->stream, - false, false, sort); - AutoPtr candidate(optimizerRetrieval.getCost()); - - innerStreams[i]->baseCost = candidate->cost; - innerStreams[i]->baseSelectivity = candidate->selectivity; - innerStreams[i]->baseIndexes = candidate->indexes; - innerStreams[i]->baseUnique = candidate->unique; - innerStreams[i]->baseNavigated = candidate->navigated; - - csb_tail->deactivate(); - } - - for (i = 0; i < innerStreams.getCount(); i++) - { - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[innerStreams[i]->stream]; - csb_tail->activate(); - - // Find streams that have a indexed relationship to this - // stream and add the information. - for (FB_SIZE_T j = 0; j < innerStreams.getCount(); j++) - { - if (innerStreams[j]->stream != innerStreams[i]->stream) - getIndexedRelationship(innerStreams[i], innerStreams[j]); - } - - csb_tail->deactivate(); - } - - // Sort the streams based on independecy and cost. - // Except when a PLAN was forced. - if (!plan && (innerStreams.getCount() > 1)) - { - StreamInfoList tempStreams(pool); - - for (i = 0; i < innerStreams.getCount(); i++) - { - FB_SIZE_T index = 0; - for (; index < tempStreams.getCount(); index++) - { - // First those streams which can't be used by other streams - // or can't depend on a stream. - if (innerStreams[i]->isIndependent() && !tempStreams[index]->isIndependent()) - break; - - // Next those with the lowest previous expected streams - const int compare = innerStreams[i]->previousExpectedStreams - - tempStreams[index]->previousExpectedStreams; - - if (compare < 0) - break; - - if (compare == 0) - { - // Next those with the cheapest base cost - if (innerStreams[i]->baseCost < tempStreams[index]->baseCost) - break; - } - } - tempStreams.insert(index, innerStreams[i]); - } - - // Finally update the innerStreams with the sorted streams - innerStreams.clear(); - innerStreams.join(tempStreams); - } -} - -bool OptimizerInnerJoin::cheaperRelationship(IndexRelationship* checkRelationship, - IndexRelationship* withRelationship) const -{ -/************************************** - * - * c h e a p e r R e l a t i o n s h i p - * - ************************************** - * - * Return true if checking relationship - * is cheaper as withRelationship. - * - **************************************/ - if (checkRelationship->cost == 0) - return true; - - if (withRelationship->cost == 0) - return false; - - const double compareValue = checkRelationship->cost / withRelationship->cost; - if (compareValue >= 0.98 && compareValue <= 1.02) - { - // cost is nearly the same, now check uniqueness and cardinality - - if (checkRelationship->unique == withRelationship->unique) - { - if (checkRelationship->cardinality < withRelationship->cardinality) - return true; - } - else if (checkRelationship->unique) - return true; - else if (withRelationship->unique) - return false; - } - else if (checkRelationship->cost < withRelationship->cost) - return true; - - return false; -} - -void OptimizerInnerJoin::estimateCost(StreamType stream, double* cost, - double* resulting_cardinality, bool start) const -{ -/************************************** - * - * e s t i m a t e C o s t - * - ************************************** - * - * Estimate the cost for the stream. - * - **************************************/ - // Create the optimizer retrieval generation class and calculate - // which indexes will be used and the total estimated selectivity will be returned - OptimizerRetrieval optimizerRetrieval(pool, optimizer, stream, false, false, - (start ? sort : NULL)); - AutoPtr candidate(optimizerRetrieval.getCost()); - - *cost = candidate->cost; - - // Calculate cardinality - const CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; - const double cardinality = csb_tail->csb_cardinality * candidate->selectivity; - - *resulting_cardinality = MAX(cardinality, MINIMUM_CARDINALITY); -} - -StreamType OptimizerInnerJoin::findJoinOrder() -{ -/************************************** - * - * f i n d J o i n O r d e r - * - ************************************** - * - * Find the best order out of the streams. - * First return a stream if it can't use - * an index based on a previous stream and - * it can't be used by another stream. - * Next loop through the remaining streams - * and find the best order. - * - **************************************/ - - optimizer->opt_best_count = 0; - -#ifdef OPT_DEBUG - // Debug - printStartOrder(); -#endif - - int filters = 0, navigations = 0; - - FB_SIZE_T i = 0; - remainingStreams = 0; - - for (i = 0; i < innerStreams.getCount(); i++) - { - if (!innerStreams[i]->used) - { - remainingStreams++; - - const int currentFilter = innerStreams[i]->isFiltered() ? 1 : 0; - - if (navigations && currentFilter) - navigations = 0; - - filters += currentFilter; - - if (innerStreams[i]->baseNavigated && currentFilter == filters) - navigations++; - - if (innerStreams[i]->isIndependent()) - { - if (!optimizer->opt_best_count || innerStreams[i]->baseCost < optimizer->opt_best_cost) - { - optimizer->opt_streams[0].opt_best_stream = innerStreams[i]->stream; - optimizer->opt_best_count = 1; - optimizer->opt_best_cost = innerStreams[i]->baseCost; - } - } - } - } - - if (optimizer->opt_best_count == 0) - { - IndexedRelationships indexedRelationships(pool); - - for (i = 0; i < innerStreams.getCount(); i++) - { - if (!innerStreams[i]->used) - { - // If optimization for first rows has been requested and index navigations are - // possible, then consider only join orders starting with a navigational stream. - // Except cases when other streams have local predicates applied. - - const int currentFilter = innerStreams[i]->isFiltered() ? 1 : 0; - - if (!optimizer->favorFirstRows || !navigations || - (innerStreams[i]->baseNavigated && currentFilter == filters)) - { - indexedRelationships.clear(); - findBestOrder(0, innerStreams[i], &indexedRelationships, 0.0, 1.0); - - if (plan) - { - // If a explicit PLAN was specified we should be ready; - break; - } - } -#ifdef OPT_DEBUG - // Debug - printProcessList(&indexedRelationships, innerStreams[i]->stream); -#endif - } - } - } - - // Mark streams as used - for (StreamType stream = 0; stream < optimizer->opt_best_count; stream++) - { - InnerJoinStreamInfo* streamInfo = getStreamInfo(optimizer->opt_streams[stream].opt_best_stream); - streamInfo->used = true; - } - -#ifdef OPT_DEBUG - // Debug - printBestOrder(); -#endif - - return optimizer->opt_best_count; -} - -void OptimizerInnerJoin::findBestOrder(StreamType position, InnerJoinStreamInfo* stream, - IndexedRelationships* processList, double cost, double cardinality) -{ -/************************************** - * - * f i n d B e s t O r d e r - * - ************************************** - * Make different combinations to find - * out the join order. - * For every position we start with the - * stream that has the best selectivity - * for that position. If we've have - * used up all our streams after that - * we assume we're done. - * - **************************************/ - - fb_assert(processList); - - const bool start = (position == 0); - - // do some initializations. - csb->csb_rpt[stream->stream].activate(); - optimizer->opt_streams[position].opt_stream_number = stream->stream; - position++; - const OptimizerBlk::opt_stream* order_end = optimizer->opt_streams.begin() + position; - - // Save the various flag bits from the optimizer block to reset its - // state after each test. - HalfStaticArray streamFlags(pool); - streamFlags.grow(innerStreams.getCount()); - for (FB_SIZE_T i = 0; i < streamFlags.getCount(); i++) - streamFlags[i] = innerStreams[i]->used; - - // Compute delta and total estimate cost to fetch this stream. - double position_cost, position_cardinality, new_cost = 0, new_cardinality = 0; - - if (!plan) - { - estimateCost(stream->stream, &position_cost, &position_cardinality, start); - new_cost = cost + cardinality * position_cost; - new_cardinality = position_cardinality * cardinality; - } - - // If the partial order is either longer than any previous partial order, - // or the same length and cheap, save order as "best". - if (position > optimizer->opt_best_count || - (position == optimizer->opt_best_count && new_cost < optimizer->opt_best_cost)) - { - optimizer->opt_best_count = position; - optimizer->opt_best_cost = new_cost; - for (OptimizerBlk::opt_stream* tail = optimizer->opt_streams.begin(); tail < order_end; tail++) - tail->opt_best_stream = tail->opt_stream_number; - } - -#ifdef OPT_DEBUG - // Debug information - printFoundOrder(position, position_cost, position_cardinality, new_cost, new_cardinality); -#endif - - // mark this stream as "used" in the sense that it is already included - // in this particular proposed stream ordering. - stream->used = true; - bool done = false; - - // if we've used up all the streams there's no reason to go any further. - if (position == remainingStreams) - done = true; - - // If we know a combination with all streams used and the - // current cost is higher as the one from the best we're done. - if ((optimizer->opt_best_count == remainingStreams) && (optimizer->opt_best_cost < new_cost)) - done = true; - - if (!done && !plan) - { - // Add these relations to the processing list - for (FB_SIZE_T j = 0; j < stream->indexedRelationships.getCount(); j++) - { - IndexRelationship* relationship = stream->indexedRelationships[j]; - InnerJoinStreamInfo* relationStreamInfo = getStreamInfo(relationship->stream); - if (!relationStreamInfo->used) - { - bool found = false; - IndexRelationship** processRelationship = processList->begin(); - FB_SIZE_T index; - for (index = 0; index < processList->getCount(); index++) - { - if (relationStreamInfo->stream == processRelationship[index]->stream) - { - // If the cost of this relationship is cheaper then remove the - // old relationship and add this one. - if (cheaperRelationship(relationship, processRelationship[index])) - { - processList->remove(index); - break; - } - - found = true; - break; - } - } - if (!found) - { - // Add relationship sorted on cost (cheapest as first) - IndexRelationship** relationships = processList->begin(); - for (index = 0; index < processList->getCount(); index++) - { - if (cheaperRelationship(relationship, relationships[index])) - break; - } - processList->insert(index, relationship); - } - } - } - - IndexRelationship** nextRelationship = processList->begin(); - for (FB_SIZE_T j = 0; j < processList->getCount(); j++) - { - InnerJoinStreamInfo* relationStreamInfo = getStreamInfo(nextRelationship[j]->stream); - if (!relationStreamInfo->used) - { - findBestOrder(position, relationStreamInfo, processList, new_cost, new_cardinality); - break; - } - } - } - - if (plan) - { - // If a explicit PLAN was specific pick the next relation. - // The order in innerStreams is expected to be exactly the order as - // specified in the explicit PLAN. - for (FB_SIZE_T j = 0; j < innerStreams.getCount(); j++) - { - InnerJoinStreamInfo* nextStream = innerStreams[j]; - if (!nextStream->used) - { - findBestOrder(position, nextStream, processList, new_cost, new_cardinality); - break; - } - } - } - - // Clean up from any changes made for compute the cost for this stream - csb->csb_rpt[stream->stream].deactivate(); - for (FB_SIZE_T i = 0; i < streamFlags.getCount(); i++) - innerStreams[i]->used = streamFlags[i]; -} - -void OptimizerInnerJoin::getIndexedRelationship(InnerJoinStreamInfo* baseStream, - InnerJoinStreamInfo* testStream) -{ -/************************************** - * - * g e t I n d e x e d R e l a t i o n s h i p - * - ************************************** - * - * Check if the testStream can use a index - * when the baseStream is active. If so - * then we create a indexRelationship - * and fill it with the needed information. - * The reference is added to the baseStream - * and the baseStream is added as previous - * expected stream to the testStream. - * - **************************************/ - - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[testStream->stream]; - csb_tail->activate(); - - OptimizerRetrieval optimizerRetrieval(pool, optimizer, testStream->stream, false, false, NULL); - AutoPtr candidate(optimizerRetrieval.getCost()); - - if (candidate->dependentFromStreams.exist(baseStream->stream)) - { - // If we could use more conjunctions on the testing stream - // with the base stream active as without the base stream - // then the test stream has a indexed relationship with the base stream. - IndexRelationship* indexRelationship = FB_NEW_POOL(pool) IndexRelationship(); - indexRelationship->stream = testStream->stream; - indexRelationship->unique = candidate->unique; - indexRelationship->cost = candidate->cost; - indexRelationship->cardinality = candidate->unique ? - csb_tail->csb_cardinality : csb_tail->csb_cardinality * candidate->selectivity; - - // indexRelationship are kept sorted on cost and unique in the indexRelations array. - // The unique and cheapest indexed relatioships are on the first position. - FB_SIZE_T index = 0; - for (; index < baseStream->indexedRelationships.getCount(); index++) - { - if (cheaperRelationship(indexRelationship, baseStream->indexedRelationships[index])) - break; - } - baseStream->indexedRelationships.insert(index, indexRelationship); - testStream->previousExpectedStreams++; - } - - csb_tail->deactivate(); -} - -InnerJoinStreamInfo* OptimizerInnerJoin::getStreamInfo(StreamType stream) -{ -/************************************** - * - * g e t S t r e a m I n f o - * - ************************************** - * - * Return stream information based on - * the stream number. - * - **************************************/ - - for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) - { - if (innerStreams[i]->stream == stream) - return innerStreams[i]; - } - - // We should never come here - fb_assert(false); - return NULL; -} - -#ifdef OPT_DEBUG -void OptimizerInnerJoin::printBestOrder() const -{ -/************************************** - * - * p r i n t B e s t O r d e r - * - ************************************** - * - * Dump finally selected stream order. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " best order, streams: "); - for (StreamType i = 0; i < optimizer->opt_best_count; i++) - { - if (i == 0) - fprintf(opt_debug_file, "%d", optimizer->opt_streams[i].opt_best_stream); - else - fprintf(opt_debug_file, ", %d", optimizer->opt_streams[i].opt_best_stream); - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerInnerJoin::printFoundOrder(StreamType position, double positionCost, - double positionCardinality, double cost, double cardinality) const -{ -/************************************** - * - * p r i n t F o u n d O r d e r - * - ************************************** - * - * Dump currently passed streams to a - * debug file. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " position %2.2d:", position); - fprintf(opt_debug_file, " pos. cardinality(%10.2f) pos. cost(%10.2f)", positionCardinality, positionCost); - fprintf(opt_debug_file, " cardinality(%10.2f) cost(%10.2f)", cardinality, cost); - fprintf(opt_debug_file, ", streams: ", position); - const OptimizerBlk::opt_stream* tail = optimizer->opt_streams.begin(); - const OptimizerBlk::opt_stream* const order_end = tail + position; - for (; tail < order_end; tail++) - { - if (tail == optimizer->opt_streams.begin()) - fprintf(opt_debug_file, "%d", tail->opt_stream_number); - else - fprintf(opt_debug_file, ", %d", tail->opt_stream_number); - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerInnerJoin::printProcessList(const IndexedRelationships* processList, - StreamType stream) const -{ -/************************************** - * - * p r i n t P r o c e s s L i s t - * - ************************************** - * - * Dump the processlist to a debug file. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, " basestream %d, relationships: stream(cost)", stream); - const IndexRelationship* const* relationships = processList->begin(); - for (int i = 0; i < processList->getCount(); i++) - fprintf(opt_debug_file, ", %d (%1.2f)", relationships[i]->stream, relationships[i]->cost); - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} - -void OptimizerInnerJoin::printStartOrder() const -{ -/************************************** - * - * p r i n t B e s t O r d e r - * - ************************************** - * - * Dump finally selected stream order. - * - **************************************/ - - FILE *opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); - fprintf(opt_debug_file, "Start join order: with stream(baseCost)"); - bool firstStream = true; - for (int i = 0; i < innerStreams.getCount(); i++) - { - if (!innerStreams[i]->used) - fprintf(opt_debug_file, ", %d (%1.2f)", innerStreams[i]->stream, innerStreams[i]->baseCost); - } - fprintf(opt_debug_file, "\n"); - fclose(opt_debug_file); -} -#endif diff --git a/src/jrd/Optimizer.h b/src/jrd/Optimizer.h deleted file mode 100644 index 26a146291d..0000000000 --- a/src/jrd/Optimizer.h +++ /dev/null @@ -1,390 +0,0 @@ -/* - * PROGRAM: Client/Server Common Code - * MODULE: Optimizer.h - * DESCRIPTION: Optimizer - * - * The contents of this file are subject to the Initial - * Developer's Public License Version 1.0 (the "License"); - * you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. - * - * Software distributed under the License is distributed AS IS, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. - * See the License for the specific language governing rights - * and limitations under the License. - * - * The Original Code was created by Arno Brinkman - * for the Firebird Open Source RDBMS project. - * - * Copyright (c) 2004 Arno Brinkman - * and all contributors signed below. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - * - * - */ - -#ifndef OPTIMIZER_H -#define OPTIMIZER_H - -//#define OPT_DEBUG -//#define OPT_DEBUG_RETRIEVAL - -//#ifdef OPT_DEBUG -#define OPTIMIZER_DEBUG_FILE "opt_debug.out" -//#endif - -#include "../common/classes/alloc.h" -#include "../common/classes/array.h" -#include "../jrd/RecordSourceNodes.h" -#include "../jrd/rse.h" -#include "../jrd/exe.h" - -namespace Jrd { - - -// AB: 2005-11-05 -// Constants below needs some discussions and ideas -const double REDUCE_SELECTIVITY_FACTOR_BETWEEN = 0.0025; -const double REDUCE_SELECTIVITY_FACTOR_LESS = 0.05; -const double REDUCE_SELECTIVITY_FACTOR_GREATER = 0.05; -const double REDUCE_SELECTIVITY_FACTOR_STARTING = 0.01; - -const double REDUCE_SELECTIVITY_FACTOR_EQUALITY = 0.1; -const double REDUCE_SELECTIVITY_FACTOR_INEQUALITY = 0.3; - -const double MAXIMUM_SELECTIVITY = 1.0; -const double DEFAULT_SELECTIVITY = 0.1; - -const double MINIMUM_CARDINALITY = 1.0; -const double THRESHOLD_CARDINALITY = 5.0; - -// Default depth of an index tree (including one leaf page), -// also representing the minimal cost of the index scan. -// We assume that the root page would be always cached, -// so it's not included here. -const int DEFAULT_INDEX_COST = 3; - - -struct index_desc; -class OptimizerBlk; -class jrd_rel; -class IndexTableScan; -class ComparativeBoolNode; -class InversionNode; -class PlanNode; -class SortNode; - -Firebird::string OPT_make_alias(const CompilerScratch*, StreamType stream); - -enum segmentScanType { - segmentScanNone, - segmentScanGreater, - segmentScanLess, - segmentScanBetween, - segmentScanEqual, - segmentScanEquivalent, - segmentScanMissing, - segmentScanStarting -}; - -class IndexScratchSegment -{ -public: - explicit IndexScratchSegment(MemoryPool& p); - IndexScratchSegment(MemoryPool& p, IndexScratchSegment* segment); - - - ValueExprNode* lowerValue; // lower bound on index value - ValueExprNode* upperValue; // upper bound on index value - bool excludeLower; // exclude lower bound value from scan - bool excludeUpper; // exclude upper bound value from scan - int scope; // highest scope level - segmentScanType scanType; // scan type - - Firebird::Array matches; -}; - -class IndexScratch -{ -public: - IndexScratch(MemoryPool& p, thread_db* tdbb, index_desc* idx, CompilerScratch::csb_repeat* csb_tail); - IndexScratch(MemoryPool& p, const IndexScratch& scratch); - ~IndexScratch(); - - index_desc* idx; // index descriptor - double selectivity; // calculated selectivity for this index - bool candidate; // used when deciding which indices to use - bool scopeCandidate; // used when making inversion based on scope - int lowerCount; // - int upperCount; // - int nonFullMatchedSegments; // - bool fuzzy; // Need to use INTL_KEY_PARTIAL in btr lookups - double cardinality; // Estimated cardinality when using the whole index - - Firebird::Array segments; -}; - -class InversionCandidate -{ -public: - explicit InversionCandidate(MemoryPool& p); - - double selectivity; - double cost; - USHORT nonFullMatchedSegments; - USHORT matchedSegments; - int indexes; - int dependencies; - BoolExprNode* boolean; - BoolExprNode* condition; - InversionNode* inversion; - IndexScratch* scratch; - bool used; - bool unique; - bool navigated; - - Firebird::Array matches; - Firebird::Array dbkeyRanges; - SortedStreamList dependentFromStreams; -}; - -typedef Firebird::HalfStaticArray InversionCandidateList; -typedef Firebird::ObjectsArray IndexScratchList; - -class OptimizerRetrieval -{ -public: - OptimizerRetrieval(MemoryPool& p, OptimizerBlk* opt, StreamType streamNumber, - bool outer, bool inner, SortNode* sortNode); - ~OptimizerRetrieval(); - - MemoryPool& getPool() const - { - return pool; - } - - InversionCandidate* getInversion() - { - createIndexScanNodes = true; - setConjunctionsMatched = true; - - return generateInversion(); - } - - InversionCandidate* getCost() - { - createIndexScanNodes = false; - setConjunctionsMatched = false; - - return generateInversion(); - } - - IndexTableScan* getNavigation(); - -protected: - void analyzeNavigation(const InversionCandidateList& inversions); - bool betterInversion(const InversionCandidate* inv1, const InversionCandidate* inv2, - bool ignoreUnmatched) const; - InversionNode* composeInversion(InversionNode* node1, InversionNode* node2, - InversionNode::Type node_type) const; - const Firebird::string& getAlias(); - InversionCandidate* generateInversion(); - void getInversionCandidates(InversionCandidateList* inversions, - IndexScratchList* indexScratches, USHORT scope) const; - InversionNode* makeIndexScanNode(IndexScratch* indexScratch) const; - InversionCandidate* makeInversion(InversionCandidateList* inversions) const; - bool matchBoolean(IndexScratch* indexScratch, BoolExprNode* boolean, USHORT scope) const; - InversionCandidate* matchDbKey(BoolExprNode* boolean) const; - InversionCandidate* matchOnIndexes(IndexScratchList* indexScratches, - BoolExprNode* boolean, USHORT scope) const; - ValueExprNode* findDbKey(ValueExprNode* dbkey, SLONG* position) const; - -#ifdef OPT_DEBUG_RETRIEVAL - void printCandidate(const InversionCandidate* candidate) const; - void printCandidates(const InversionCandidateList* inversions) const; - void printFinalCandidate(const InversionCandidate* candidate) const; -#endif - - bool validateStarts(IndexScratch* indexScratch, ComparativeBoolNode* cmpNode, - USHORT segment) const; - -private: - MemoryPool& pool; - thread_db* tdbb; - -public: - StreamType stream; - Firebird::string alias; - SortNode* sort; - jrd_rel* relation; - CompilerScratch* csb; - Database* database; - OptimizerBlk* optimizer; - IndexScratchList indexScratches; - InversionCandidateList inversionCandidates; - bool innerFlag; - bool outerFlag; - bool createIndexScanNodes; - bool setConjunctionsMatched; - InversionCandidate* navigationCandidate; -}; - -class IndexRelationship -{ -public: - IndexRelationship() - : stream(0), unique(false), cost(0), cardinality(0) - {} - - StreamType stream; - bool unique; - double cost; - double cardinality; -}; - -typedef Firebird::Array IndexedRelationships; - -class InnerJoinStreamInfo -{ -public: - explicit InnerJoinStreamInfo(MemoryPool& p) - : indexedRelationships(p), - stream(0), baseUnique(false), baseCost(0), baseSelectivity(0), - baseIndexes(0), baseNavigated(false), - used(false), previousExpectedStreams(0) - {} - - bool isIndependent() const - { - // Return true if this stream can't be used by other streams - // and it can't use index retrieval based on other streams - - return (indexedRelationships.isEmpty() && !previousExpectedStreams); - } - - bool isFiltered() const - { - return (baseIndexes || baseSelectivity < MAXIMUM_SELECTIVITY); - } - - IndexedRelationships indexedRelationships; - StreamType stream; - bool baseUnique; - double baseCost; - double baseSelectivity; - int baseIndexes; - bool baseNavigated; - bool used; - int previousExpectedStreams; -}; - -typedef Firebird::HalfStaticArray StreamInfoList; - -class OptimizerInnerJoin -{ -public: - OptimizerInnerJoin(MemoryPool& p, OptimizerBlk* opt, const StreamList& streams, - SortNode* sort_clause, PlanNode* plan_clause); - ~OptimizerInnerJoin(); - - StreamType findJoinOrder(); - -protected: - void calculateStreamInfo(); - bool cheaperRelationship(IndexRelationship* checkRelationship, - IndexRelationship* withRelationship) const; - void estimateCost(StreamType stream, double* cost, double* resulting_cardinality, bool start) const; - void findBestOrder(StreamType position, InnerJoinStreamInfo* stream, - IndexedRelationships* processList, double cost, double cardinality); - void getIndexedRelationship(InnerJoinStreamInfo* baseStream, InnerJoinStreamInfo* testStream); - InnerJoinStreamInfo* getStreamInfo(StreamType stream); -#ifdef OPT_DEBUG - void printBestOrder() const; - void printFoundOrder(StreamType position, double positionCost, - double positionCardinality, double cost, double cardinality) const; - void printProcessList(const IndexedRelationships* processList, StreamType stream) const; - void printStartOrder() const; -#endif - -private: - MemoryPool& pool; - thread_db* tdbb; - SortNode* sort; - PlanNode* plan; - CompilerScratch* csb; - Database* database; - OptimizerBlk* optimizer; - StreamInfoList innerStreams; - StreamType remainingStreams; -}; - -class StreamStateHolder -{ -public: - explicit StreamStateHolder(CompilerScratch* csb) - : m_csb(csb), m_streams(csb->csb_pool), m_flags(csb->csb_pool) - { - for (StreamType stream = 0; stream < csb->csb_n_stream; stream++) - m_streams.add(stream); - - init(); - } - - StreamStateHolder(CompilerScratch* csb, const StreamList& streams) - : m_csb(csb), m_streams(csb->csb_pool), m_flags(csb->csb_pool) - { - m_streams.assign(streams); - - init(); - } - - ~StreamStateHolder() - { - for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) - { - const StreamType stream = m_streams[i]; - - if (m_flags[i >> 3] & (1 << (i & 7))) - m_csb->csb_rpt[stream].activate(); - else - m_csb->csb_rpt[stream].deactivate(); - } - } - - void activate() - { - for (const StreamType* iter = m_streams.begin(); iter != m_streams.end(); ++iter) - m_csb->csb_rpt[*iter].activate(); - } - - void deactivate() - { - for (const StreamType* iter = m_streams.begin(); iter != m_streams.end(); ++iter) - m_csb->csb_rpt[*iter].deactivate(); - } - -private: - void init() - { - m_flags.resize(FLAG_BYTES(m_streams.getCount())); - - for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) - { - const StreamType stream = m_streams[i]; - - if (m_csb->csb_rpt[stream].csb_flags & csb_active) - m_flags[i >> 3] |= (1 << (i & 7)); - } - } - - CompilerScratch* const m_csb; - StreamList m_streams; - Firebird::HalfStaticArray m_flags; -}; - -} // namespace Jrd - -#endif // OPTIMIZER_H diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index e58a309d3d..371e1c161a 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -302,7 +302,7 @@ PreparedStatement::~PreparedStatement() { thread_db* tdbb = JRD_get_thread_data(); - DSQL_free_statement(tdbb, request, DSQL_drop); + DSQL_free_statement(tdbb, dsqlRequest, DSQL_drop); if (resultSet) resultSet->stmt = NULL; @@ -315,30 +315,28 @@ void PreparedStatement::init(thread_db* tdbb, Attachment* attachment, jrd_tra* t AutoSetRestore autoAttCharset(&attachment->att_charset, (isInternalRequest ? CS_METADATA : attachment->att_charset)); - request = NULL; + dsqlRequest = NULL; try { const Database& dbb = *tdbb->getDatabase(); const int dialect = isInternalRequest || (dbb.dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; - request = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, 0, + dsqlRequest = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, 0, NULL, NULL, isInternalRequest); - const DsqlCompiledStatement* statement = request->getStatement(); + const auto dsqlStatement = dsqlRequest->getDsqlStatement(); - if (statement->getSendMsg()) - parseDsqlMessage(statement->getSendMsg(), inValues, inMetadata, inMessage); + if (dsqlStatement->getSendMsg()) + parseDsqlMessage(dsqlStatement->getSendMsg(), inValues, inMetadata, inMessage); - if (statement->getReceiveMsg()) - parseDsqlMessage(statement->getReceiveMsg(), outValues, outMetadata, outMessage); + if (dsqlStatement->getReceiveMsg()) + parseDsqlMessage(dsqlStatement->getReceiveMsg(), outValues, outMetadata, outMessage); } catch (const Exception&) { - if (request) - { - DSQL_free_statement(tdbb, request, DSQL_drop); - } + if (dsqlRequest) + DSQL_free_statement(tdbb, dsqlRequest, DSQL_drop); throw; } } @@ -348,12 +346,12 @@ void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& valu { fb_assert(param > 0); - jrd_req* jrdRequest = getRequest()->req_request; + Request* request = getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. - AutoSetRestore2 autoRequest( - tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); - AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, + AutoSetRestore2 autoRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); MOV_move(tdbb, const_cast(&value), &inValues[(param - 1) * 2]); @@ -371,7 +369,7 @@ void PreparedStatement::execute(thread_db* tdbb, jrd_tra* transaction) if (builder) builder->moveToStatement(tdbb, this); - DSQL_execute(tdbb, &transaction, request, inMetadata, inMessage.begin(), NULL, NULL); + DSQL_execute(tdbb, &transaction, dsqlRequest, inMetadata, inMessage.begin(), NULL, NULL); } @@ -382,13 +380,13 @@ void PreparedStatement::open(thread_db* tdbb, jrd_tra* transaction) if (builder) builder->moveToStatement(tdbb, this); - DSQL_open(tdbb, &transaction, request, inMetadata, inMessage.begin(), outMetadata, 0); + dsqlRequest->openCursor(tdbb, &transaction, inMetadata, inMessage.begin(), outMetadata, 0); } ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction) { - fb_assert(resultSet == NULL && request->getStatement()->getReceiveMsg()); + fb_assert(resultSet == NULL && dsqlRequest->getDsqlStatement()->getReceiveMsg()); if (builder) builder->moveToStatement(tdbb, this); @@ -400,7 +398,7 @@ ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction unsigned PreparedStatement::executeUpdate(thread_db* tdbb, jrd_tra* transaction) { execute(tdbb, transaction); - return getRequest()->req_request->req_records_updated; + return getDsqlRequest()->getRequest()->req_records_updated; } diff --git a/src/jrd/PreparedStatement.h b/src/jrd/PreparedStatement.h index 9c37ae611f..703c1d42b6 100644 --- a/src/jrd/PreparedStatement.h +++ b/src/jrd/PreparedStatement.h @@ -40,7 +40,7 @@ namespace Jrd { class thread_db; class jrd_tra; class Attachment; -class dsql_req; +class DsqlRequest; class dsql_msg; class ResultSet; @@ -359,9 +359,9 @@ public: int getResultCount() const; - dsql_req* getRequest() + DsqlRequest* getDsqlRequest() { - return request; + return dsqlRequest; } static void parseDsqlMessage(const dsql_msg* dsqlMsg, Firebird::Array& values, @@ -369,7 +369,7 @@ public: private: const Builder* builder; - dsql_req* request; + DsqlRequest* dsqlRequest; Firebird::Array inValues, outValues; Firebird::RefPtr inMetadata, outMetadata; Firebird::UCharBuffer inMessage, outMessage; diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 9605f06563..9107a9117b 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -22,7 +22,6 @@ #include "../jrd/align.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/DataTypeUtil.h" -#include "../jrd/Optimizer.h" #include "../jrd/recsrc/RecordSource.h" #include "../dsql/BoolNodes.h" #include "../dsql/ExprNodes.h" @@ -32,12 +31,12 @@ #include "../jrd/cmp_proto.h" #include "../common/dsc_proto.h" #include "../jrd/met_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/gen_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/pass1_proto.h" +#include "../jrd/optimizer/Optimizer.h" using namespace Firebird; using namespace Jrd; @@ -49,50 +48,11 @@ static int strcmpSpace(const char* p, const char* q); static void processSource(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, RecordSourceNode* source, BoolExprNode** boolean, RecordSourceNodeStack& stack); static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Format** inputFormat); -static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverStack, MapNode* map, - BoolExprNodeStack* parentStack, StreamType shellStream); +static void genDeliverUnmapped(CompilerScratch* csb, const BoolExprNodeStack& parentStack, + BoolExprNodeStack& deliverStack, MapNode* map, StreamType shellStream); static ValueExprNode* resolveUsingField(DsqlCompilerScratch* dsqlScratch, const MetaName& name, ValueListNode* list, const FieldNode* flawedNode, const TEXT* side, dsql_ctx*& ctx); -namespace -{ - class AutoActivateResetStreams : public AutoStorage - { - public: - AutoActivateResetStreams(CompilerScratch* csb, const RseNode* rse) - : m_csb(csb), m_streams(getPool()), m_flags(getPool()) - { - rse->computeRseStreams(m_streams); - - if (m_streams.getCount() >= MAX_STREAMS) - ERR_post(Arg::Gds(isc_too_many_contexts)); - - m_flags.resize(m_streams.getCount()); - - for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) - { - const StreamType stream = m_streams[i]; - m_flags[i] = m_csb->csb_rpt[stream].csb_flags; - m_csb->csb_rpt[stream].csb_flags |= (csb_active | csb_sub_stream); - } - } - - ~AutoActivateResetStreams() - { - for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) - { - const StreamType stream = m_streams[i]; - m_csb->csb_rpt[stream].csb_flags = m_flags[i]; - } - } - - private: - CompilerScratch* m_csb; - StreamList m_streams; - HalfStaticArray m_flags; - }; -} - //-------------------- @@ -136,9 +96,6 @@ SortNode* SortNode::pass1(thread_db* tdbb, CompilerScratch* csb) SortNode* SortNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) - (*i)->nodFlags |= ExprNode::FLAG_VALUE; - for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) ExprNode::doPass2(tdbb, csb, i->getAddress()); @@ -156,11 +113,11 @@ bool SortNode::computable(CompilerScratch* csb, StreamType stream, bool allowOnl return true; } -void SortNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void SortNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) - (*i)->findDependentFromStreams(optRet, streamList); + (*i)->findDependentFromStreams(csb, currentStream, streamList); } @@ -573,14 +530,11 @@ void LocalTableSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) pass2(tdbb, csb); } -RecordSource* LocalTableSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* LocalTableSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - opt->localStreams.add(stream); + const auto csb = opt->getCompilerScratch(); - const auto csb = opt->opt_csb; - - if (tableNumber >= opt->opt_csb->csb_localTables.getCount() || !opt->opt_csb->csb_localTables[tableNumber]) + if (tableNumber >= csb->csb_localTables.getCount() || !csb->csb_localTables[tableNumber]) ERR_post(Arg::Gds(isc_bad_loctab_num) << Arg::Num(tableNumber)); auto localTable = csb->csb_localTables[tableNumber]; @@ -770,7 +724,7 @@ RecordSourceNode* RelationSourceNode::pass1(thread_db* tdbb, CompilerScratch* cs const SLONG ssRelationId = tail->csb_view ? tail->csb_view->rel_id : view ? view->rel_id : csb->csb_view ? csb->csb_view->rel_id : 0; CMP_post_access(tdbb, csb, relation->rel_security_name, ssRelationId, - SCL_select, SCL_object_table, relation->rel_name); + SCL_select, obj_relations, relation->rel_name); } return this; @@ -858,12 +812,11 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN // disect view into component relations - NestConst* arg = viewRse->rse_relations.begin(); - for (const NestConst* const end = viewRse->rse_relations.end(); arg != end; ++arg) + for (const auto sub : viewRse->rse_relations) { // this call not only copies the node, it adds any streams it finds to the map NodeCopier copier(csb->csb_pool, csb, map); - RecordSourceNode* node = (*arg)->copy(tdbb, copier); + RecordSourceNode* node = sub->copy(tdbb, copier); // Now go out and process the base table itself. This table might also be a view, // in which case we will continue the process by recursion. @@ -919,22 +872,9 @@ void RelationSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) pass2(tdbb, csb); } -RecordSource* RelationSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* RelationSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - // we have found a base relation; record its stream - // number in the streams array as a candidate for - // merging into a river - - opt->beds.add(stream); - opt->compileStreams.add(stream); - - // if we have seen any booleans or sort fields, we may be able to - // use an index to optimize them; retrieve the current format of - // all indices at this time so we can determine if it's possible - - const bool needIndices = - opt->opt_conjuncts.getCount() || opt->rse->rse_sorted || opt->rse->rse_aggregate; - OPT_compile_relation(tdbb, relation, opt->opt_csb, stream, needIndices); + opt->compileRelation(stream); return NULL; } @@ -949,6 +889,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch { SET_TDBB(tdbb); + const auto blrStartPos = csb->csb_blr_reader.getPos(); jrd_prc* procedure = NULL; string* aliasString = NULL; QualifiedName name; @@ -1015,6 +956,25 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch if (!procedure) PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); + else + { + if (procedure->isImplemented() && !procedure->isDefined()) + { + if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) + { + PAR_warning( + Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Warning(isc_modnotfound)); + } + else + { + csb->csb_blr_reader.setPos(blrStartPos); + PAR_error(csb, + Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) << + Arg::Gds(isc_modnotfound)); + } + } + } if (procedure->prc_type == prc_executable) { @@ -1296,21 +1256,10 @@ void ProcedureSourceNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) pass2(tdbb, csb); } -RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - opt->localStreams.add(stream); - - return generate(tdbb, opt); -} - -// Compile and optimize a record selection expression into a set of record source blocks (rsb's). -ProcedureScan* ProcedureSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt) -{ - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; - const string alias = OPT_make_alias(csb, stream); + const auto csb = opt->getCompilerScratch(); + const string alias = opt->makeAlias(stream); return FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureScan(csb, alias, stream, procedure, sourceList, targetList, in_msg); @@ -1328,14 +1277,14 @@ bool ProcedureSourceNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void ProcedureSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void ProcedureSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { if (sourceList) - sourceList->findDependentFromStreams(optRet, streamList); + sourceList->findDependentFromStreams(csb, currentStream, streamList); if (targetList) - targetList->findDependentFromStreams(optRet, streamList); + targetList->findDependentFromStreams(csb, currentStream, streamList); } void ProcedureSourceNode::collectStreams(SortedStreamList& streamList) const @@ -1646,37 +1595,19 @@ bool AggregateSourceNode::containsStream(StreamType checkStream) const return false; } -RecordSource* AggregateSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* AggregateSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - - BoolExprNodeStack conjunctStack; - for (USHORT i = 0; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); - - RecordSource* const rsb = generate(tdbb, opt, &conjunctStack, stream); - - opt->localStreams.add(stream); - - return rsb; -} - -// Generate a RecordSource (Record Source Block) for each aggregate operation. -// Generate an AggregateSort (Aggregate SortedStream Block) for each DISTINCT aggregate. -RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, - BoolExprNodeStack* parentStack, StreamType shellStream) -{ - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; + const auto csb = opt->getCompilerScratch(); rse->rse_sorted = group; // AB: Try to distribute items from the HAVING CLAUSE to the WHERE CLAUSE. // Zip thru stack of booleans looking for fields that belong to shellStream. // Those fields are mappings. Mappings that hold a plain field may be used // to distribute. Handle the simple cases only. - BoolExprNodeStack deliverStack; - genDeliverUnmapped(csb, &deliverStack, map, parentStack, shellStream); + BoolExprNodeStack parentStack, deliverStack; + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + parentStack.push(*iter); + genDeliverUnmapped(csb, parentStack, deliverStack, map, stream); // try to optimize MAX and MIN to use an index; for now, optimize // only the simplest case, although it is probably possible @@ -1704,7 +1635,7 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, rse->flags |= RseNode::FLAG_OPT_FIRST_ROWS; } - RecordSource* const nextRsb = OPT_compile(tdbb, csb, rse, &deliverStack); + RecordSource* const nextRsb = opt->compile(rse, &deliverStack); // allocate and optimize the record source block @@ -1719,7 +1650,7 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, aggNode->indexed = true; } - OPT_gen_aggregate_distincts(tdbb, csb, map); + opt->generateAggregateDistincts(map); return rsb; } @@ -1731,11 +1662,11 @@ bool AggregateSourceNode::computable(CompilerScratch* csb, StreamType stream, return rse->computable(csb, stream, allowOnlyCurrentStream, NULL); } -void AggregateSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void AggregateSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { rse->rse_sorted = group; - rse->findDependentFromStreams(optRet, streamList); + rse->findDependentFromStreams(csb, currentStream, streamList); } @@ -1989,36 +1920,17 @@ bool UnionSourceNode::containsStream(StreamType checkStream) const return false; } -RecordSource* UnionSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* UnionSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - opt->beds.add(stream); - - const FB_SIZE_T oldCount = opt->keyStreams.getCount(); - computeDbKeyStreams(opt->keyStreams); - - BoolExprNodeStack conjunctStack; - for (USHORT i = 0; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); - - RecordSource* const rsb = generate(tdbb, opt, opt->keyStreams.begin() + oldCount, - opt->keyStreams.getCount() - oldCount, &conjunctStack, stream); - - opt->localStreams.add(stream); - - return rsb; -} - -// Generate an union complex. -RecordSource* UnionSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, const StreamType* streams, - FB_SIZE_T nstreams, BoolExprNodeStack* parentStack, StreamType shellStream) -{ - SET_TDBB(tdbb); - - CompilerScratch* csb = opt->opt_csb; + const auto csb = opt->getCompilerScratch(); HalfStaticArray rsbs; const ULONG baseImpure = csb->allocImpure(FB_ALIGNMENT, 0); + BoolExprNodeStack parentStack; + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + parentStack.push(*iter); + NestConst* ptr = clauses.begin(); NestConst* ptr2 = maps.begin(); @@ -2032,9 +1944,9 @@ RecordSource* UnionSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, cons // hvlad: don't do it for recursive unions else they will work wrong ! BoolExprNodeStack deliverStack; if (!recursive) - genDeliverUnmapped(csb, &deliverStack, map, parentStack, shellStream); + genDeliverUnmapped(csb, parentStack, deliverStack, map, stream); - rsbs.add(OPT_compile(tdbb, csb, rse, &deliverStack)); + rsbs.add(opt->compile(rse, &deliverStack)); // hvlad: activate recursive union itself after processing first (non-recursive) // member to allow recursive members be optimized @@ -2042,17 +1954,20 @@ RecordSource* UnionSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, cons csb->csb_rpt[stream].activate(); } + StreamList keyStreams; + computeDbKeyStreams(keyStreams); + if (recursive) { fb_assert(rsbs.getCount() == 2 && maps.getCount() == 2); // hvlad: save size of inner impure area and context of mapped record // for recursive processing later return FB_NEW_POOL(*tdbb->getDefaultPool()) RecursiveStream(csb, stream, mapStream, - rsbs[0], rsbs[1], maps[0], maps[1], nstreams, streams, baseImpure); + rsbs[0], rsbs[1], maps[0], maps[1], keyStreams, baseImpure); } return FB_NEW_POOL(*tdbb->getDefaultPool()) Union(csb, stream, clauses.getCount(), rsbs.begin(), - maps.begin(), nstreams, streams); + maps.begin(), keyStreams); } // Identify all of the streams for which a dbkey may need to be carried through a sort. @@ -2078,13 +1993,11 @@ bool UnionSourceNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void UnionSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void UnionSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - NestConst* ptr = clauses.begin(); - - for (NestConst* const end = clauses.end(); ptr != end; ++ptr) - (*ptr)->findDependentFromStreams(optRet, streamList); + for (auto clause : clauses) + clause->findDependentFromStreams(csb, currentStream, streamList); } @@ -2424,24 +2337,12 @@ void WindowSourceNode::collectStreams(SortedStreamList& streamList) const } } -RecordSource* WindowSourceNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool /*innerSubStream*/) +RecordSource* WindowSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool /*innerSubStream*/) { - for (ObjectsArray::iterator window = windows.begin(); - window != windows.end(); - ++window) - { - opt->beds.add(window->stream); - } + const auto csb = opt->getCompilerScratch(); - RecordSource* const rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) WindowedStream(tdbb, opt->opt_csb, - windows, OPT_compile(tdbb, opt->opt_csb, rse, NULL)); - - StreamList rsbStreams; - rsb->findUsedStreams(rsbStreams); - - opt->localStreams.join(rsbStreams); - - return rsb; + return FB_NEW_POOL(*tdbb->getDefaultPool()) WindowedStream(tdbb, opt, + windows, opt->compile(rse, NULL)); } bool WindowSourceNode::computable(CompilerScratch* csb, StreamType stream, @@ -2460,10 +2361,10 @@ void WindowSourceNode::computeRseStreams(StreamList& streamList) const } } -void WindowSourceNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void WindowSourceNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { - rse->findDependentFromStreams(optRet, streamList); + rse->findDependentFromStreams(csb, currentStream, streamList); } @@ -2841,10 +2742,8 @@ RseNode* RseNode::copy(thread_db* tdbb, NodeCopier& copier) const { RseNode* newSource = FB_NEW_POOL(*tdbb->getDefaultPool()) RseNode(*tdbb->getDefaultPool()); - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - newSource->rse_relations.add((*ptr)->copy(tdbb, copier)); + for (const auto sub : rse_relations) + newSource->rse_relations.add(sub->copy(tdbb, copier)); newSource->flags = flags; newSource->rse_jointype = rse_jointype; @@ -2876,10 +2775,9 @@ RseNode* RseNode::pass1(thread_db* tdbb, CompilerScratch* csb) bool topLevelRse = true; - for (ExprNode** node = csb->csb_current_nodes.begin(); - node != csb->csb_current_nodes.end(); ++node) + for (const auto node : csb->csb_current_nodes) { - if (nodeAs(*node)) + if (nodeAs(node)) { topLevelRse = false; break; @@ -2900,14 +2798,13 @@ RseNode* RseNode::pass1(thread_db* tdbb, CompilerScratch* csb) PlanNode* plan = rse_plan; // zip thru RseNode expanding views and inner joins - NestConst* arg = rse_relations.begin(); - for (const NestConst* const end = rse_relations.end(); arg != end; ++arg) - processSource(tdbb, csb, this, *arg, &boolean, stack); + for (auto sub : rse_relations) + processSource(tdbb, csb, this, sub, &boolean, stack); // Now, rebuild the RseNode block. rse_relations.resize(stack.getCount()); - arg = rse_relations.end(); + auto arg = rse_relations.end(); while (stack.hasData()) *--arg = stack.pop(); @@ -2984,9 +2881,8 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, !rse_sorted && !rse_projection && !rse_first && !rse_skip && !rse_plan) { - NestConst* arg = rse_relations.begin(); - for (const NestConst* const end = rse_relations.end(); arg != end; ++arg) - processSource(tdbb, csb, rse, *arg, boolean, stack); + for (auto sub : rse_relations) + processSource(tdbb, csb, rse, sub, boolean, stack); // fold in the boolean for this inner join with the one for the parent @@ -3029,10 +2925,8 @@ void RseNode::pass2Rse(thread_db* tdbb, CompilerScratch* csb) if (rse_skip) ExprNode::doPass2(tdbb, csb, rse_skip.getAddress()); - NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->pass2Rse(tdbb, csb); + for (auto sub : rse_relations) + sub->pass2Rse(tdbb, csb); ExprNode::doPass2(tdbb, csb, rse_boolean.getAddress()); ExprNode::doPass2(tdbb, csb, rse_sorted.getAddress()); @@ -3055,12 +2949,8 @@ bool RseNode::containsStream(StreamType checkStream) const // Look through all relation nodes in this RseNode to see // if the field references this instance of the relation. - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) + for (const auto sub : rse_relations) { - const RecordSourceNode* sub = *ptr; - if (sub->containsStream(checkStream)) return true; // do not mark as variant } @@ -3068,15 +2958,13 @@ bool RseNode::containsStream(StreamType checkStream) const return false; } -RecordSource* RseNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) +RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream) { - // for nodes which are not relations, generate an rsb to + // For nodes which are not relations, generate an rsb to // represent that work has to be done to retrieve them; // find all the substreams involved and compile them as well - computeRseStreams(opt->beds); - computeRseStreams(opt->localStreams); - computeDbKeyStreams(opt->keyStreams); + const auto csb = opt->getCompilerScratch(); BoolExprNodeStack conjunctStack; @@ -3084,8 +2972,7 @@ RecordSource* RseNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSub // pass RseNode boolean only to inner substreams because join condition // should never exclude records from outer substreams - if (opt->rse->rse_jointype == blr_inner || - (opt->rse->rse_jointype == blr_left && innerSubStream)) + if (opt->isInnerJoin() || (opt->isLeftJoin() && innerSubStream)) { // AB: For an (X LEFT JOIN Y) mark the outer-streams (X) as // active because the inner-streams (Y) are always "dependent" @@ -3093,49 +2980,33 @@ RecordSource* RseNode::compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSub // // dimitr: the same for lateral derived tables in inner joins - if (opt->rse->rse_jointype == blr_left || isLateral) - { - for (StreamList::iterator i = opt->outerStreams.begin(); - i != opt->outerStreams.end(); - ++i) - { - opt->opt_csb->csb_rpt[*i].activate(); - } + StreamStateHolder stateHolder(csb, opt->getOuterStreams()); - if (opt->rse->rse_jointype == blr_left) + if (opt->isLeftJoin() || isLateral) + { + stateHolder.activate(); + + if (opt->isLeftJoin()) { // Push all conjuncts except "missing" ones (e.g. IS NULL) - - for (USHORT i = 0; i < opt->opt_base_missing_conjuncts; i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(false, true); iter.hasData(); ++iter) + conjunctStack.push(iter); } } else { - for (USHORT i = 0; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); + for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter) + conjunctStack.push(iter); } - RecordSource* const rsb = OPT_compile(tdbb, opt->opt_csb, this, &conjunctStack); - - if (opt->rse->rse_jointype == blr_left || isLateral) - { - for (StreamList::iterator i = opt->outerStreams.begin(); - i != opt->outerStreams.end(); ++i) - { - opt->opt_csb->csb_rpt[*i].deactivate(); - } - } - - return rsb; + return opt->compile(this, &conjunctStack); } // Push only parent conjuncts to the outer stream + for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter) + conjunctStack.push(iter); - for (USHORT i = opt->opt_base_parent_conjuncts; i < opt->opt_conjuncts.getCount(); i++) - conjunctStack.push(opt->opt_conjuncts[i].opt_conjunct_node); - - return OPT_compile(tdbb, opt->opt_csb, this, &conjunctStack); + return opt->compile(this, &conjunctStack); } // Check that all streams in the RseNode have a plan specified for them. @@ -3144,11 +3015,8 @@ void RseNode::planCheck(const CompilerScratch* csb) const { // if any streams are not marked with a plan, give an error - const NestConst* ptr = rse_relations.begin(); - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) + for (const auto node : rse_relations) { - const RecordSourceNode* node = *ptr; - if (nodeIs(node)) { const StreamType stream = node->getStream(); @@ -3159,8 +3027,8 @@ void RseNode::planCheck(const CompilerScratch* csb) const Arg::Str(csb->csb_rpt[stream].csb_relation->rel_name)); } } - else if (nodeIs(node)) - static_cast(node)->planCheck(csb); + else if (const auto rse = nodeAs(node)) + rse->planCheck(csb); } } @@ -3171,12 +3039,8 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan) { if (plan->type == PlanNode::TYPE_JOIN) { - for (NestConst* ptr = plan->subNodes.begin(), *end = plan->subNodes.end(); - ptr != end; - ++ptr) - { - planSet(csb, *ptr); - } + for (auto planNode : plan->subNodes) + planSet(csb, planNode); } if (plan->type != PlanNode::TYPE_RETRIEVE) @@ -3356,18 +3220,14 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan) void RseNode::computeDbKeyStreams(StreamList& streamList) const { - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->computeDbKeyStreams(streamList); + for (const auto sub : rse_relations) + sub->computeDbKeyStreams(streamList); } void RseNode::computeRseStreams(StreamList& streamList) const { - const NestConst* ptr = rse_relations.begin(); - - for (const NestConst* const end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->computeRseStreams(streamList); + for (const auto sub : rse_relations) + sub->computeRseStreams(streamList); } bool RseNode::computable(CompilerScratch* csb, StreamType stream, @@ -3379,11 +3239,11 @@ bool RseNode::computable(CompilerScratch* csb, StreamType stream, if (rse_skip && !rse_skip->computable(csb, stream, allowOnlyCurrentStream)) return false; - const NestConst* const end = rse_relations.end(); - NestConst* ptr; - // Set sub-streams of rse active - AutoActivateResetStreams activator(csb, this); + StreamList streams; + computeRseStreams(streams); + StreamStateHolder streamHolder(csb, streams); + streamHolder.activate(true); // Check sub-stream if ((rse_boolean && !rse_boolean->computable(csb, stream, allowOnlyCurrentStream)) || @@ -3393,9 +3253,9 @@ bool RseNode::computable(CompilerScratch* csb, StreamType stream, return false; } - for (ptr = rse_relations.begin(); ptr != end; ++ptr) + for (auto sub : rse_relations) { - if (!(*ptr)->computable(csb, stream, allowOnlyCurrentStream, NULL)) + if (!sub->computable(csb, stream, allowOnlyCurrentStream, NULL)) return false; } @@ -3406,29 +3266,26 @@ bool RseNode::computable(CompilerScratch* csb, StreamType stream, return true; } -void RseNode::findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList) +void RseNode::findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList) { if (rse_first) - rse_first->findDependentFromStreams(optRet, streamList); + rse_first->findDependentFromStreams(csb, currentStream, streamList); if (rse_skip) - rse_skip->findDependentFromStreams(optRet, streamList); + rse_skip->findDependentFromStreams(csb, currentStream, streamList); if (rse_boolean) - rse_boolean->findDependentFromStreams(optRet, streamList); + rse_boolean->findDependentFromStreams(csb, currentStream, streamList); if (rse_sorted) - rse_sorted->findDependentFromStreams(optRet, streamList); + rse_sorted->findDependentFromStreams(csb, currentStream, streamList); if (rse_projection) - rse_projection->findDependentFromStreams(optRet, streamList); + rse_projection->findDependentFromStreams(csb, currentStream, streamList); - NestConst* ptr; - const NestConst* end; - - for (ptr = rse_relations.begin(), end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->findDependentFromStreams(optRet, streamList); + for (auto sub : rse_relations) + sub->findDependentFromStreams(csb, currentStream, streamList); } void RseNode::collectStreams(SortedStreamList& streamList) const @@ -3447,11 +3304,8 @@ void RseNode::collectStreams(SortedStreamList& streamList) const // rse_sorted->collectStreams(streamList); // rse_projection->collectStreams(streamList); - const NestConst* ptr; - const NestConst* end; - - for (ptr = rse_relations.begin(), end = rse_relations.end(); ptr != end; ++ptr) - (*ptr)->collectStreams(streamList); + for (const auto sub : rse_relations) + sub->collectStreams(streamList); } @@ -3714,19 +3568,22 @@ static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Form } // Make new boolean nodes from nodes that contain a field from the given shellStream. -// Those fields are references (mappings) to other nodes and are used by aggregates and union rse's. -static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverStack, MapNode* map, - BoolExprNodeStack* parentStack, StreamType shellStream) +// Those fields are references (mappings) to other nodes and are used by aggregates and unions. +static void genDeliverUnmapped(CompilerScratch* csb, + const BoolExprNodeStack& conjunctStack, + BoolExprNodeStack& deliverStack, + MapNode* map, + StreamType shellStream) { MemoryPool& pool = csb->csb_pool; - for (BoolExprNodeStack::iterator stack1(*parentStack); stack1.hasData(); ++stack1) + for (BoolExprNodeStack::const_iterator iter(conjunctStack); iter.hasData(); ++iter) { - BoolExprNode* const boolean = stack1.object(); + const auto boolean = iter.object(); // Handle the "OR" case first - BinaryBoolNode* const binaryNode = nodeAs(boolean); + const auto binaryNode = nodeAs(boolean); if (binaryNode && binaryNode->blrOp == blr_or) { BoolExprNodeStack orgStack, newStack; @@ -3734,17 +3591,17 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS orgStack.push(binaryNode->arg1); orgStack.push(binaryNode->arg2); - genDeliverUnmapped(csb, &newStack, map, &orgStack, shellStream); + genDeliverUnmapped(csb, orgStack, newStack, map, shellStream); if (newStack.getCount() == 2) { - BoolExprNode* const newArg2 = newStack.pop(); - BoolExprNode* const newArg1 = newStack.pop(); + const auto newArg2 = newStack.pop(); + const auto newArg1 = newStack.pop(); - BinaryBoolNode* const newBinaryNode = + const auto newBinaryNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or, newArg1, newArg2); - deliverStack->push(newBinaryNode); + deliverStack.push(newBinaryNode); } else { @@ -3757,8 +3614,8 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS // Reduce to simple comparisons - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - MissingBoolNode* const missingNode = nodeAs(boolean); + const auto cmpNode = nodeAs(boolean); + const auto missingNode = nodeAs(boolean); HalfStaticArray children; if (cmpNode && @@ -3781,7 +3638,7 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS for (indexArg = 0; (indexArg < children.getCount()) && !mappingFound; ++indexArg) { - FieldNode* fieldNode = nodeAs(children[indexArg]); + const auto fieldNode = nodeAs(children[indexArg]); if (fieldNode && fieldNode->fieldStream == shellStream) mappingFound = true; @@ -3792,12 +3649,12 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS // Create new node and assign the correct existing arguments - BoolExprNode* deliverNode = NULL; + AutoPtr deliverNode; HalfStaticArray newChildren; if (cmpNode) { - ComparativeBoolNode* const newCmpNode = + const auto newCmpNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, cmpNode->blrOp); newChildren.add(newCmpNode->arg1.getAddress()); @@ -3807,7 +3664,7 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS } else if (missingNode) { - MissingBoolNode* const newMissingNode = FB_NEW_POOL(pool) MissingBoolNode(pool); + const auto newMissingNode = FB_NEW_POOL(pool) MissingBoolNode(pool); newChildren.add(newMissingNode->arg.getAddress()); @@ -3826,11 +3683,11 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS // forget to leave aggregate-functions alone in case of aggregate rse). // Because this is only to help using an index we keep it simple. - FieldNode* fieldNode = nodeAs(children[indexArg]); + const auto fieldNode = nodeAs(children[indexArg]); if (fieldNode && fieldNode->fieldStream == shellStream) { - const USHORT fieldId = fieldNode->fieldId; + const auto fieldId = fieldNode->fieldId; if (fieldId >= map->sourceList.getCount()) okNode = false; @@ -3838,7 +3695,7 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS { // Check also the expression inside the map, because aggregate // functions aren't allowed to be delivered to the WHERE clause. - ValueExprNode* value = map->sourceList[fieldId]; + const auto value = map->sourceList[fieldId]; okNode = value->unmappable(map, shellStream); if (okNode) @@ -3852,10 +3709,8 @@ static void genDeliverUnmapped(CompilerScratch* csb, BoolExprNodeStack* deliverS } } - if (!okNode) - delete deliverNode; - else - deliverStack->push(deliverNode); + if (okNode) + deliverStack.push(deliverNode.release()); } } diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index d45297bd37..b77f3d0dfe 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -69,7 +69,8 @@ public: SortNode* pass1(thread_db* tdbb, CompilerScratch* csb); SortNode* pass2(thread_db* tdbb, CompilerScratch* csb); bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream); - void findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList); + void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); NullsPlacement getEffectiveNullOrder(unsigned index) const { @@ -283,7 +284,7 @@ public: }; -class LocalTableSourceNode : public TypedNode +class LocalTableSourceNode final : public TypedNode { public: explicit LocalTableSourceNode(MemoryPool& pool, const MetaName& aDsqlName = NULL) @@ -339,12 +340,12 @@ public: return true; } - void findDependentFromStreams(const OptimizerRetrieval* /*optRet*/, - SortedStreamList* /*streamList*/) override + void findDependentFromStreams(const CompilerScratch* /*csb*/, + StreamType /*currentStream*/, SortedStreamList* /*streamList*/) override { } - RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream) override; + RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream) override; public: Firebird::string alias; @@ -352,7 +353,7 @@ public: SSHORT context = 0; // user-specified context number for the local table reference }; -class RelationSourceNode : public TypedNode +class RelationSourceNode final : public TypedNode { public: explicit RelationSourceNode(MemoryPool& pool, const MetaName& aDsqlName = NULL) @@ -409,12 +410,12 @@ public: return true; } - virtual void findDependentFromStreams(const OptimizerRetrieval* /*optRet*/, - SortedStreamList* /*streamList*/) + virtual void findDependentFromStreams(const CompilerScratch* /*csb*/, + StreamType /*currentStream*/, SortedStreamList* /*streamList*/) { } - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); public: MetaName dsqlName; @@ -428,7 +429,7 @@ public: SSHORT context; // user-specified context number for the relation reference }; -class ProcedureSourceNode : public TypedNode +class ProcedureSourceNode final : public TypedNode { public: explicit ProcedureSourceNode(MemoryPool& pool, @@ -481,15 +482,12 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void collectStreams(SortedStreamList& streamList) const; - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); - -private: - ProcedureScan* generate(thread_db* tdbb, OptimizerBlk* opt); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); public: QualifiedName dsqlName; @@ -524,7 +522,7 @@ private: bool isSubRoutine; }; -class AggregateSourceNode : public TypedNode +class AggregateSourceNode final : public TypedNode { public: explicit AggregateSourceNode(MemoryPool& pool) @@ -565,17 +563,14 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: void genMap(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, dsql_map* map); - RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, BoolExprNodeStack* parentStack, - StreamType shellStream); - public: NestConst dsqlGroup; NestConst dsqlRse; @@ -589,7 +584,7 @@ public: bool dsqlWindow; }; -class UnionSourceNode : public TypedNode +class UnionSourceNode final : public TypedNode { public: explicit UnionSourceNode(MemoryPool& pool) @@ -631,14 +626,10 @@ public: virtual void computeDbKeyStreams(StreamList& streamList) const; virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); - -private: - RecordSource* generate(thread_db* tdbb, OptimizerBlk* opt, const StreamType* streams, - FB_SIZE_T nstreams, BoolExprNodeStack* parentStack, StreamType shellStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); public: RecSourceListNode* dsqlClauses; @@ -654,7 +645,7 @@ public: bool recursive; // union node is a recursive union }; -class WindowSourceNode : public TypedNode +class WindowSourceNode final : public TypedNode { public: struct Window @@ -713,16 +704,16 @@ public: virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: NestConst rse; Firebird::ObjectsArray windows; }; -class RseNode : public TypedNode +class RseNode final : public TypedNode { public: static const USHORT FLAG_VARIANT = 0x01; // variant (not invariant?) @@ -833,12 +824,12 @@ public: virtual void computeRseStreams(StreamList& streamList) const; virtual bool computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* value); - virtual void findDependentFromStreams(const OptimizerRetrieval* optRet, - SortedStreamList* streamList); + virtual void findDependentFromStreams(const CompilerScratch* csb, + StreamType currentStream, SortedStreamList* streamList); virtual void collectStreams(SortedStreamList& streamList) const; - virtual RecordSource* compile(thread_db* tdbb, OptimizerBlk* opt, bool innerSubStream); + virtual RecordSource* compile(thread_db* tdbb, Optimizer* opt, bool innerSubStream); private: void planCheck(const CompilerScratch* csb) const; @@ -871,7 +862,7 @@ public: bool dsqlExplicitJoin; }; -class SelectExprNode : public TypedNode +class SelectExprNode final : public TypedNode { public: explicit SelectExprNode(MemoryPool& pool) @@ -928,7 +919,7 @@ public: fb_assert(false); } - virtual RecordSource* compile(thread_db* /*tdbb*/, OptimizerBlk* /*opt*/, bool /*innerSubStream*/) + virtual RecordSource* compile(thread_db* /*tdbb*/, Optimizer* /*opt*/, bool /*innerSubStream*/) { fb_assert(false); return NULL; diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index 7d753399a7..3e989d1cb4 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -127,19 +127,18 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a if (!idxTran) idxTran = attachment->getSysTransaction(); - IndexDescAlloc* indices = NULL; + IndexDescList indices; // read indices from "base" index root page - const USHORT idx_count = BTR_all(tdbb, this, &indices, &rel_pages_base); + BTR_all(tdbb, this, indices, &rel_pages_base); - const index_desc* const end = indices->items + idx_count; - for (index_desc* idx = indices->items; idx < end; idx++) + for (auto& idx : indices) { MetaName idx_name; - MET_lookup_index(tdbb, idx_name, this->rel_name, idx->idx_id + 1); + MET_lookup_index(tdbb, idx_name, this->rel_name, idx.idx_id + 1); - idx->idx_root = 0; + idx.idx_root = 0; SelectivityList selectivity(*pool); - IDX_create_index(tdbb, this, idx, idx_name.c_str(), NULL, idxTran, selectivity); + IDX_create_index(tdbb, this, &idx, idx_name.c_str(), NULL, idxTran, selectivity); #ifdef VIO_DEBUG VIO_trace(DEBUG_WRITES, @@ -147,15 +146,14 @@ RelationPages* jrd_rel::getPagesInternal(thread_db* tdbb, TraNumber tran, bool a rel_id, newPages->rel_instance_id, newPages->rel_index_root, - idx->idx_id, - idx->idx_root, + idx.idx_id, + idx.idx_root, newPages); #endif } if (poolCreated) dbb->deletePool(pool); - delete indices; return newPages; } diff --git a/src/jrd/ResultSet.cpp b/src/jrd/ResultSet.cpp index 0d22863ce6..59d0f03861 100644 --- a/src/jrd/ResultSet.cpp +++ b/src/jrd/ResultSet.cpp @@ -54,14 +54,14 @@ ResultSet::~ResultSet() stmt->resultSet = NULL; - if (stmt->request->getStatement()->getType() != DsqlCompiledStatement::TYPE_EXEC_PROCEDURE) - DSQL_free_statement(tdbb, stmt->request, DSQL_close); + if (stmt->dsqlRequest->getDsqlStatement()->getType() != DsqlStatement::TYPE_EXEC_PROCEDURE) + DSQL_free_statement(tdbb, stmt->dsqlRequest, DSQL_close); } bool ResultSet::fetch(thread_db* tdbb) { - if (stmt->request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_PROCEDURE && + if (stmt->dsqlRequest->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE && firstFetchDone) { return false; @@ -69,7 +69,7 @@ bool ResultSet::fetch(thread_db* tdbb) memset(stmt->outMessage.begin(), 0, stmt->outMessage.getCount()); - if (!stmt->request->fetch(tdbb, stmt->outMessage.begin())) + if (!stmt->dsqlRequest->fetch(tdbb, stmt->outMessage.begin())) return false; if (stmt->builder) @@ -103,12 +103,12 @@ Firebird::string ResultSet::getString(thread_db* tdbb, unsigned param) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->req_request; + Request* request = stmt->getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. - AutoSetRestore2 autoRequest( - tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); - AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, + AutoSetRestore2 autoRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); return MOV_make_string2(tdbb, &getDesc(param), CS_NONE); @@ -131,12 +131,12 @@ void ResultSet::moveDesc(thread_db* tdbb, unsigned param, dsc& desc) { fb_assert(param > 0); - jrd_req* jrdRequest = stmt->getRequest()->req_request; + Request* request = stmt->getDsqlRequest()->getRequest(); // Setup tdbb info necessary for blobs. - AutoSetRestore2 autoRequest( - tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); - AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, + AutoSetRestore2 autoRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + AutoSetRestore autoRequestTrans(&request->req_transaction, tdbb->getTransaction()); MOV_move(tdbb, &getDesc(param), &desc); diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index 03d19d8e30..c0fb46955d 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -21,7 +21,7 @@ #include "firebird.h" #include "../jrd/Routine.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/Function.h" #include "../jrd/jrd.h" #include "../jrd/exe.h" @@ -111,8 +111,8 @@ Format* Routine::createFormat(MemoryPool& pool, IMessageMetadata* params, bool a return format; } -void Routine::setStatement(JrdStatement* value) -{ +void Routine::setStatement(Statement* value) +{ statement = value; if (statement) @@ -142,7 +142,7 @@ void Routine::checkReload(thread_db* tdbb) if (!reload(tdbb)) { string err; - err.printf("Recompile of %s \"%s\" failed", + err.printf("Recompile of %s \"%s\" failed", getObjectType() == obj_udf ? "FUNCTION" : "PROCEDURE", getName().toString().c_str()); @@ -173,7 +173,7 @@ void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* flags &= ~Routine::FLAG_RELOAD; - JrdStatement* statement = getStatement(); + Statement* statement = getStatement(); PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &statement, false, 0); setStatement(statement); diff --git a/src/jrd/Routine.h b/src/jrd/Routine.h index 1de4ef5463..9002453ef9 100644 --- a/src/jrd/Routine.h +++ b/src/jrd/Routine.h @@ -34,7 +34,7 @@ namespace Jrd { class thread_db; class CompilerScratch; - class JrdStatement; + class Statement; class Lock; class Format; class Parameter; @@ -105,8 +105,8 @@ namespace Jrd const MetaName& getSecurityName() const { return securityName; } void setSecurityName(const MetaName& value) { securityName = value; } - /*const*/ JrdStatement* getStatement() const { return statement; } - void setStatement(JrdStatement* value); + /*const*/ Statement* getStatement() const { return statement; } + void setStatement(Statement* value); bool isSubRoutine() const { return subRoutine; } void setSubRoutine(bool value) { subRoutine = value; } @@ -166,7 +166,7 @@ namespace Jrd USHORT id; // routine ID QualifiedName name; // routine name MetaName securityName; // security class name - JrdStatement* statement; // compiled routine statement + Statement* statement; // compiled routine statement bool subRoutine; // Is this a subroutine? bool implemented; // Is the packaged routine missing the body/entrypoint? bool defined; // UDF has its implementation module available diff --git a/src/jrd/RuntimeStatistics.h b/src/jrd/RuntimeStatistics.h index 68c0c51fb6..8871aee017 100644 --- a/src/jrd/RuntimeStatistics.h +++ b/src/jrd/RuntimeStatistics.h @@ -32,7 +32,7 @@ namespace Firebird { // declared in firebird/Interface.h -struct TraceCounts; +struct TraceCounts; struct PerformanceInfo; } // namespace Firebird @@ -360,7 +360,7 @@ private: // These two numbers are used in adjust() and assign() methods as "generation" // values in order to avoid costly operations when two instances of RuntimeStatistics // contain equal counters values. This is intended to use *only* with the - // same pair of class instances, as in jrd_req. + // same pair of class instances, as in Request. ULONG allChgNumber; // incremented when any counter changes ULONG relChgNumber; // incremented when relation counter changes diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/Statement.cpp similarity index 80% rename from src/jrd/JrdStatement.cpp rename to src/jrd/Statement.cpp index 2af48dcb89..949e430ea4 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/Statement.cpp @@ -19,7 +19,7 @@ */ #include "firebird.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/Attachment.h" #include "../jrd/intl_classes.h" #include "../jrd/acl.h" @@ -41,7 +41,7 @@ using namespace Firebird; using namespace Jrd; -template static void makeSubRoutines(thread_db* tdbb, JrdStatement* statement, +template static void makeSubRoutines(thread_db* tdbb, Statement* statement, CompilerScratch* csb, T& subs); @@ -49,7 +49,7 @@ ULONG CompilerScratch::allocImpure(ULONG align, ULONG size) { const ULONG offset = FB_ALIGN(csb_impure, align); - if (offset + size > JrdStatement::MAX_REQUEST_SIZE) + if (offset + size > Statement::MAX_REQUEST_SIZE) IBERROR(226); // msg 226: request size limit exceeded csb_impure = offset + size; @@ -59,7 +59,7 @@ ULONG CompilerScratch::allocImpure(ULONG align, ULONG size) // Start to turn a parsed scratch into a statement. This is completed by makeStatement. -JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) +Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) : pool(p), rpbsSetup(*p), requests(*p), @@ -74,8 +74,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) localTables(*p), invariants(*p), blr(*p), - mapFieldInfo(*p), - mapItemInfo(*p) + mapFieldInfo(*p) { try { @@ -86,9 +85,16 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) static_cast(csb->csb_node) : NULL; accessList = csb->csb_access; + csb->csb_access.clear(); + externalList = csb->csb_external; + csb->csb_external.clear(); + mapFieldInfo.takeOwnership(csb->csb_map_field_info); + resources = csb->csb_resources; // Assign array contents + csb->csb_resources.clear(); + impureSize = csb->csb_impure; //if (csb->csb_g_flags & csb_blr_version4) @@ -133,7 +139,7 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) #ifdef DEBUG_PROCS string buffer; buffer.printf( - "Called from JrdStatement::makeRequest:\n\t Incrementing use count of %s\n", + "Called from Statement::makeRequest:\n\t Incrementing use count of %s\n", routine->getName()->toString().c_str()); JRD_print_procedure_info(tdbb, buffer.c_str()); #endif @@ -155,19 +161,22 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) // make a vector of all used RSEs fors = csb->csb_fors; + csb->csb_fors.clear(); localTables = csb->csb_localTables; + csb->csb_localTables.clear(); // make a vector of all invariant-type nodes, so that we will // be able to easily reinitialize them when we restart the request invariants.join(csb->csb_invariants); + csb->csb_invariants.clear(); rpbsSetup.grow(csb->csb_n_stream); - CompilerScratch::csb_repeat* tail = csb->csb_rpt.begin(); - const CompilerScratch::csb_repeat* const streams_end = tail + csb->csb_n_stream; + auto tail = csb->csb_rpt.begin(); + const auto* const streams_end = tail + csb->csb_n_stream; - for (record_param* rpb = rpbsSetup.begin(); tail < streams_end; ++rpb, ++tail) + for (auto rpb = rpbsSetup.begin(); tail < streams_end; ++rpb, ++tail) { // fetch input stream for update if all booleans matched against indices if ((tail->csb_flags & csb_update) && !(tail->csb_flags & csb_unmatched)) @@ -186,10 +195,26 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) delete tail->csb_fields; tail->csb_fields = NULL; } + + if (csb->csb_variables) + csb->csb_variables->clear(); + + csb->csb_current_nodes.free(); + csb->csb_current_for_nodes.free(); + csb->csb_computing_fields.free(); + csb->csb_variables_used_in_subroutines.free(); + csb->csb_dbg_info.reset(); + csb->csb_map_item_info.clear(); + csb->csb_message_pad.clear(); + csb->subFunctions.clear(); + csb->subProcedures.clear(); + csb->outerMessagesMap.clear(); + csb->outerVarsMap.clear(); + csb->csb_rpt.free(); } catch (Exception&) { - for (JrdStatement** subStatement = subStatements.begin(); + for (Statement** subStatement = subStatements.begin(); subStatement != subStatements.end(); ++subStatement) { @@ -201,18 +226,21 @@ JrdStatement::JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb) } // Turn a parsed scratch into a statement. -JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, + std::function beforeCsbRelease) { DEV_BLKCHK(csb, type_csb); SET_TDBB(tdbb); - Database* const dbb = tdbb->getDatabase(); + const auto dbb = tdbb->getDatabase(); fb_assert(dbb); - jrd_req* const old_request = tdbb->getRequest(); - tdbb->setRequest(NULL); + const auto attachment = tdbb->getAttachment(); - JrdStatement* statement = NULL; + const auto old_request = tdbb->getRequest(); + tdbb->setRequest(nullptr); + + Statement* statement = nullptr; try { @@ -275,11 +303,12 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, if (csb->csb_impure > MAX_REQUEST_SIZE) IBERROR(226); // msg 226 request size limit exceeded + if (beforeCsbRelease) + beforeCsbRelease(); + // Build the statement and the final request block. - - MemoryPool* const pool = tdbb->getDefaultPool(); - - statement = FB_NEW_POOL(*pool) JrdStatement(tdbb, pool, csb); + const auto pool = tdbb->getDefaultPool(); + statement = FB_NEW_POOL(*pool) Statement(tdbb, pool, csb); tdbb->setRequest(old_request); } // try @@ -288,12 +317,8 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, if (statement) { // Release sub statements. - for (JrdStatement** subStatement = statement->subStatements.begin(); - subStatement != statement->subStatements.end(); - ++subStatement) - { - (*subStatement)->release(tdbb); - } + for (auto subStatement : statement->subStatements) + subStatement->release(tdbb); } ex.stuffException(tdbb->tdbb_status_vector); @@ -302,20 +327,40 @@ JrdStatement* JrdStatement::makeStatement(thread_db* tdbb, CompilerScratch* csb, } if (internalFlag) + { statement->flags |= FLAG_INTERNAL; + statement->charSetId = CS_METADATA; + } + else + statement->charSetId = attachment->att_charset; + + attachment->att_statements.add(statement); return statement; } -// Turn a parsed scratch into an executable request. -jrd_req* JrdStatement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +Statement* Statement::makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc, + CompilerScratch* csb, bool internalFlag) { - JrdStatement* statement = makeStatement(tdbb, csb, internalFlag); + fb_assert(csb->csb_node->getKind() == DmlNode::KIND_VALUE); + + return makeStatement(tdbb, csb, internalFlag, + [&] + { + node = static_cast(csb->csb_node); + node->getDesc(tdbb, csb, &desc); + }); +} + +// Turn a parsed scratch into an executable request. +Request* Statement::makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag) +{ + Statement* statement = makeStatement(tdbb, csb, internalFlag); return statement->getRequest(tdbb, 0); } // Returns function or procedure routine. -const Routine* JrdStatement::getRoutine() const +const Routine* Statement::getRoutine() const { fb_assert(!(procedure && function)); @@ -326,9 +371,9 @@ const Routine* JrdStatement::getRoutine() const } // Determine if any request of this statement are active. -bool JrdStatement::isActive() const +bool Statement::isActive() const { - for (const jrd_req* const* request = requests.begin(); request != requests.end(); ++request) + for (const Request* const* request = requests.begin(); request != requests.end(); ++request) { if (*request && ((*request)->req_flags & req_in_use)) return true; @@ -337,26 +382,26 @@ bool JrdStatement::isActive() const return false; } -jrd_req* JrdStatement::findRequest(thread_db* tdbb, bool unique) +Request* Statement::findRequest(thread_db* tdbb, bool unique) { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); - const JrdStatement* const thisPointer = this; // avoid warning + const Statement* const thisPointer = this; // avoid warning if (!thisPointer) BUGCHECK(167); /* msg 167 invalid SEND request */ // Search clones for one request in use by this attachment. // If not found, return first inactive request. - jrd_req* clone = NULL; + Request* clone = NULL; USHORT count = 0; const USHORT clones = requests.getCount(); USHORT n; for (n = 0; n < clones; ++n) { - jrd_req* next = getRequest(tdbb, n); + Request* next = getRequest(tdbb, n); if (next->req_attachment == attachment) { @@ -389,7 +434,7 @@ jrd_req* JrdStatement::findRequest(thread_db* tdbb, bool unique) return clone; } -jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) +Request* Statement::getRequest(thread_db* tdbb, USHORT level) { SET_TDBB(tdbb); @@ -400,15 +445,11 @@ jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) if (level < requests.getCount() && requests[level]) return requests[level]; - requests.grow(level + 1); - - MemoryStats* const parentStats = (flags & FLAG_INTERNAL) ? - &dbb->dbb_memory_stats : &attachment->att_memory_stats; - // Create the request. - jrd_req* const request = FB_NEW_POOL(*pool) jrd_req(attachment, this, parentStats); - request->setRequestId(dbb->generateStatementId()); + AutoMemoryPool reqPool(MemoryPool::createPool(pool)); + const auto request = FB_NEW_POOL(*reqPool) Request(reqPool, attachment, this); + requests.grow(level + 1); requests[level] = request; return request; @@ -416,8 +457,11 @@ jrd_req* JrdStatement::getRequest(thread_db* tdbb, USHORT level) // Check that we have enough rights to access all resources this request touches including // resources it used indirectly via procedures or triggers. -void JrdStatement::verifyAccess(thread_db* tdbb) +void Statement::verifyAccess(thread_db* tdbb) { + if (flags & FLAG_INTERNAL) + return; + SET_TDBB(tdbb); ExternalAccessList external; @@ -493,15 +537,13 @@ void JrdStatement::verifyAccess(thread_db* tdbb) if (!routine->getStatement()) continue; - for (const AccessItem* access = routine->getStatement()->accessList.begin(); - access != routine->getStatement()->accessList.end(); - ++access) + for (const auto& access : routine->getStatement()->accessList) { MetaName userName = item->user; - if (access->acc_ss_rel_id) + if (access.acc_ss_rel_id) { - const jrd_rel* view = MET_lookup_relation_id(tdbb, access->acc_ss_rel_id, false); + const jrd_rel* view = MET_lookup_relation_id(tdbb, access.acc_ss_rel_id, false); if (view && (view->rel_flags & REL_sql_relation)) userName = view->rel_owner_name; } @@ -510,17 +552,17 @@ void JrdStatement::verifyAccess(thread_db* tdbb) UserId* effectiveUser = userName.hasData() ? attachment->getUserId(userName) : attachment->att_ss_user; AutoSetRestore userIdHolder(&attachment->att_ss_user, effectiveUser); - const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); + const SecurityClass* sec_class = SCL_get_class(tdbb, access.acc_security_name.c_str()); if (routine->getName().package.isEmpty()) { SCL_check_access(tdbb, sec_class, aclType, routine->getName().identifier, - access->acc_mask, access->acc_type, true, access->acc_name, access->acc_r_name); + access.acc_mask, access.acc_type, true, access.acc_name, access.acc_r_name); } else { SCL_check_access(tdbb, sec_class, id_package, routine->getName().package, - access->acc_mask, access->acc_type, true, access->acc_name, access->acc_r_name); + access.acc_mask, access.acc_type, true, access.acc_name, access.acc_r_name); } } } @@ -586,12 +628,12 @@ void JrdStatement::verifyAccess(thread_db* tdbb) } // Release a statement. -void JrdStatement::release(thread_db* tdbb) +void Statement::release(thread_db* tdbb) { SET_TDBB(tdbb); // Release sub statements. - for (JrdStatement** subStatement = subStatements.begin(); + for (Statement** subStatement = subStatements.begin(); subStatement != subStatements.end(); ++subStatement) { @@ -642,21 +684,44 @@ void JrdStatement::release(thread_db* tdbb) } } - for (jrd_req** instance = requests.begin(); instance != requests.end(); ++instance) + for (Request** instance = requests.begin(); instance != requests.end(); ++instance) + { EXE_release(tdbb, *instance); + MemoryPool::deletePool((*instance)->req_pool); + *instance = nullptr; + } + + const auto attachment = tdbb->getAttachment(); + + FB_SIZE_T pos; + if (attachment->att_statements.find(this, pos)) + attachment->att_statements.remove(pos); + else + fb_assert(false); sqlText = NULL; // Sub statement pool is the same of the main statement, so don't delete it. if (!parentStatement) + attachment->deletePool(pool); +} + +// Returns a formatted textual plan for all RseNode's in the specified request +string Statement::getPlan(thread_db* tdbb, bool detailed) const +{ + string plan; + + for (const auto rsb : fors) { - Jrd::Attachment* const att = tdbb->getAttachment(); - att->deletePool(pool); + plan += detailed ? "\nSelect Expression" : "\nPLAN "; + rsb->print(tdbb, plan, detailed, 0); } + + return plan; } // Check that we have enough rights to access all resources this list of triggers touches. -void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, +void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers, MetaName userName) { if (!triggers) @@ -685,12 +750,12 @@ void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, if (!(ownerRelation->rel_flags & REL_system)) { - if (access->acc_type == SCL_object_table && + if (access->acc_type == obj_relations && (ownerRelation->rel_name == access->acc_name)) { continue; } - if (access->acc_type == SCL_object_column && + if (access->acc_type == obj_column && (ownerRelation->rel_name == access->acc_r_name)) { continue; @@ -720,7 +785,7 @@ void JrdStatement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, } // Invoke buildExternalAccess for triggers in vector -inline void JrdStatement::triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, +inline void Statement::triggersExternalAccess(thread_db* tdbb, ExternalAccessList& list, TrigVector* tvec, const MetaName& user) { if (!tvec) @@ -741,7 +806,7 @@ inline void JrdStatement::triggersExternalAccess(thread_db* tdbb, ExternalAccess // Recursively walk external dependencies (procedures, triggers) for request to assemble full // list of requests it depends on. -void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const MetaName &user) +void Statement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list, const MetaName &user) { for (ExternalAccess* item = externalList.begin(); item != externalList.end(); ++item) { @@ -811,46 +876,38 @@ void JrdStatement::buildExternalAccess(thread_db* tdbb, ExternalAccessList& list // Make sub routines. -template static void makeSubRoutines(thread_db* tdbb, JrdStatement* statement, +template static void makeSubRoutines(thread_db* tdbb, Statement* statement, CompilerScratch* csb, T& subs) { typename T::Accessor subAccessor(&subs); - for (bool found = subAccessor.getFirst(); found; found = subAccessor.getNext()) + for (auto& sub : subs) { - typename T::ValueType subNode = subAccessor.current()->second; - Routine* subRoutine = subNode->routine; - CompilerScratch*& subCsb = subNode->subCsb; + auto subNode = sub.second; + auto subRoutine = subNode->routine; + auto& subCsb = subNode->subCsb; - JrdStatement* subStatement = JrdStatement::makeStatement(tdbb, subCsb, false); + auto subStatement = Statement::makeStatement(tdbb, subCsb, false); subStatement->parentStatement = statement; subRoutine->setStatement(subStatement); // Move dependencies and permissions from the sub routine to the parent. - for (CompilerScratch::Dependency* dependency = subCsb->csb_dependencies.begin(); - dependency != subCsb->csb_dependencies.end(); - ++dependency) - { - csb->csb_dependencies.push(*dependency); - } + for (auto& dependency : subCsb->csb_dependencies) + csb->csb_dependencies.push(dependency); - for (ExternalAccess* access = subCsb->csb_external.begin(); - access != subCsb->csb_external.end(); - ++access) + for (auto& access : subStatement->externalList) { FB_SIZE_T i; - if (!csb->csb_external.find(*access, i)) - csb->csb_external.insert(i, *access); + if (!csb->csb_external.find(access, i)) + csb->csb_external.insert(i, access); } - for (AccessItem* access = subCsb->csb_access.begin(); - access != subCsb->csb_access.end(); - ++access) + for (auto& access : subStatement->accessList) { FB_SIZE_T i; - if (!csb->csb_access.find(*access, i)) - csb->csb_access.insert(i, *access); + if (!csb->csb_access.find(access, i)) + csb->csb_access.insert(i, access); } delete subCsb; diff --git a/src/jrd/JrdStatement.h b/src/jrd/Statement.h similarity index 71% rename from src/jrd/JrdStatement.h rename to src/jrd/Statement.h index ed67f30dfa..2c8cb07661 100644 --- a/src/jrd/JrdStatement.h +++ b/src/jrd/Statement.h @@ -24,11 +24,12 @@ #include "../include/fb_blk.h" #include "../jrd/exe.h" #include "../jrd/EngineInterface.h" +#include namespace Jrd { // Compiled statement. -class JrdStatement : public pool_alloc +class Statement : public pool_alloc { public: static const unsigned FLAG_SYS_TRIGGER = 0x01; @@ -42,20 +43,39 @@ public: static const unsigned MAX_REQUEST_SIZE = 50 * 1048576; // 50 MB - just to be safe private: - JrdStatement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); + Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb); public: - static JrdStatement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); - static jrd_req* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, + std::function beforeCsbRelease = nullptr); + + static Statement* makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc, + CompilerScratch* csb, bool internalFlag); + + static Request* makeRequest(thread_db* tdbb, CompilerScratch* csb, bool internalFlag); + + StmtNumber getStatementId() const + { + if (!id) + id = JRD_get_thread_data()->getDatabase()->generateStatementId(); + return id; + } + + unsigned getSize() const + { + return (unsigned) pool->getStatsGroup().getCurrentUsage(); + } const Routine* getRoutine() const; bool isActive() const; - jrd_req* findRequest(thread_db* tdbb, bool unique = false); - jrd_req* getRequest(thread_db* tdbb, USHORT level); + Request* findRequest(thread_db* tdbb, bool unique = false); + Request* getRequest(thread_db* tdbb, USHORT level); void verifyAccess(thread_db* tdbb); void release(thread_db* tdbb); + Firebird::string getPlan(thread_db* tdbb, bool detailed) const; + private: static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers, MetaName userName); @@ -68,8 +88,10 @@ public: unsigned flags; // statement flags unsigned blrVersion; ULONG impureSize; // Size of impure area + mutable StmtNumber id; // statement identifier + USHORT charSetId; // client character set (CS_METADATA for internal statements) Firebird::Array rpbsSetup; - Firebird::Array requests; // vector of requests + Firebird::Array requests; // vector of requests ExternalAccessList externalList; // Access to procedures/triggers to be checked AccessItemList accessList; // Access items to be checked ResourceList resources; // Resources (relations and indices) @@ -77,8 +99,8 @@ public: const Function* function; // function, if any MetaName triggerName; // name of request (trigger), if any Jrd::UserId* triggerInvoker; // user name if trigger run with SQL SECURITY DEFINER - JrdStatement* parentStatement; // Sub routine's parent statement - Firebird::Array subStatements; // Array of subroutines' statements + Statement* parentStatement; // Sub routine's parent statement + Firebird::Array subStatements; // Array of subroutines' statements const StmtNode* topNode; // top of execution tree Firebird::Array fors; // record sources Firebird::Array localTables; // local tables @@ -86,7 +108,6 @@ public: Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset) Firebird::Array blr; // BLR for non-SQL query MapFieldInfo mapFieldInfo; // Map field name to field info - MapItemInfo mapItemInfo; // Map item to item info }; diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index b3555ecccf..068fe65bb2 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -1862,7 +1862,7 @@ dsc* evlStdMath(thread_db* tdbb, const SysFunction* function, const NestValueArr fb_assert(args.getCount() == 1); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -1962,7 +1962,7 @@ dsc* evlAbs(thread_db* tdbb, const SysFunction*, const NestValueArray& args, imp { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2020,7 +2020,7 @@ dsc* evlAsciiChar(thread_db* tdbb, const SysFunction*, const NestValueArray& arg { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2042,7 +2042,7 @@ dsc* evlAsciiVal(thread_db* tdbb, const SysFunction*, const NestValueArray& args { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2077,7 +2077,7 @@ dsc* evlAtan2(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* desc1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if desc1 is NULL @@ -2146,7 +2146,7 @@ dsc* evlBin(thread_db* tdbb, const SysFunction* function, const NestValueArray& fb_assert(function->misc != NULL); Function func = (Function)(IPTR) function->misc; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); bool f128 = false; for (unsigned i = 0; i < args.getCount(); ++i) @@ -2212,7 +2212,7 @@ dsc* evlBinShift(thread_db* tdbb, const SysFunction* function, const NestValueAr fb_assert(args.getCount() == 2); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -2263,7 +2263,7 @@ dsc* evlCeil(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2348,7 +2348,7 @@ dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValue { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -2466,7 +2466,7 @@ dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() == 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* quantityDsc = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if quantityDsc is NULL @@ -2994,7 +2994,7 @@ dsc* evlEncryptDecrypt(thread_db* tdbb, const SysFunction* function, const NestV fb_assert(args.getCount() == CRYPT_ARG_MAX); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[CRYPT_ARG_MAX]; @@ -3512,7 +3512,7 @@ dsc* evlRsaEncryptDecrypt(thread_db* tdbb, const SysFunction* function, const Ne fb_assert(args.getCount() == RSA_CRYPT_ARG_MAX || args.getCount() == RSA_CRYPT_ARG_MAX - 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[RSA_CRYPT_ARG_MAX]; @@ -3582,7 +3582,7 @@ dsc* evlRsaPrivate(thread_db* tdbb, const SysFunction* function, const NestValue fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -3613,7 +3613,7 @@ dsc* evlRsaPublic(thread_db* tdbb, const SysFunction* function, const NestValueA fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -3652,7 +3652,7 @@ dsc* evlRsaSign(thread_db* tdbb, const SysFunction* function, const NestValueArr fb_assert(args.getCount() == RSA_SIGN_ARG_MAX || args.getCount() == RSA_SIGN_ARG_MAX - 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[RSA_SIGN_ARG_MAX]; @@ -3721,7 +3721,7 @@ dsc* evlRsaVerify(thread_db* tdbb, const SysFunction* function, const NestValueA fb_assert(args.getCount() == RSA_VERIFY_ARG_MAX || args.getCount() == RSA_VERIFY_ARG_MAX - 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // parse args and check correctness const dsc* dscs[RSA_VERIFY_ARG_MAX]; @@ -3780,7 +3780,7 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr { fb_assert(args.getCount() == 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* partDsc = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if partDsc is NULL @@ -4010,7 +4010,7 @@ dsc* evlExp(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4047,7 +4047,7 @@ dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestVal { fb_assert(args.getCount() >= 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* partDsc = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if partDsc is NULL @@ -4174,7 +4174,7 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4292,7 +4292,7 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar Jrd::Attachment* attachment = tdbb->getAttachment(); Database* dbb = tdbb->getDatabase(); jrd_tra* transaction = tdbb->getTransaction(); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); request->req_flags &= ~req_null; const dsc* nameSpace = EVL_expr(tdbb, request, args[0]); @@ -4442,7 +4442,7 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar else if ((transaction->tra_flags & TRA_read_committed) && (transaction->tra_flags & TRA_read_consistency)) { - jrd_req* snapshot_req = request->req_snapshot.m_owner; + Request* snapshot_req = request->req_snapshot.m_owner; if (snapshot_req) resultStr.printf("%" SQUADFORMAT, snapshot_req->req_snapshot.m_number); else @@ -4579,7 +4579,7 @@ dsc* evlSetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar Jrd::Attachment* attachment = tdbb->getAttachment(); jrd_tra* transaction = tdbb->getTransaction(); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); request->req_flags &= ~req_null; const dsc* nameSpace = EVL_expr(tdbb, request, args[0]); @@ -4679,7 +4679,7 @@ dsc* evlGetTranCN(thread_db* tdbb, const SysFunction* function, const NestValueA fb_assert(args.getCount() == 1); Database* dbb = tdbb->getDatabase(); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); request->req_flags &= ~req_null; const dsc* value = EVL_expr(tdbb, request, args[0]); @@ -4720,7 +4720,7 @@ dsc* evlHash(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() >= 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4779,7 +4779,7 @@ dsc* evlLeft(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* str = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if str is NULL @@ -4803,7 +4803,7 @@ dsc* evlLnLog10(thread_db* tdbb, const SysFunction* function, const NestValueArr fb_assert(args.getCount() == 1); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -4876,7 +4876,7 @@ dsc* evlLog(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -4942,7 +4942,7 @@ dsc* evlQuantize(thread_db* tdbb, const SysFunction* function, const NestValueAr { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -4981,7 +4981,7 @@ dsc* evlCompare(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -5037,7 +5037,7 @@ dsc* evlNormDec(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value; value = EVL_expr(tdbb, request, args[0]); @@ -5070,7 +5070,7 @@ dsc* evlMakeDbkey(Jrd::thread_db* tdbb, const SysFunction* function, const NestV // MAKE_DBKEY ( REL_NAME | REL_ID, RECNUM [, DPNUM [, PPNUM] ] ) Database* const dbb = tdbb->getDatabase(); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); fb_assert(args.getCount() >= 2 && args.getCount() <= 4); @@ -5171,7 +5171,7 @@ dsc* evlMaxMinValue(thread_db* tdbb, const SysFunction* function, const NestValu fb_assert(args.getCount() >= 1); fb_assert(function->misc != NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* result = NULL; for (FB_SIZE_T i = 0; i < args.getCount(); ++i) @@ -5211,7 +5211,7 @@ dsc* evlMod(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -5274,7 +5274,7 @@ dsc* evlOverlay(thread_db* tdbb, const SysFunction* function, const NestValueArr { fb_assert(args.getCount() >= 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -5458,7 +5458,7 @@ dsc* evlPad(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() >= 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -5633,7 +5633,7 @@ dsc* evlPosition(thread_db* tdbb, const SysFunction* function, const NestValueAr { fb_assert(args.getCount() >= 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value1 = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value1 is NULL @@ -5759,7 +5759,7 @@ dsc* evlPower(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value[2]; value[0] = EVL_expr(tdbb, request, args[0]); @@ -5835,7 +5835,7 @@ dsc* evlReplace(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 3); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* values[3]; // 0 = searched, 1 = find, 2 = replacement const dsc* firstBlob = NULL; @@ -5993,7 +5993,7 @@ dsc* evlReverse(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6096,7 +6096,7 @@ dsc* evlRight(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 2); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6149,7 +6149,7 @@ dsc* evlRound(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() >= 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6197,7 +6197,7 @@ dsc* evlSign(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6228,7 +6228,7 @@ dsc* evlSqrt(thread_db* tdbb, const SysFunction* function, const NestValueArray& { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6271,7 +6271,7 @@ dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray { fb_assert(args.getCount() >= 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6404,7 +6404,7 @@ dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValue { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6448,7 +6448,7 @@ dsc* evlRoleInUse(thread_db* tdbb, const SysFunction*, const NestValueArray& arg { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); Jrd::Attachment* attachment = tdbb->getAttachment(); const dsc* value = EVL_expr(tdbb, request, args[0]); @@ -6475,7 +6475,7 @@ dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction*, const NestValueArra { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL return NULL; @@ -6497,7 +6497,7 @@ dsc* evlUnicodeChar(thread_db* tdbb, const SysFunction* function, const NestValu { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL @@ -6532,7 +6532,7 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar { fb_assert(args.getCount() == 1); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const dsc* value = EVL_expr(tdbb, request, args[0]); if (request->req_flags & req_null) // return NULL if value is NULL diff --git a/src/jrd/TimeZone.h b/src/jrd/TimeZone.h index b734733b7b..4c6c8eafaf 100644 --- a/src/jrd/TimeZone.h +++ b/src/jrd/TimeZone.h @@ -43,7 +43,7 @@ public: TimeZoneSnapshot(thread_db* tdbb, MemoryPool& pool); }; -class TimeZonesTableScan : public VirtualTableScan +class TimeZonesTableScan final : public VirtualTableScan { public: TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, jrd_rel* relation); diff --git a/src/jrd/VirtualTable.cpp b/src/jrd/VirtualTable.cpp index 7eaa5bb2c2..1c3485425a 100644 --- a/src/jrd/VirtualTable.cpp +++ b/src/jrd/VirtualTable.cpp @@ -29,7 +29,6 @@ #include "../jrd/ids.h" #include "../jrd/ini.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/val.h" #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" diff --git a/src/jrd/blb.cpp b/src/jrd/blb.cpp index 10cbbd763b..66c73150f4 100644 --- a/src/jrd/blb.cpp +++ b/src/jrd/blb.cpp @@ -345,13 +345,13 @@ blb* blb::create2(thread_db* tdbb, // Bind non-user blob to the request - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (!userBlob && request) { transaction->tra_blobs->locate(blob->blb_temp_id); BlobIndex* current = &transaction->tra_blobs->current(); - jrd_req* blob_request = request; + Request* blob_request = request; while (blob_request->req_caller) blob_request = blob_request->req_caller; @@ -1052,7 +1052,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, return; } - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); if (relation->isVirtual()) { ERR_post(Arg::Gds(isc_read_only)); @@ -1138,7 +1138,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, { // Walk through call stack looking if our BLOB is // owned by somebody from our call chain - jrd_req* temp_req = request; + Request* temp_req = request; do { if (blobIndex->bli_request == temp_req) break; @@ -1224,7 +1224,7 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, blobIndex->bli_materialized = true; blobIndex->bli_blob_id = *destination; // Assign temporary BLOB ownership to top-level request if it is not assigned yet - jrd_req* own_request; + Request* own_request; if (blobIndex->bli_request) { own_request = blobIndex->bli_request; } @@ -2081,7 +2081,7 @@ blb* blb::copy_blob(thread_db* tdbb, const bid* source, bid* destination, **************************************/ SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); blb* input = open2(tdbb, transaction, source, bpb_length, bpb); blb* output = create(tdbb, transaction, destination); @@ -2532,7 +2532,7 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc status_exception::raise(Arg::Gds(isc_malformed_string)); } - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); transaction = transaction->getOuter(); @@ -2580,7 +2580,7 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc if (current->bli_materialized) { // Delete BLOB from request owned blob list - jrd_req* blob_request = current->bli_request; + Request* blob_request = current->bli_request; if (blob_request) { if (blob_request->req_blobs.locate(blob_temp_id)) { @@ -2603,7 +2603,7 @@ static void move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc // we may still bind lifetime of blob to current top level request. if (!current->bli_request) { - jrd_req* blob_request = request; + Request* blob_request = request; while (blob_request->req_caller) blob_request = blob_request->req_caller; @@ -2641,7 +2641,7 @@ static void move_to_string(thread_db* tdbb, dsc* fromDesc, dsc* toDesc) else blobAsText.dsc_ttype() = ttype_ascii; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction(); transaction = transaction->getOuter(); @@ -2678,7 +2678,7 @@ void blb::destroy(const bool purge_flag) { if (blb_transaction->tra_blobs->locate(blb_temp_id)) { - jrd_req* blob_request = blb_transaction->tra_blobs->current().bli_request; + Request* blob_request = blb_transaction->tra_blobs->current().bli_request; if (blob_request) { diff --git a/src/jrd/blb.h b/src/jrd/blb.h index 99d9c055bb..29d9dde90a 100644 --- a/src/jrd/blb.h +++ b/src/jrd/blb.h @@ -51,7 +51,7 @@ namespace Jrd class Attachment; class BlobControl; class jrd_rel; -class jrd_req; +class Request; class jrd_tra; class vcl; class thread_db; diff --git a/src/jrd/blp.h b/src/jrd/blp.h index b6ed5eacee..b4ee1dd7a2 100644 --- a/src/jrd/blp.h +++ b/src/jrd/blp.h @@ -252,5 +252,6 @@ static const struct {"dcl_local_table", dcl_local_table}, {"local_table_truncate", one_word}, {"local_table_id", local_table}, + {"outer_map", outer_map}, {0, 0} }; diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 931432af2c..15aecc733b 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -252,7 +252,9 @@ void BtrPageGCLock::disablePageGC(thread_db* tdbb, const PageNumber& page) void BtrPageGCLock::enablePageGC(thread_db* tdbb) { - LCK_release(tdbb, this); + fb_assert(lck_id); + if (lck_id) + LCK_release(tdbb, this); } bool BtrPageGCLock::isPageGCAllowed(thread_db* tdbb, const PageNumber& page) @@ -340,7 +342,7 @@ void IndexErrorContext::raise(thread_db* tdbb, idx_e result, Record* record) } -USHORT BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescAlloc** csb_idx, RelationPages* relPages) +void BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescList& idxList, RelationPages* relPages) { /************************************** * @@ -362,21 +364,16 @@ USHORT BTR_all(thread_db* tdbb, jrd_rel* relation, IndexDescAlloc** csb_idx, Rel index_root_page* const root = fetch_root(tdbb, &window, relation, relPages); if (!root) - return 0; + return; - delete *csb_idx; - *csb_idx = FB_NEW_RPT(*tdbb->getDefaultPool(), root->irt_count) IndexDescAlloc(); - - index_desc* buffer = (*csb_idx)->items; - USHORT count = 0; for (USHORT i = 0; i < root->irt_count; i++) { - if (BTR_description(tdbb, relation, root, &buffer[count], i)) - count++; + index_desc idx; + if (BTR_description(tdbb, relation, root, &idx, i)) + idxList.add(idx); } CCH_RELEASE(tdbb, &window); - return count; } @@ -392,9 +389,12 @@ void BTR_complement_key(temporary_key* key) * Negate a key for descending index. * **************************************/ - UCHAR* p = key->key_data; - for (const UCHAR* const end = p + key->key_length; p < end; p++) - *p ^= -1; + do + { + UCHAR* p = key->key_data; + for (const UCHAR* const end = p + key->key_length; p < end; p++) + *p ^= -1; + } while (key = key->key_next.get()); } @@ -546,8 +546,8 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& fb_assert(idx->idx_expression != NULL); // check for resursive expression evaluation - jrd_req* const org_request = tdbb->getRequest(); - jrd_req* const expr_request = idx->idx_expression_statement->findRequest(tdbb, true); + Request* const org_request = tdbb->getRequest(); + Request* const expr_request = idx->idx_expression_statement->findRequest(tdbb, true); if (expr_request == NULL) ERR_post(Arg::Gds(isc_random) << "Attempt to evaluate index expression recursively"); @@ -577,9 +577,9 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& Jrd::ContextPoolHolder context(tdbb, expr_request->req_pool); if (org_request) - expr_request->req_gmt_timestamp = org_request->req_gmt_timestamp; + expr_request->setGmtTimeStamp(org_request->getGmtTimeStamp()); else - TimeZoneUtil::validateGmtTimeStamp(expr_request->req_gmt_timestamp); + expr_request->validateTimeStamp(); if (!(result = EVL_expr(tdbb, expr_request, idx->idx_expression))) result = &idx->idx_expression_desc; @@ -594,7 +594,7 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& expr_request->req_caller = NULL; expr_request->req_flags &= ~req_in_use; expr_request->req_attachment = NULL; - expr_request->req_gmt_timestamp.invalidate(); + expr_request->invalidateTimeStamp(); throw; } @@ -605,7 +605,7 @@ DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& expr_request->req_caller = NULL; expr_request->req_flags &= ~req_in_use; expr_request->req_attachment = NULL; - expr_request->req_gmt_timestamp.invalidate(); + expr_request->invalidateTimeStamp(); return result; } @@ -699,149 +699,158 @@ void BTR_evaluate(thread_db* tdbb, const IndexRetrieval* retrieval, RecordBitmap index_desc idx; RelationPages* relPages = retrieval->irb_relation->getPages(tdbb); WIN window(relPages->rel_pg_space_id, -1); - temporary_key lower, upper; - lower.key_flags = 0; - lower.key_length = 0; - upper.key_flags = 0; - upper.key_length = 0; - btree_page* page = BTR_find_page(tdbb, retrieval, &window, &idx, &lower, &upper); + temporary_key lowerKey, upperKey; + lowerKey.key_flags = 0; + lowerKey.key_length = 0; + upperKey.key_flags = 0; + upperKey.key_length = 0; - const bool descending = (idx.idx_flags & idx_descending); - bool skipLowerKey = (retrieval->irb_generic & irb_exclude_lower); - const bool partLower = (retrieval->irb_lower_count < idx.idx_count); + temporary_key* lower = &lowerKey; + temporary_key* upper = &upperKey; + bool first = true; - // If there is a starting descriptor, search down index to starting position. - // This may involve sibling buckets if splits are in progress. If there - // isn't a starting descriptor, walk down the left side of the index. - USHORT prefix; - UCHAR* pointer; - if (retrieval->irb_lower_count) + do { - while (!(pointer = find_node_start_point(page, &lower, 0, &prefix, - idx.idx_flags & idx_descending, (retrieval->irb_generic & (irb_starting | irb_partial))))) - { - page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); - } + btree_page* page = BTR_find_page(tdbb, retrieval, &window, &idx, lower, upper, first); + first = false; - // Compute the number of matching characters in lower and upper bounds - if (retrieval->irb_upper_count) - { - prefix = IndexNode::computePrefix(upper.key_data, upper.key_length, - lower.key_data, lower.key_length); - } + const bool descending = (idx.idx_flags & idx_descending); + bool skipLowerKey = (retrieval->irb_generic & irb_exclude_lower); + const bool partLower = (retrieval->irb_lower_count < idx.idx_count); - if (skipLowerKey) + // If there is a starting descriptor, search down index to starting position. + // This may involve sibling buckets if splits are in progress. If there + // isn't a starting descriptor, walk down the left side of the index. + USHORT prefix; + UCHAR* pointer; + if (retrieval->irb_lower_count) { - IndexNode node; - node.readNode(pointer, true); - - if ((lower.key_length == node.prefix + node.length) || - ((lower.key_length <= node.prefix + node.length) && partLower)) + while (!(pointer = find_node_start_point(page, lower, 0, &prefix, + idx.idx_flags & idx_descending, (retrieval->irb_generic & (irb_starting | irb_partial))))) { - const UCHAR* p = node.data, *q = lower.key_data + node.prefix; - const UCHAR* const end = lower.key_data + lower.key_length; - while (q < end) + page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + } + + // Compute the number of matching characters in lower and upper bounds + if (retrieval->irb_upper_count) + { + prefix = IndexNode::computePrefix(upper->key_data, upper->key_length, + lower->key_data, lower->key_length); + } + + if (skipLowerKey) + { + IndexNode node; + node.readNode(pointer, true); + + if ((lower->key_length == node.prefix + node.length) || + ((lower->key_length <= node.prefix + node.length) && partLower)) { - if (*p++ != *q++) + const UCHAR* p = node.data, *q = lower->key_data + node.prefix; + const UCHAR* const end = lower->key_data + lower->key_length; + while (q < end) { - skipLowerKey = false; - break; + if (*p++ != *q++) + { + skipLowerKey = false; + break; + } + } + + if ((q >= end) && (p < node.data + node.length) && skipLowerKey && partLower) + { + // since key length always is multiplier of (STUFF_COUNT + 1) (for partial + // compound keys) and we passed lower key completely then p pointed + // us to the next segment number and we can use this fact to calculate + // how many segments is equal to lower key + const USHORT segnum = idx.idx_count - (UCHAR) (descending ? ((*p) ^ -1) : *p); + + if (segnum < retrieval->irb_lower_count) + skipLowerKey = false; } } - - if ((q >= end) && (p < node.data + node.length) && skipLowerKey && partLower) - { - // since key length always is multiplier of (STUFF_COUNT + 1) (for partial - // compound keys) and we passed lower key completely then p pointed - // us to the next segment number and we can use this fact to calculate - // how many segments is equal to lower key - const USHORT segnum = idx.idx_count - (UCHAR) (descending ? ((*p) ^ -1) : *p); - - if (segnum < retrieval->irb_lower_count) - skipLowerKey = false; - } + else + skipLowerKey = false; } - else - skipLowerKey = false; } - } - else - { - pointer = page->btr_nodes + page->btr_jump_size; - prefix = 0; - skipLowerKey = false; - } - - // if there is an upper bound, scan the index pages looking for it - if (retrieval->irb_upper_count) - { - while (scan(tdbb, pointer, bitmap, bitmap_and, &idx, retrieval, prefix, &upper, - skipLowerKey, lower)) + else { - page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); pointer = page->btr_nodes + page->btr_jump_size; prefix = 0; + skipLowerKey = false; } - } - else - { - // if there isn't an upper bound, just walk the index to the end of the level - const UCHAR* endPointer = (UCHAR*) page + page->btr_length; - const bool ignoreNulls = - (retrieval->irb_generic & irb_ignore_null_value_key) && (idx.idx_count == 1); - IndexNode node; - pointer = node.readNode(pointer, true); - - // Check if pointer is still valid - if (pointer > endPointer) - BUGCHECK(204); // msg 204 index inconsistent - - while (true) + // if there is an upper bound, scan the index pages looking for it + if (retrieval->irb_upper_count) { - if (node.isEndLevel) - break; - - if (!node.isEndBucket) + while (scan(tdbb, pointer, bitmap, bitmap_and, &idx, retrieval, prefix, upper, + skipLowerKey, *lower)) { - // If we're walking in a descending index and we need to ignore NULLs - // then stop at the first NULL we see (only for single segment!) - if (descending && ignoreNulls && node.prefix == 0 && - node.length >= 1 && node.data[0] == 255) - { - break; - } - - if (skipLowerKey) - checkForLowerKeySkip(skipLowerKey, partLower, node, lower, idx, retrieval); - - if (!skipLowerKey) - { - if (!bitmap_and || bitmap_and->test(node.recordNumber.getValue())) - RBM_SET(tdbb->getDefaultPool(), bitmap, node.recordNumber.getValue()); - } - - pointer = node.readNode(pointer, true); - - // Check if pointer is still valid - if (pointer > endPointer) - BUGCHECK(204); // msg 204 index inconsistent - - continue; + page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + pointer = page->btr_nodes + page->btr_jump_size; + prefix = 0; } + } + else + { + // if there isn't an upper bound, just walk the index to the end of the level + const UCHAR* endPointer = (UCHAR*) page + page->btr_length; + const bool ignoreNulls = + (retrieval->irb_generic & irb_ignore_null_value_key) && (idx.idx_count == 1); - page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); - endPointer = (UCHAR*) page + page->btr_length; - pointer = page->btr_nodes + page->btr_jump_size; + IndexNode node; pointer = node.readNode(pointer, true); // Check if pointer is still valid if (pointer > endPointer) BUGCHECK(204); // msg 204 index inconsistent - } - } - CCH_RELEASE(tdbb, &window); + while (true) + { + if (node.isEndLevel) + break; + + if (!node.isEndBucket) + { + // If we're walking in a descending index and we need to ignore NULLs + // then stop at the first NULL we see (only for single segment!) + if (descending && ignoreNulls && node.prefix == 0 && + node.length >= 1 && node.data[0] == 255) + { + break; + } + + if (skipLowerKey) + checkForLowerKeySkip(skipLowerKey, partLower, node, *lower, idx, retrieval); + + if (!skipLowerKey) + { + if (!bitmap_and || bitmap_and->test(node.recordNumber.getValue())) + RBM_SET(tdbb->getDefaultPool(), bitmap, node.recordNumber.getValue()); + } + + pointer = node.readNode(pointer, true); + + // Check if pointer is still valid + if (pointer > endPointer) + BUGCHECK(204); // msg 204 index inconsistent + + continue; + } + + page = (btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + endPointer = (UCHAR*) page + page->btr_length; + pointer = page->btr_nodes + page->btr_jump_size; + pointer = node.readNode(pointer, true); + + // Check if pointer is still valid + if (pointer > endPointer) + BUGCHECK(204); // msg 204 index inconsistent + } + } + + CCH_RELEASE(tdbb, &window); + } while ((lower = lower->key_next.get()) && (upper = upper->key_next.get())); } @@ -869,7 +878,8 @@ btree_page* BTR_find_page(thread_db* tdbb, WIN* window, index_desc* idx, temporary_key* lower, - temporary_key* upper) + temporary_key* upper, + bool makeKeys) { /************************************** * @@ -889,19 +899,26 @@ btree_page* BTR_find_page(thread_db* tdbb, // are looking for an equality if (retrieval->irb_key) { + fb_assert(makeKeys); copy_key(retrieval->irb_key, lower); copy_key(retrieval->irb_key, upper); } - else + else if (makeKeys) { idx_e errorCode = idx_e_ok; + const USHORT keyType = + (retrieval->irb_generic & irb_multi_starting) ? INTL_KEY_MULTI_STARTING : + (retrieval->irb_generic & irb_starting) ? INTL_KEY_PARTIAL : + (retrieval->irb_desc.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : + INTL_KEY_SORT; + if (retrieval->irb_upper_count) { errorCode = BTR_make_key(tdbb, retrieval->irb_upper_count, retrieval->irb_value + retrieval->irb_desc.idx_count, &retrieval->irb_desc, upper, - (retrieval->irb_generic & irb_starting) != 0); + keyType); } if (errorCode == idx_e_ok) @@ -910,7 +927,7 @@ btree_page* BTR_find_page(thread_db* tdbb, { errorCode = BTR_make_key(tdbb, retrieval->irb_lower_count, retrieval->irb_value, &retrieval->irb_desc, lower, - (retrieval->irb_generic & irb_starting) != 0); + keyType); } } @@ -1090,7 +1107,11 @@ void BTR_insert(thread_db* tdbb, WIN* root_window, index_insertion* insertion) window.win_page = root->irt_rpt[idx->idx_id].getRoot(); bucket = (btree_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_index); - key = ret_key; + key.key_length = ret_key.key_length; + memcpy(key.key_data, ret_key.key_data, ret_key.key_length); + key.key_flags = ret_key.key_flags; + key.key_nulls = ret_key.key_nulls; + key.key_next.reset(ret_key.key_next.release()); } // the original page was marked as not garbage-collectable, but @@ -1177,7 +1198,7 @@ void BTR_insert(thread_db* tdbb, WIN* root_window, index_insertion* insertion) idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* idx, - temporary_key* key, const bool fuzzy, USHORT count) + temporary_key* key, const USHORT keyType, USHORT count) { /************************************** * @@ -1214,11 +1235,6 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id const USHORT maxKeyLength = dbb->getMaxIndexKeyLength(); try { - - const USHORT keyType = fuzzy ? - INTL_KEY_PARTIAL : - ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT); - // Special case single segment indices if (idx->idx_count == 1) @@ -1241,7 +1257,8 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id // isNull = !EVL_field(relation, record, tail->idx_field, desc_ptr); - if (!isNull && desc_ptr->dsc_dtype == dtype_text) + if (!isNull && desc_ptr->dsc_dtype == dtype_text && + tail->idx_field < record->getFormat()->fmt_desc.getCount()) { // That's necessary for NO-PAD collations. INTL_adjust_text_descriptor(tdbb, desc_ptr); @@ -1280,7 +1297,8 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id key->key_nulls |= 1 << n; else { - if (desc_ptr->dsc_dtype == dtype_text) + if (desc_ptr->dsc_dtype == dtype_text && + tail->idx_field < record->getFormat()->fmt_desc.getCount()) { // That's necessary for NO-PAD collations. INTL_adjust_text_descriptor(tdbb, desc_ptr); @@ -1519,7 +1537,7 @@ idx_e BTR_make_key(thread_db* tdbb, const ValueExprNode* const* exprs, const index_desc* idx, temporary_key* key, - bool fuzzy) + USHORT keyType) { /************************************** * @@ -1548,13 +1566,11 @@ idx_e BTR_make_key(thread_db* tdbb, key->key_flags = 0; key->key_nulls = 0; + const bool fuzzy = (keyType == INTL_KEY_PARTIAL || keyType == INTL_KEY_MULTI_STARTING); const bool descending = (idx->idx_flags & idx_descending); const index_desc::idx_repeat* tail = idx->idx_rpt; - const USHORT keyType = fuzzy ? - INTL_KEY_PARTIAL : ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT); - const USHORT maxKeyLength = dbb->getMaxIndexKeyLength(); // If the index is a single segment index, don't sweat the compound stuff @@ -1570,7 +1586,10 @@ idx_e BTR_make_key(thread_db* tdbb, compress(tdbb, desc, key, tail->idx_itype, isNull, descending, keyType); if (fuzzy && (key->key_flags & key_empty)) + { key->key_length = 0; + key->key_next.reset(); + } } else { @@ -1607,37 +1626,64 @@ idx_e BTR_make_key(thread_db* tdbb, prior_length = (p - key->key_data); - const UCHAR* q = temp.key_data; - for (USHORT l = temp.key_length; l; --l, --stuff_count) - { - if (stuff_count == 0) - { - *p++ = idx->idx_count - n; - stuff_count = STUFF_COUNT; + fb_assert(n == count - 1 || !temp.key_next); - if (p - key->key_data >= maxKeyLength) + SSHORT save_stuff_count = stuff_count; + temporary_key* current_key = key; + temporary_key* temp_ptr = &temp; + + do + { + const UCHAR* q = temp_ptr->key_data; + + for (USHORT l = temp_ptr->key_length; l; --l, --stuff_count) + { + if (stuff_count == 0) + { + *p++ = idx->idx_count - n; + stuff_count = STUFF_COUNT; + + if (p - current_key->key_data >= maxKeyLength) + return idx_e_keytoobig; + } + + *p++ = *q++; + + if (p - current_key->key_data >= maxKeyLength) return idx_e_keytoobig; } - *p++ = *q++; + // AB: Fix bug SF #1242982 + // Equality search on first segment (integer) in compound indexes resulted + // in more scans on specific values (2^n, f.e. 131072) than needed. + if (!fuzzy && count != idx->idx_count && n == count - 1) + { + for (; stuff_count; --stuff_count) + { + *p++ = 0; - if (p - key->key_data >= maxKeyLength) - return idx_e_keytoobig; - } - } + if (p - current_key->key_data >= maxKeyLength) + return idx_e_keytoobig; + } + } - // AB: Fix bug SF #1242982 - // Equality search on first segment (integer) in compound indexes resulted - // in more scans on specific values (2^n, f.e. 131072) than needed. - if (!fuzzy && (n != idx->idx_count)) - { - for (; stuff_count; --stuff_count) - { - *p++ = 0; + current_key->key_length = p - current_key->key_data; - if (p - key->key_data >= maxKeyLength) - return idx_e_keytoobig; - } + if ((temp_ptr = temp_ptr->key_next.get())) + { + temporary_key* next_key = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key(); + next_key->key_length = 0; + next_key->key_flags = key->key_flags; + next_key->key_nulls = key->key_nulls; + memcpy(next_key->key_data, key->key_data, prior_length); + + current_key->key_next = next_key; + current_key = next_key; + p = current_key->key_data + prior_length; + + stuff_count = save_stuff_count; + } + } while (temp_ptr); } // dimitr: If the search is fuzzy and the last segment is empty, @@ -1645,8 +1691,6 @@ idx_e BTR_make_key(thread_db* tdbb, // the rule that every string starts with an empty string. if (fuzzy && (temp.key_flags & key_empty)) key->key_length = prior_length; - else - key->key_length = (p - key->key_data); if (is_key_empty) { @@ -1707,7 +1751,7 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke // If the index is a single segment index, don't sweat the compound stuff if ((idx->idx_count == 1) || (idx->idx_flags & idx_expressn)) { - compress(tdbb, &null_desc, key, tail->idx_itype, true, descending, false); + compress(tdbb, &null_desc, key, tail->idx_itype, true, descending, INTL_KEY_SORT); } else { @@ -1721,7 +1765,7 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke for (; stuff_count; --stuff_count) *p++ = 0; - compress(tdbb, &null_desc, &temp, tail->idx_itype, true, descending, false); + compress(tdbb, &null_desc, &temp, tail->idx_itype, true, descending, INTL_KEY_SORT); const UCHAR* q = temp.key_data; for (USHORT l = temp.key_length; l; --l, --stuff_count) @@ -2425,16 +2469,24 @@ static void compress(thread_db* tdbb, * Compress a data value into an index key. * **************************************/ - union { - INT64_KEY temp_int64_key; - double temp_double; - ULONG temp_ulong; - SLONG temp_slong; - SINT64 temp_sint64; - UCHAR temp_char[sizeof(INT64_KEY)]; - } temp; - bool temp_is_negative = false; - bool int64_key_op = false; + if (isNull) + { + const UCHAR pad = 0; + key->key_flags &= ~key_empty; + // AB: NULL should be threated as lowest value possible. + // Therefore don't complement pad when we have an ascending index. + if (descending) + { + // DESC NULLs are stored as 1 byte + key->key_data[0] = pad; + key->key_length = 1; + } + else + key->key_length = 0; // ASC NULLs are stored with no data + + fb_assert(!key->key_next); + return; + } // For descending index and new index structure we insert 0xFE at the beginning. // This is only done for values which begin with 0xFE (254) or 0xFF (255) and @@ -2445,96 +2497,151 @@ static void compress(thread_db* tdbb, const UCHAR desc_end_value_check = 0x00; // ~0xFF; const Database* dbb = tdbb->getDatabase(); - + bool first_key = true; + VaryStr buffer; + size_t multiKeyLength; + UCHAR* ptr; UCHAR* p = key->key_data; - if (isNull) - { - const UCHAR pad = 0; - key->key_flags &= ~key_empty; - // AB: NULL should be threated as lowest value possible. - // Therefore don't complement pad when we have an ascending index. - if (descending) - { - // DESC NULLs are stored as 1 byte - *p++ = pad; - key->key_length = (p - key->key_data); - } - else - key->key_length = 0; // ASC NULLs are stored with no data - - return; - } - if (itype == idx_string || itype == idx_byte_array || itype == idx_metadata || itype == idx_decimal || itype >= idx_first_intl_string) { - VaryStr buffer; - const UCHAR pad = (itype == idx_string) ? ' ' : 0; - UCHAR* ptr; + temporary_key* root_key = key; + bool has_next; - size_t length; - - if (itype == idx_decimal) + do { - Decimal128 dec = MOV_get_dec128(tdbb, desc); - length = dec.makeIndexKey(&buffer); - ptr = reinterpret_cast(buffer.vary_string); - } - else if (itype >= idx_first_intl_string || itype == idx_metadata) - { - DSC to; + size_t length; - // convert to an international byte array - to.dsc_dtype = dtype_text; - to.dsc_flags = 0; - to.dsc_sub_type = 0; - to.dsc_scale = 0; - to.dsc_ttype() = ttype_sort_key; - to.dsc_length = MIN(MAX_KEY, sizeof(buffer)); - ptr = to.dsc_address = reinterpret_cast(buffer.vary_string); - length = INTL_string_to_key(tdbb, itype, desc, &to, key_type); - } - else - length = MOV_get_string(tdbb, desc, &ptr, &buffer, MAX_KEY); + has_next = false; - if (length) - { - // clear key_empty flag, because length is >= 1 - key->key_flags &= ~key_empty; - - if (length > sizeof(key->key_data)) - length = sizeof(key->key_data); - - if (descending && ((*ptr == desc_end_value_prefix) || (*ptr == desc_end_value_check))) + if (first_key) { - *p++ = desc_end_value_prefix; - if ((length + 1) > sizeof(key->key_data)) - length = sizeof(key->key_data) - 1; + first_key = false; + + if (itype == idx_decimal) + { + Decimal128 dec = MOV_get_dec128(tdbb, desc); + length = dec.makeIndexKey(&buffer); + ptr = reinterpret_cast(buffer.vary_string); + } + else if (itype >= idx_first_intl_string || itype == idx_metadata) + { + DSC to; + + // convert to an international byte array + to.dsc_dtype = dtype_text; + to.dsc_flags = 0; + to.dsc_sub_type = 0; + to.dsc_scale = 0; + to.dsc_ttype() = ttype_sort_key; + to.dsc_length = MIN(MAX_COLUMN_SIZE, MAX_KEY * 4); + ptr = to.dsc_address = reinterpret_cast(buffer.vary_string); + multiKeyLength = length = INTL_string_to_key(tdbb, itype, desc, &to, key_type); + } + else + length = MOV_get_string(tdbb, desc, &ptr, &buffer, MAX_KEY); } - memcpy(p, ptr, length); - p += length; - } - else - { - // Leave key_empty flag, because the string is an empty string - if (descending && ((pad == desc_end_value_prefix) || (pad == desc_end_value_check))) - *p++ = desc_end_value_prefix; + if (key_type == INTL_KEY_MULTI_STARTING && multiKeyLength != 0) + { + fb_assert(ptr < (UCHAR*) buffer.vary_string + multiKeyLength); - *p++ = pad; - } + length = ptr[0] + ptr[1] * 256; + ptr += 2; - while (p > key->key_data) - { - if (*--p != pad) - break; - } + has_next = ptr + length < (UCHAR*) buffer.vary_string + multiKeyLength; + + if (descending) + { + if (has_next) + { + temporary_key* new_key = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key(); + new_key->key_length = 0; + new_key->key_flags = 0; + new_key->key_nulls = 0; + new_key->key_next = key == root_key ? NULL : key; + + key = new_key; + } + else if (key != root_key) + { + root_key->key_next = key; + key = root_key; + } + + p = key->key_data; + } + } + + const UCHAR pad = (itype == idx_string) ? ' ' : 0; + + if (length) + { + // clear key_empty flag, because length is >= 1 + key->key_flags &= ~key_empty; + + if (length > sizeof(key->key_data)) + length = sizeof(key->key_data); + + if (descending && ((*ptr == desc_end_value_prefix) || (*ptr == desc_end_value_check))) + { + *p++ = desc_end_value_prefix; + if ((length + 1) > sizeof(key->key_data)) + length = sizeof(key->key_data) - 1; + } + + memcpy(p, ptr, length); + p += length; + } + else + { + // Leave key_empty flag, because the string is an empty string + if (descending && ((pad == desc_end_value_prefix) || (pad == desc_end_value_check))) + *p++ = desc_end_value_prefix; + + *p++ = pad; + } + + while (p > key->key_data) + { + if (*--p != pad) + break; + } + + key->key_length = p + 1 - key->key_data; + + if (has_next && !descending) + { + temporary_key* new_key = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key(); + new_key->key_length = 0; + new_key->key_flags = 0; + new_key->key_nulls = 0; + key->key_next = new_key; + + key = new_key; + p = key->key_data; + } + + ptr += length; + } while (has_next); - key->key_length = p + 1 - key->key_data; return; } + p = key->key_data; + + union { + INT64_KEY temp_int64_key; + double temp_double; + ULONG temp_ulong; + SLONG temp_slong; + SINT64 temp_sint64; + UCHAR temp_char[sizeof(INT64_KEY)]; + } temp; + bool temp_is_negative = false; + bool int64_key_op = false; + // The index is numeric. // For idx_numeric... // Convert the value to a double precision number, @@ -3284,7 +3391,7 @@ static DSC* eval(thread_db* tdbb, const ValueExprNode* node, DSC* temp, bool* is **************************************/ SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); dsc* desc = EVL_expr(tdbb, request, node); *isNull = false; @@ -6033,7 +6140,7 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re { bool notNull = false; const dsc* const desc = BTR_eval_expression(tdbb, idx, record, notNull); - value = DescPrinter(tdbb, notNull ? desc : NULL, MAX_KEY_STRING_LEN).get(); + value = DescPrinter(tdbb, notNull ? desc : NULL, MAX_KEY_STRING_LEN, CS_METADATA).get(); key += " = " + value; } else @@ -6052,7 +6159,7 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re dsc desc; const bool notNull = EVL_field(relation, record, field_id, &desc); - value = DescPrinter(tdbb, notNull ? &desc : NULL, MAX_KEY_STRING_LEN).get(); + value = DescPrinter(tdbb, notNull ? &desc : NULL, MAX_KEY_STRING_LEN, CS_METADATA).get(); key += " = " + value; if (i < idx->idx_count - 1) diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 47cc91c009..b3f630a247 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -43,7 +43,7 @@ namespace Jrd { class jrd_rel; class jrd_tra; template class vec; -class JrdStatement; +class Statement; struct temporary_key; class jrd_tra; class BtrPageGCLock; @@ -66,7 +66,7 @@ struct index_desc vec* idx_foreign_indexes; // ids for foreign key partner indexes ValueExprNode* idx_expression; // node tree for indexed expresssion dsc idx_expression_desc; // descriptor for expression result - JrdStatement* idx_expression_statement; // stored statement for expression evaluation + Statement* idx_expression_statement; // stored statement for expression evaluation // This structure should exactly match IRTD structure for current ODS struct idx_repeat { @@ -76,11 +76,7 @@ struct index_desc } idx_rpt[MAX_INDEX_SEGMENTS]; }; -struct IndexDescAlloc : public pool_alloc_rpt -{ - index_desc items[1]; -}; - +typedef Firebird::HalfStaticArray IndexDescList; const USHORT idx_invalid = USHORT(~0); // Applies to idx_id as special value @@ -154,6 +150,7 @@ struct temporary_key UCHAR key_flags; USHORT key_nulls; // bitmap of encountered null segments, // USHORT is enough to store MAX_INDEX_SEGMENTS bits + Firebird::AutoPtr key_next; // next key (INTL_KEY_MULTI_STARTING) }; @@ -227,6 +224,7 @@ const int irb_ignore_null_value_key = 8; // if lower bound is specified and upp const int irb_descending = 16; // Base index uses descending order const int irb_exclude_lower = 32; // exclude lower bound keys while scanning index const int irb_exclude_upper = 64; // exclude upper bound keys while scanning index +const int irb_multi_starting = 128; // Use INTL_KEY_MULTI_STARTING typedef Firebird::HalfStaticArray SelectivityList; @@ -242,6 +240,12 @@ public: void disablePageGC(thread_db* tdbb, const PageNumber &page); void enablePageGC(thread_db* tdbb); + // return true if lock is active + bool isActive() const + { + return lck_id != 0; + } + static bool isPageGCAllowed(thread_db* tdbb, const PageNumber& page); #ifdef DEBUG_LCK_LIST @@ -250,7 +254,7 @@ public: { } - static bool checkPool(const Lock* lock, Firebird::MemoryPool* pool) + static bool checkPool(const Lock* lock, Firebird::MemoryPool* pool) { if (!pool || !lock) return false; diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index 71a38586fa..e33482ba34 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -29,7 +29,7 @@ #include "../jrd/req.h" #include "../jrd/exe.h" -USHORT BTR_all(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::IndexDescAlloc**, Jrd::RelationPages*); +void BTR_all(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::IndexDescList&, Jrd::RelationPages*); void BTR_complement_key(Jrd::temporary_key*); void BTR_create(Jrd::thread_db*, Jrd::IndexCreation&, Jrd::SelectivityList&); bool BTR_delete_index(Jrd::thread_db*, Jrd::win*, USHORT); @@ -38,15 +38,15 @@ DSC* BTR_eval_expression(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*, bool&) void BTR_evaluate(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::RecordBitmap**, Jrd::RecordBitmap*); UCHAR* BTR_find_leaf(Ods::btree_page*, Jrd::temporary_key*, UCHAR*, USHORT*, bool, bool); Ods::btree_page* BTR_find_page(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::win*, Jrd::index_desc*, - Jrd::temporary_key*, Jrd::temporary_key*); + Jrd::temporary_key*, Jrd::temporary_key*, bool = true); void BTR_insert(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); Jrd::idx_e BTR_key(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::Record*, Jrd::index_desc*, Jrd::temporary_key*, - const bool, USHORT = 0); + const USHORT, USHORT = 0); USHORT BTR_key_length(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); Ods::btree_page* BTR_left_handoff(Jrd::thread_db*, Jrd::win*, Ods::btree_page*, SSHORT); bool BTR_lookup(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::index_desc*, Jrd::RelationPages*); Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const Jrd::index_desc*, - Jrd::temporary_key*, bool); + Jrd::temporary_key*, USHORT); void BTR_make_null_key(Jrd::thread_db*, const Jrd::index_desc*, Jrd::temporary_key*); bool BTR_next_index(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*); void BTR_remove(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index d112aef6b2..401c1158a6 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:351 + FORMAL BUILD NUMBER:477 */ -#define PRODUCT_VER_STRING "5.0.0.351" -#define FILE_VER_STRING "WI-T5.0.0.351" -#define LICENSE_VER_STRING "WI-T5.0.0.351" -#define FILE_VER_NUMBER 5, 0, 0, 351 +#define PRODUCT_VER_STRING "5.0.0.477" +#define FILE_VER_STRING "WI-T5.0.0.477" +#define LICENSE_VER_STRING "WI-T5.0.0.477" +#define FILE_VER_NUMBER 5, 0, 0, 477 #define FB_MAJOR_VER "5" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "351" +#define FB_BUILD_NO "477" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial" diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index c891e8a54f..9a7051cc6e 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -48,7 +48,6 @@ #include "../jrd/align.h" #include "../jrd/lls.h" #include "../jrd/exe.h" -#include "../jrd/rse.h" #include "../jrd/scl.h" #include "../jrd/tra.h" #include "../jrd/lck.h" @@ -69,12 +68,11 @@ #include "../jrd/jrd_proto.h" #include "../jrd/lck_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../common/dsc_proto.h" -#include "../jrd/Optimizer.h" +#include "../jrd/optimizer/Optimizer.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/SysFunction.h" @@ -143,38 +141,26 @@ BoolExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, BoolExpr return clone; } - -jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool internal_flag, - ULONG dbginfo_length, const UCHAR* dbginfo) +// Compile a statement. +Statement* CMP_compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, + ULONG dbginfoLength, const UCHAR* dbginfo) { -/************************************** - * - * C M P _ c o m p i l e 2 - * - ************************************** - * - * Functional description - * Compile a BLR request. - * - **************************************/ - jrd_req* request = NULL; + Statement* statement = nullptr; SET_TDBB(tdbb); - Jrd::Attachment* const att = tdbb->getAttachment(); + const auto att = tdbb->getAttachment(); // 26.09.2002 Nickolay Samofatov: default memory pool will become statement pool // and will be freed by CMP_release - MemoryPool* const new_pool = att->createPool(); + const auto newPool = att->createPool(); try { - Jrd::ContextPoolHolder context(tdbb, new_pool); + Jrd::ContextPoolHolder context(tdbb, newPool); - CompilerScratch* csb = - PAR_parse(tdbb, blr, blr_length, internal_flag, dbginfo_length, dbginfo); + const auto csb = PAR_parse(tdbb, blr, blrLength, internalFlag, dbginfoLength, dbginfo); - request = JrdStatement::makeRequest(tdbb, csb, internal_flag); - new_pool->setStatsGroup(request->req_memory_stats); + statement = Statement::makeStatement(tdbb, csb, internalFlag); #ifdef CMP_DEBUG if (csb->csb_dump.hasData()) @@ -196,20 +182,40 @@ jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool } #endif - request->getStatement()->verifyAccess(tdbb); + statement->verifyAccess(tdbb); delete csb; } catch (const Firebird::Exception& ex) { ex.stuffException(tdbb->tdbb_status_vector); - if (request) - CMP_release(tdbb, request); + if (statement) + statement->release(tdbb); else - att->deletePool(new_pool); + att->deletePool(newPool); ERR_punt(); } + return statement; +} + +Request* CMP_compile_request(thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag) +{ +/************************************** + * + * C M P _ c o m p i l e _ r e q u e s t + * + ************************************** + * + * Functional description + * Compile a BLR request. + * + **************************************/ + SET_TDBB(tdbb); + + auto statement = CMP_compile(tdbb, blr, blrLength, internalFlag, 0, nullptr); + auto request = statement->getRequest(tdbb, 0); + return request; } @@ -321,7 +327,7 @@ void CMP_post_access(thread_db* tdbb, const MetaName& security_name, SLONG ssRelationId, // SQL SECURITY relation in which context permissions should be check SecurityClass::flags_t mask, - SLONG type_name, + ObjectType obj_type, const MetaName& name, const MetaName& r_name) { @@ -347,7 +353,7 @@ void CMP_post_access(thread_db* tdbb, SET_TDBB(tdbb); - AccessItem access(security_name, ssRelationId, name, type_name, mask, r_name); + AccessItem access(security_name, ssRelationId, name, obj_type, mask, r_name); FB_SIZE_T i; @@ -395,7 +401,7 @@ void CMP_post_resource( ResourceList* rsc_ptr, void* obj, Resource::rsc_s type, } -void CMP_release(thread_db* tdbb, jrd_req* request) +void CMP_release(thread_db* tdbb, Request* request) { /************************************** * @@ -481,13 +487,13 @@ void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* p { CMP_post_access(tdbb, csb, procedure->getSecurityName(), (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, SCL_object_procedure, procedure->getName().identifier); + SCL_execute, obj_procedures, procedure->getName().identifier); } else { CMP_post_access(tdbb, csb, procedure->getSecurityName(), (csb->csb_view ? csb->csb_view->rel_id : 0), - SCL_execute, SCL_object_package, procedure->getName().package); + SCL_execute, obj_packages, procedure->getName().package); } // Add the procedure to list of external objects accessed @@ -512,32 +518,15 @@ RecordSource* CMP_post_rse(thread_db* tdbb, CompilerScratch* csb, RseNode* rse) **************************************/ SET_TDBB(tdbb); - DEV_BLKCHK(csb, type_csb); - DEV_BLKCHK(rse, type_nod); + const auto rsb = Optimizer::compile(tdbb, csb, rse); - RecordSource* rsb = OPT_compile(tdbb, csb, rse, NULL); - - if (rse->flags & RseNode::FLAG_SINGULAR) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) SingularStream(csb, rsb); - - if (rse->flags & RseNode::FLAG_WRITELOCK) - { - for (StreamType i = 0; i < csb->csb_n_stream; i++) - csb->csb_rpt[i].csb_flags |= csb_update; - - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) LockedStream(csb, rsb); - } - - if (rse->flags & RseNode::FLAG_SCROLLABLE) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) BufferedStream(csb, rsb); - - // mark all the substreams as inactive + // Mark all the substreams as inactive StreamList streams; rse->computeRseStreams(streams); - for (StreamList::iterator i = streams.begin(); i != streams.end(); ++i) - csb->csb_rpt[*i].deactivate(); + for (const auto stream : streams) + csb->csb_rpt[stream].deactivate(); return rsb; } diff --git a/src/jrd/cmp_proto.h b/src/jrd/cmp_proto.h index c95f41c881..a8938a5665 100644 --- a/src/jrd/cmp_proto.h +++ b/src/jrd/cmp_proto.h @@ -37,21 +37,22 @@ StreamType* CMP_alloc_map(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType str Jrd::ValueExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); Jrd::BoolExprNode* CMP_clone_node_opt(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::BoolExprNode*); Jrd::ValueExprNode* CMP_clone_node(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::ValueExprNode*); -Jrd::jrd_req* CMP_compile2(Jrd::thread_db*, const UCHAR* blr, ULONG blr_length, bool internal_flag, - ULONG = 0, const UCHAR* = NULL); +Jrd::Statement* CMP_compile(Jrd::thread_db* tdbb, const UCHAR* blr, ULONG blrLength, bool internalFlag, + ULONG dbginfoLength, const UCHAR* dbginfo); +Jrd::Request* CMP_compile_request(Jrd::thread_db*, const UCHAR* blr, ULONG blrLength, bool internalFlag); Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element); const Jrd::Format* CMP_format(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType); Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); -Jrd::jrd_req* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); +Jrd::Request* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&); void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName&, SLONG ssRelationId, - Jrd::SecurityClass::flags_t, SLONG type_name, const Jrd::MetaName&, + Jrd::SecurityClass::flags_t, ObjectType obj_type, const Jrd::MetaName&, const Jrd::MetaName& = ""); void CMP_post_procedure_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*); void CMP_post_resource(Jrd::ResourceList*, void*, Jrd::Resource::rsc_s, USHORT); Jrd::RecordSource* CMP_post_rse(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::RseNode*); -void CMP_release(Jrd::thread_db*, Jrd::jrd_req*); +void CMP_release(Jrd::thread_db*, Jrd::Request*); #endif // JRD_CMP_PROTO_H diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 130c05b8aa..da2aa2f439 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -284,7 +284,8 @@ enum stat_group_t { stat_attachment = 1, stat_transaction = 2, stat_statement = 3, - stat_call = 4 + stat_call = 4, + stat_cmp_statement = 5 }; enum InfoType @@ -462,8 +463,8 @@ const TraNumber MAX_TRA_NUMBER = 0x0000FFFFFFFFFFFF; // ~2.8 * 10^14 // Number of streams, conjuncts, indices that will be statically allocated // in various arrays. Larger numbers will have to be allocated dynamically -// CVC: I think we need to have a special, higher value for streams. -const int OPT_STATIC_ITEMS = 64; +const unsigned OPT_STATIC_ITEMS = 16; +const unsigned OPT_STATIC_STREAMS = 64; #define CURRENT_ENGINE "Engine13" #define EMBEDDED_PROVIDERS "Providers=" CURRENT_ENGINE diff --git a/src/jrd/cvt.cpp b/src/jrd/cvt.cpp index 3578188ed3..efd887ecd9 100644 --- a/src/jrd/cvt.cpp +++ b/src/jrd/cvt.cpp @@ -580,10 +580,7 @@ SLONG EngineCallbacks::getLocalDate() thread_db* tdbb = JRD_get_thread_data(); if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getRequest()) - { - fb_assert(!tdbb->getRequest()->req_gmt_timestamp.isEmpty()); - return tdbb->getRequest()->getLocalTimeStamp().value().timestamp_date; - } + return tdbb->getRequest()->getLocalTimeStamp().timestamp_date; return TimeZoneUtil::timeStampTzToTimeStamp( TimeZoneUtil::getCurrentSystemTimeStamp(), getSessionTimeZone()).timestamp_date; @@ -595,10 +592,7 @@ ISC_TIMESTAMP EngineCallbacks::getCurrentGmtTimeStamp() thread_db* tdbb = JRD_get_thread_data(); if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getRequest()) - { - fb_assert(!tdbb->getRequest()->req_gmt_timestamp.isEmpty()); - return tdbb->getRequest()->req_gmt_timestamp.value(); - } + return tdbb->getRequest()->getGmtTimeStamp(); return TimeZoneUtil::timeStampTzToTimeStamp(TimeZoneUtil::getCurrentSystemTimeStamp(), TimeZoneUtil::GMT_ZONE); } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 5d104fc606..fcc9106463 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -85,6 +85,7 @@ #include "../jrd/intl.h" #include "../intl/charsets.h" #include "../jrd/align.h" +#include "../dsql/DsqlStatementCache.h" #include "../common/gdsassert.h" #include "../jrd/blb_proto.h" #include "../jrd/btr_proto.h" @@ -983,7 +984,7 @@ namespace if (routine && !blobId.isEmpty()) { - JrdStatement* statement = NULL; + Statement* statement = NULL; // Nickolay Samofatov: allocate statement memory pool... MemoryPool* new_pool = attachment->createPool(); // block is used to ensure MET_verify_cache @@ -1477,6 +1478,9 @@ void DFW_perform_work(thread_db* tdbb, jrd_tra* transaction) } SET_TDBB(tdbb); + + tdbb->getAttachment()->att_dsql_instance->dbb_statement_cache->purgeAllAttachments(tdbb); + Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool); /* Loop for as long as any of the deferred work routines says that it has @@ -2754,10 +2758,12 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* if (!IDX.RDB$EXPRESSION_BLR.NULL) { - idx.idx_expression = static_cast(MET_get_dependencies( - tdbb, relation, NULL, 0, NULL, &IDX.RDB$EXPRESSION_BLR, - &idx.idx_expression_statement, &csb, work->dfw_name, obj_expression_index, 0, - transaction)); + MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR, + nullptr, &csb, work->dfw_name, obj_expression_index, 0, + transaction); + + idx.idx_expression_statement = Statement::makeValueExpression(tdbb, + idx.idx_expression, idx.idx_expression_desc, csb, false); } } // end scope @@ -2765,7 +2771,6 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* idx.idx_count = 1; idx.idx_flags |= idx_expressn; - idx.idx_expression->getDesc(tdbb, csb, &idx.idx_expression_desc); idx.idx_rpt[0].idx_itype = DFW_assign_index_type(tdbb, work->dfw_name, idx.idx_expression_desc.dsc_dtype, @@ -2795,7 +2800,7 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* SelectivityList selectivity(*tdbb->getDefaultPool()); jrd_tra* const current_transaction = tdbb->getTransaction(); - jrd_req* const current_request = tdbb->getRequest(); + Request* const current_request = tdbb->getRequest(); try { @@ -3998,6 +4003,7 @@ void DFW_reset_icu(thread_db* tdbb) "join RDB$CHARACTER_SETS cs on coll.RDB$CHARACTER_SET_ID = cs.RDB$CHARACTER_SET_ID " "where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%' " " and coalesce(ind.RDB$INDEX_INACTIVE, 0) = 0 " + " and coalesce(rel.RDB$RELATION_TYPE, 0) = 0 " // rel_persistent "group by ind.RDB$INDEX_NAME, rel.RDB$RELATION_ID, coll.RDB$BASE_COLLATION_NAME, " " coll.RDB$COLLATION_NAME, cs.RDB$CHARACTER_SET_NAME, coll.RDB$SPECIFIC_ATTRIBUTES"; @@ -5433,7 +5439,7 @@ static void get_trigger_dependencies(DeferredWork* work, bool compile, jrd_tra* if ((relation || (type & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DML) && !blob_id.isEmpty()) { - JrdStatement* statement = NULL; + Statement* statement = NULL; // Nickolay Samofatov: allocate statement memory pool... MemoryPool* new_pool = attachment->createPool(); USHORT par_flags; @@ -5905,19 +5911,19 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ if (notNull && !defaultValue->isEmpty()) { Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - JrdStatement* defaultStatement = NULL; + Statement* defaultStatement = NULL; try { ValueExprNode* defaultNode = static_cast(MET_parse_blob( tdbb, relation, defaultValue, NULL, &defaultStatement, false, false)); - jrd_req* const defaultRequest = defaultStatement->findRequest(tdbb); + Request* const defaultRequest = defaultStatement->findRequest(tdbb); // Attention: this is scoped to the end of this "try". - AutoSetRestore2 autoRequest(tdbb, + AutoSetRestore2 autoRequest(tdbb, &thread_db::getRequest, &thread_db::setRequest, defaultRequest); - TimeZoneUtil::validateGmtTimeStamp(defaultRequest->req_gmt_timestamp); + defaultRequest->validateTimeStamp(); TRA_attach_request(transaction, defaultRequest); dsc* result = EVL_expr(tdbb, defaultRequest, defaultNode); diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 1c7bc79715..71e5dc2b09 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -48,7 +48,6 @@ #include "../jrd/lck.h" #include "../jrd/cch.h" #include "../jrd/pag.h" -#include "../jrd/rse.h" #include "../jrd/val.h" #include "../jrd/vio_debug.h" #include "../jrd/cch_proto.h" diff --git a/src/jrd/err.cpp b/src/jrd/err.cpp index 582e95d4c9..dd4bd9585c 100644 --- a/src/jrd/err.cpp +++ b/src/jrd/err.cpp @@ -37,7 +37,6 @@ #include "../jrd/ods.h" #include "../jrd/btr.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/tra.h" #include "../jrd/cch_proto.h" #include "../jrd/met_proto.h" diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 5d3aa24b54..5116c08806 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -78,7 +78,6 @@ #include "../jrd/lls.h" #include "../jrd/intl.h" #include "../jrd/intl_classes.h" -#include "../jrd/rse.h" #include "../jrd/sort.h" #include "firebird/impl/blr.h" #include "../jrd/tra.h" @@ -135,26 +134,19 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) DEV_BLKCHK(node, type_nod); - jrd_req* request = tdbb->getRequest(); - impure_value* impure = request->getImpure(node->impureOffset); + Request* request = tdbb->getRequest(); // The only nodes that can be assigned to are: argument, field and variable. - int arg_number; - const dsc* desc; - const MessageNode* message; - Record* record; - const ParameterNode* paramNode; - const VariableNode* varNode; - const FieldNode* fieldNode; - - if ((paramNode = nodeAs(node))) + if (auto paramNode = nodeAs(node)) { - message = paramNode->message; - arg_number = paramNode->argNumber; - desc = &message->format->fmt_desc[arg_number]; + auto message = paramNode->message; + auto arg_number = paramNode->argNumber; + auto desc = &message->format->fmt_desc[arg_number]; - impure->vlu_desc.dsc_address = request->getImpure( + auto impure = request->getImpure(node->impureOffset); + + impure->vlu_desc.dsc_address = paramNode->getParamRequest(request)->getImpure( message->impureOffset + (IPTR) desc->dsc_address); impure->vlu_desc.dsc_dtype = desc->dsc_dtype; impure->vlu_desc.dsc_length = desc->dsc_length; @@ -177,15 +169,15 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) } else if (nodeIs(node)) return NULL; - else if ((varNode = nodeAs(node))) + else if (auto varNode = nodeAs(node)) { - // Calculate descriptor - impure = request->getImpure(varNode->varDecl->impureOffset); + auto impure = varNode->getVarRequest(request)->getImpure(varNode->varDecl->impureOffset); return &impure->vlu_desc; } - else if ((fieldNode = nodeAs(node))) + else if (auto fieldNode = nodeAs(node)) { - record = request->req_rpb[fieldNode->fieldStream].rpb_record; + auto record = request->req_rpb[fieldNode->fieldStream].rpb_record; + auto impure = request->getImpure(node->impureOffset); if (!EVL_field(0, record, fieldNode->fieldId, &impure->vlu_desc)) { @@ -251,7 +243,7 @@ RecordBitmap** EVL_bitmap(thread_db* tdbb, const InversionNode* node, RecordBitm case InversionNode::TYPE_DBKEY: { - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); impure_inversion* impure = request->getImpure(node->impure); RecordBitmap::reset(impure->inv_bitmap); const dsc* const desc = EVL_expr(tdbb, request, node->value); @@ -311,7 +303,7 @@ void EVL_dbkey_bounds(thread_db* tdbb, const Array& ranges, **************************************/ SET_TDBB(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); for (const auto node : ranges) { @@ -632,7 +624,7 @@ void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, d if (itemInfo == NULL) return; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); bool err = false; if (null && !itemInfo->nullable) @@ -672,11 +664,10 @@ void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, d request->req_flags = flags; } - Firebird::string s; - if (err) { ISC_STATUS status = isc_not_valid_for_var; + string s; const char* arg; if (item.type == Item::TYPE_CAST) diff --git a/src/jrd/evl_proto.h b/src/jrd/evl_proto.h index 964e0b9f44..cfaa53eafa 100644 --- a/src/jrd/evl_proto.h +++ b/src/jrd/evl_proto.h @@ -46,7 +46,7 @@ void EVL_validate(Jrd::thread_db*, const Jrd::Item&, const Jrd::ItemInfo*, dsc* namespace Jrd { // Evaluate a value expression. - inline dsc* EVL_expr(thread_db* tdbb, jrd_req* request, const ValueExprNode* node) + inline dsc* EVL_expr(thread_db* tdbb, Request* request, const ValueExprNode* node) { if (!node) BUGCHECK(303); // msg 303 Invalid expression for evaluation diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index ab0f4335d3..b6bdc44a87 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -67,7 +67,6 @@ #include "iberror.h" #include "../jrd/ods.h" #include "../jrd/btr.h" -#include "../jrd/rse.h" #include "../jrd/lck.h" #include "../jrd/intl.h" #include "../jrd/sbm.h" @@ -92,7 +91,6 @@ #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/par_proto.h" #include "../jrd/rlck_proto.h" @@ -216,11 +214,11 @@ string StatusXcp::as_text() const } -static void execute_looper(thread_db*, jrd_req*, jrd_tra*, const StmtNode*, jrd_req::req_s); -static void looper_seh(thread_db*, jrd_req*, const StmtNode*); -static void release_blobs(thread_db*, jrd_req*); -static void trigger_failure(thread_db*, jrd_req*); -static void stuff_stack_trace(const jrd_req*); +static void execute_looper(thread_db*, Request*, jrd_tra*, const StmtNode*, Request::req_s); +static void looper_seh(thread_db*, Request*, const StmtNode*); +static void release_blobs(thread_db*, Request*); +static void trigger_failure(thread_db*, Request*); +static void stuff_stack_trace(const Request*); const size_t MAX_STACK_TRACE = 2048; @@ -231,7 +229,7 @@ void EXE_assignment(thread_db* tdbb, const AssignmentNode* node) DEV_BLKCHK(node, type_nod); SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // Get descriptors of src field/parameter/variable, etc. request->req_flags &= ~req_null; @@ -245,7 +243,7 @@ void EXE_assignment(thread_db* tdbb, const AssignmentNode* node) void EXE_assignment(thread_db* tdbb, const ValueExprNode* source, const ValueExprNode* target) { SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // Get descriptors of src field/parameter/variable, etc. request->req_flags &= ~req_null; @@ -259,7 +257,15 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo const ValueExprNode* missing_node, const ValueExprNode* missing2_node) { SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); + + const auto toVar = nodeAs(to); + + if (toVar && toVar->outerDecl) + request = toVar->getVarRequest(request); + + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); // Get descriptors of receiving and sending fields/parameters, variables, etc. @@ -284,31 +290,39 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo null = -1; USHORT* impure_flags = NULL; - const ParameterNode* toParam; - const VariableNode* toVar; + const auto toParam = nodeAs(to); - if ((toParam = nodeAs(to))) + if (toParam) { const MessageNode* message = toParam->message; + const auto paramRequest = toParam->getParamRequest(request); if (toParam->argInfo) { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); + EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber), toParam->argInfo, from_desc, null == -1); } - impure_flags = request->getImpure( + impure_flags = paramRequest->getImpure( message->impureFlags + (sizeof(USHORT) * toParam->argNumber)); } - else if ((toVar = nodeAs(to))) + else if (toVar) { + const auto varRequest = toVar->getVarRequest(request); + if (toVar->varInfo) { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId), toVar->varInfo, from_desc, null == -1); } - impure_flags = &request->getImpure( + impure_flags = &varRequest->getImpure( toVar->varDecl->impureOffset)->vlu_flags; } @@ -321,43 +335,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo if (!null) { - // if necessary and appropriate, use the indicator variable - - if (toParam && toParam->argIndicator) - { - dsc* indicator = EVL_assign_to(tdbb, toParam->argIndicator); - temp.dsc_dtype = dtype_short; - temp.dsc_length = sizeof(SSHORT); - temp.dsc_scale = 0; - temp.dsc_sub_type = 0; - - SSHORT len; - - if ((from_desc->dsc_dtype <= dtype_varying) && (to_desc->dsc_dtype <= dtype_varying) && - (TEXT_LEN(from_desc) > TEXT_LEN(to_desc))) - { - len = TEXT_LEN(from_desc); - } - else - len = 0; - - temp.dsc_address = (UCHAR *) &len; - MOV_move(tdbb, &temp, indicator); - - if (len) - { - temp = *from_desc; - temp.dsc_length = TEXT_LEN(to_desc); - - if (temp.dsc_dtype == dtype_cstring) - temp.dsc_length += 1; - else if (temp.dsc_dtype == dtype_varying) - temp.dsc_length += 2; - - from_desc = &temp; - } - } - // Validate range for datetime values if (DTYPE_IS_DATE(from_desc->dsc_dtype)) @@ -454,7 +431,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo // Handle the null flag as appropriate for fields and message arguments. - const FieldNode* toField = nodeAs(to); if (toField) { @@ -499,12 +475,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo temp.dsc_sub_type = 0; temp.dsc_address = (UCHAR*) &null; MOV_move(tdbb, &temp, to_desc); - - if (null && toParam->argIndicator) - { - to_desc = EVL_assign_to(tdbb, toParam->argIndicator); - MOV_move(tdbb, &temp, to_desc); - } } } @@ -619,7 +589,7 @@ void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTri void EXE_receive(thread_db* tdbb, - jrd_req* request, + Request* request, USHORT msg, ULONG length, void* buffer, @@ -677,9 +647,9 @@ void EXE_receive(thread_db* tdbb, try { if (nodeIs(request->req_message)) - execute_looper(tdbb, request, transaction, request->req_next, jrd_req::req_sync); + execute_looper(tdbb, request, transaction, request->req_next, Request::req_sync); - if (!(request->req_flags & req_active) || request->req_operation != jrd_req::req_send) + if (!(request->req_flags & req_active) || request->req_operation != Request::req_send) ERR_post(Arg::Gds(isc_req_sync)); const MessageNode* message = nodeAs(request->req_message); @@ -724,7 +694,7 @@ void EXE_receive(thread_db* tdbb, } } - execute_looper(tdbb, request, transaction, request->req_next, jrd_req::req_proceed); + execute_looper(tdbb, request, transaction, request->req_next, Request::req_proceed); } catch (const Exception&) { @@ -754,6 +724,12 @@ void EXE_receive(thread_db* tdbb, fb_assert(transaction->tra_save_free == savepoint); transaction->tra_save_free = savepoint->moveToStack(request->req_proc_sav_point); fb_assert(request->req_proc_sav_point == savepoint); + + // Ensure that the priorly existing savepoints are preserved, + // e.g. 10-11-12-(5-6-7) where savNumber == 5. This may happen + // due to looper savepoints being reused in subsequent invokations. + if (savepoint->getNumber() == savNumber) + break; } } catch (...) @@ -768,7 +744,7 @@ void EXE_receive(thread_db* tdbb, // Release a request instance. -void EXE_release(thread_db* tdbb, jrd_req* request) +void EXE_release(thread_db* tdbb, Request* request) { DEV_BLKCHK(request, type_req); @@ -789,10 +765,12 @@ void EXE_release(thread_db* tdbb, jrd_req* request) request->req_attachment = NULL; } + + request->req_flags &= ~req_in_use; } -void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const void* buffer) +void EXE_send(thread_db* tdbb, Request* request, USHORT msg, ULONG length, const void* buffer) { /************************************** * @@ -816,7 +794,7 @@ void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const const StmtNode* message = NULL; const StmtNode* node; - if (request->req_operation != jrd_req::req_receive) + if (request->req_operation != Request::req_receive) ERR_post(Arg::Gds(isc_req_sync)); node = request->req_message; @@ -855,11 +833,11 @@ void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const memcpy(request->getImpure(message->impureOffset), buffer, length); - execute_looper(tdbb, request, transaction, request->req_next, jrd_req::req_proceed); + execute_looper(tdbb, request, transaction, request->req_next, Request::req_proceed); } -void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) +void EXE_start(thread_db* tdbb, Request* request, jrd_tra* transaction) { /************************************** * @@ -882,7 +860,14 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) if (transaction->tra_flags & TRA_prepared) ERR_post(Arg::Gds(isc_req_no_trans)); - JrdStatement* statement = request->getStatement(); + const auto dbb = tdbb->getDatabase(); + const auto statement = request->getStatement(); + + // Generate request id. + request->setRequestId( + request->isRequestIdUnassigned() && request->isRoot() ? + statement->getStatementId() : + dbb->generateStatementId()); /* Post resources to transaction block. In particular, the interest locks on relations/indices are copied to the transaction, which is very @@ -907,7 +892,7 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) request->req_records_affected.clear(); // Store request start time for timestamp work - TimeZoneUtil::validateGmtTimeStamp(request->req_gmt_timestamp); + request->validateTimeStamp(); // Set all invariants to not computed. const ULONG* const* ptr, * const* end; @@ -925,11 +910,11 @@ void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction) execute_looper(tdbb, request, transaction, request->getStatement()->topNode, - jrd_req::req_evaluate); + Request::req_evaluate); } -void EXE_unwind(thread_db* tdbb, jrd_req* request) +void EXE_unwind(thread_db* tdbb, Request* request) { /************************************** * @@ -947,12 +932,12 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) if (request->req_flags & req_active) { - const JrdStatement* statement = request->getStatement(); + const Statement* statement = request->getStatement(); if (statement->fors.getCount() || request->req_ext_resultset || request->req_ext_stmt) { Jrd::ContextPoolHolder context(tdbb, request->req_pool); - jrd_req* old_request = tdbb->getRequest(); + Request* old_request = tdbb->getRequest(); jrd_tra* old_transaction = tdbb->getTransaction(); try { tdbb->setRequest(request); @@ -1011,7 +996,7 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) request->req_flags &= ~(req_active | req_proc_fetch | req_reserved); request->req_flags |= req_abort | req_stall; - request->req_gmt_timestamp.invalidate(); + request->invalidateTimeStamp(); request->req_caller = NULL; request->req_proc_inputs = NULL; request->req_proc_caller = NULL; @@ -1019,10 +1004,10 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) static void execute_looper(thread_db* tdbb, - jrd_req* request, + Request* request, jrd_tra* transaction, const StmtNode* node, - jrd_req::req_s next_state) + Request::req_s next_state) { /************************************** * @@ -1087,10 +1072,11 @@ static void execute_looper(thread_db* tdbb, if (savNumber) { - // There should be no other savepoint but the one started by ourselves. - // But just in case it suddenly happened, cleanup them all. - fb_assert(transaction->tra_save_point && - transaction->tra_save_point->getNumber() == savNumber); + // Unless the looper returns after SUSPEND (this preserves existing savepoints), + // there should be no other savepoint but the one started by ourselves. + fb_assert((request->req_flags & req_stall) || + (transaction->tra_save_point && + transaction->tra_save_point->getNumber() == savNumber)); while (transaction->tra_save_point && transaction->tra_save_point->getNumber() >= savNumber) @@ -1103,6 +1089,12 @@ static void execute_looper(thread_db* tdbb, fb_assert(savepoint == transaction->tra_save_free); transaction->tra_save_free = savepoint->moveToStack(request->req_savepoints); fb_assert(savepoint != transaction->tra_save_free); + + // Ensure that the priorly existing savepoints are preserved, + // e.g. 10-11-12-(5-6-7) where savNumber == 5. This may happen + // due to looper savepoints being reused in subsequent invokations. + if (savepoint->getNumber() == savNumber) + break; } } } @@ -1130,7 +1122,7 @@ void EXE_execute_triggers(thread_db* tdbb, SET_TDBB(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request ? request->req_transaction : tdbb->getTransaction(); TrigVector* vector = *triggers; @@ -1155,11 +1147,11 @@ void EXE_execute_triggers(thread_db* tdbb, TimeStamp timestamp; if (request) - timestamp = request->req_gmt_timestamp; + timestamp = request->getGmtTimeStamp(); else TimeZoneUtil::validateGmtTimeStamp(timestamp); - jrd_req* trigger = NULL; + Request* trigger = NULL; try { @@ -1203,13 +1195,13 @@ void EXE_execute_triggers(thread_db* tdbb, } } - trigger->req_gmt_timestamp = timestamp; + trigger->setGmtTimeStamp(timestamp.value()); trigger->req_trigger_action = trigger_action; TraceTrigExecute trace(tdbb, trigger, which_trig); { // Scope to replace att_ss_user - const JrdStatement* s = trigger->getStatement(); + const Statement* s = trigger->getStatement(); UserId* invoker = s->triggerInvoker ? s->triggerInvoker : tdbb->getAttachment()->att_ss_user; AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); @@ -1217,10 +1209,22 @@ void EXE_execute_triggers(thread_db* tdbb, &tdbb->getAttachment()->att_original_timezone, tdbb->getAttachment()->att_current_timezone); - EXE_start(tdbb, trigger, transaction); + if (trigger_action == TRIGGER_DISCONNECT) + { + if (!trigger->req_timer) + trigger->req_timer = FB_NEW_POOL(*tdbb->getAttachment()->att_pool) TimeoutTimer(); + + const unsigned int timeOut = tdbb->getDatabase()->dbb_config->getOnDisconnectTrigTimeout() * 1000; + trigger->req_timer->setup(timeOut, isc_cfg_stmt_timeout); + trigger->req_timer->start(); + thread_db::TimerGuard timerGuard(tdbb, trigger->req_timer, true); + EXE_start(tdbb, trigger, transaction); // Under timerGuard scope + } + else + EXE_start(tdbb, trigger, transaction); } - const bool ok = (trigger->req_operation != jrd_req::req_unwind); + const bool ok = (trigger->req_operation != Request::req_unwind); trace.finish(ok ? ITracePlugin::RESULT_SUCCESS : ITracePlugin::RESULT_FAILED); EXE_unwind(tdbb, trigger); @@ -1248,6 +1252,14 @@ void EXE_execute_triggers(thread_db* tdbb, trigger->req_flags &= ~req_in_use; ex.stuffException(tdbb->tdbb_status_vector); + + if (trigger_action == TRIGGER_DISCONNECT && + !(tdbb->tdbb_flags & TDBB_stack_trace_done) && (tdbb->tdbb_flags & TDBB_sys_error)) + { + stuff_stack_trace(trigger); + tdbb->tdbb_flags |= TDBB_stack_trace_done; + } + trigger_failure(tdbb, trigger); } @@ -1256,13 +1268,13 @@ void EXE_execute_triggers(thread_db* tdbb, } -static void stuff_stack_trace(const jrd_req* request) +static void stuff_stack_trace(const Request* request) { string sTrace; - for (const jrd_req* req = request; req; req = req->req_caller) + for (const Request* req = request; req; req = req->req_caller) { - const JrdStatement* const statement = req->getStatement(); + const Statement* const statement = req->getStatement(); string context, name; @@ -1320,7 +1332,7 @@ static void stuff_stack_trace(const jrd_req* request) } -const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* node) +const StmtNode* EXE_looper(thread_db* tdbb, Request* request, const StmtNode* node) { /************************************** * @@ -1358,7 +1370,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no { try { - if (request->req_operation == jrd_req::req_evaluate) + if (request->req_operation == Request::req_evaluate) { JRD_reschedule(tdbb); @@ -1400,7 +1412,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no exeState.errorPending = true; exeState.catchDisabled = true; - request->req_operation = jrd_req::req_unwind; + request->req_operation = Request::req_unwind; request->req_label = 0; if (!(tdbb->tdbb_flags & TDBB_stack_trace_done) && !(tdbb->tdbb_flags & TDBB_sys_error)) @@ -1433,7 +1445,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no TRA_release_request_snapshot(tdbb, request); request->req_flags &= ~(req_active | req_reserved); - request->req_gmt_timestamp.invalidate(); + request->invalidateTimeStamp(); release_blobs(tdbb, request); } @@ -1463,7 +1475,7 @@ const StmtNode* EXE_looper(thread_db* tdbb, jrd_req* request, const StmtNode* no // Start looper under Windows SEH (Structured Exception Handling) control -static void looper_seh(thread_db* tdbb, jrd_req* request, const StmtNode* node) +static void looper_seh(thread_db* tdbb, Request* request, const StmtNode* node) { #ifdef WIN_NT START_CHECK_FOR_EXCEPTIONS(NULL); @@ -1485,7 +1497,7 @@ static void looper_seh(thread_db* tdbb, jrd_req* request, const StmtNode* node) } -static void release_blobs(thread_db* tdbb, jrd_req* request) +static void release_blobs(thread_db* tdbb, Request* request) { /************************************** * @@ -1560,7 +1572,7 @@ static void release_blobs(thread_db* tdbb, jrd_req* request) } -static void trigger_failure(thread_db* tdbb, jrd_req* trigger) +static void trigger_failure(thread_db* tdbb, Request* trigger) { /************************************** * @@ -1582,7 +1594,7 @@ static void trigger_failure(thread_db* tdbb, jrd_req* trigger) MET_trigger_msg(tdbb, msg, trigger->getStatement()->triggerName, trigger->req_label); if (msg.hasData()) { - if (trigger->getStatement()->flags & JrdStatement::FLAG_SYS_TRIGGER) + if (trigger->getStatement()->flags & Statement::FLAG_SYS_TRIGGER) { ISC_STATUS code = PAR_symbol_to_gdscode(msg); if (code) diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 0d04522a4d..244acbd4a2 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -36,12 +36,12 @@ #include "../jrd/Relation.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" +#include "../common/classes/fb_pair.h" #include "../common/classes/NestConst.h" #include "iberror.h" #include "../common/dsc.h" -#include "../jrd/rse.h" #include "../jrd/err_proto.h" #include "../jrd/scl.h" @@ -74,7 +74,6 @@ template class vec; class jrd_prc; class Collation; struct index_desc; -struct IndexDescAlloc; class Format; class ForNode; class Cursor; @@ -188,9 +187,10 @@ typedef Firebird::SortedArray, struct AccessItem { MetaName acc_security_name; - SLONG acc_ss_rel_id; // Relation Id which owner will be used to check permissions - MetaName acc_name, acc_r_name; - SLONG acc_type; + SLONG acc_ss_rel_id; // Relation Id which owner will be used to check permissions + MetaName acc_name; + MetaName acc_r_name; + ObjectType acc_type; SecurityClass::flags_t acc_mask; static bool greaterThan(const AccessItem& i1, const AccessItem& i2) @@ -225,7 +225,7 @@ struct AccessItem } AccessItem(const MetaName& security_name, SLONG view_id, - const MetaName& name, SLONG type, + const MetaName& name, ObjectType type, SecurityClass::flags_t mask, const MetaName& relName) : acc_security_name(security_name), acc_ss_rel_id(view_id), acc_name(name), acc_r_name(relName), acc_type(type), acc_mask(mask) @@ -453,12 +453,15 @@ public: csb_current_nodes(p), csb_current_for_nodes(p), csb_computing_fields(p), + csb_variables_used_in_subroutines(p), csb_pool(p), csb_map_field_info(p), csb_map_item_info(p), csb_message_pad(p), subFunctions(p), subProcedures(p), + outerMessagesMap(p), + outerVarsMap(p), csb_currentForNode(NULL), csb_currentDMLNode(NULL), csb_currentAssignTarget(NULL), @@ -468,7 +471,7 @@ public: csb_dbg_info = FB_NEW_POOL(p) Firebird::DbgInfo(p); } - // Implemented in JrdStatement.cpp + // Implemented in Statement.cpp ULONG allocImpure(ULONG align, ULONG size); template @@ -517,6 +520,7 @@ public: // candidates within whose scope we are Firebird::Array csb_current_for_nodes; Firebird::SortedArray csb_computing_fields; // Computed fields being compiled + Firebird::SortedArray csb_variables_used_in_subroutines; StreamType csb_n_stream; // Next available stream USHORT csb_msg_number; // Highest used message number ULONG csb_impure; // Next offset into impure area @@ -541,8 +545,10 @@ public: bool csb_returning_expr; bool csb_implicit_cursor; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + Firebird::LeftPooledMap subFunctions; + Firebird::LeftPooledMap subProcedures; + Firebird::NonPooledMap outerMessagesMap; // + Firebird::NonPooledMap outerVarsMap; // ForNode* csb_currentForNode; StmtNode* csb_currentDMLNode; // could be StoreNode or ModifyNode @@ -554,21 +560,20 @@ public: // We must zero-initialize this one csb_repeat(); - void activate(); + void activate(bool subStream = false); void deactivate(); Nullable csb_cursor_number; // Cursor number for this stream StreamType csb_stream; // Map user context to internal stream StreamType csb_view_stream; // stream number for view relation, below USHORT csb_flags; - USHORT csb_indices; // Number of indices jrd_rel* csb_relation; Firebird::string* csb_alias; // SQL alias name for this instance of relation jrd_prc* csb_procedure; jrd_rel* csb_view; // parent view - IndexDescAlloc* csb_idx; // Packed description of indices + IndexDescList* csb_idx; // Packed description of indices MessageNode* csb_message; // Msg for send/receive const Format* csb_format; // Default Format for stream Format* csb_internal_format; // Statement internal format @@ -589,7 +594,6 @@ inline CompilerScratch::csb_repeat::csb_repeat() : csb_stream(0), csb_view_stream(0), csb_flags(0), - csb_indices(0), csb_relation(0), csb_alias(0), csb_procedure(0), @@ -632,9 +636,12 @@ const int csb_unmatched = 512; // stream has conjuncts unmatched by any index const int csb_update = 1024; // erase or modify for relation const int csb_unstable = 2048; // unstable explicit cursor -inline void CompilerScratch::csb_repeat::activate() +inline void CompilerScratch::csb_repeat::activate(bool subStream) { csb_flags |= csb_active; + + if (subStream) + csb_flags |= csb_sub_stream; } inline void CompilerScratch::csb_repeat::deactivate() @@ -664,6 +671,9 @@ public: // must correspond to the declared size of RDB$EXCEPTIONS.RDB$MESSAGE const unsigned XCP_MESSAGE_LENGTH = 1023; +// Array which stores relative pointers to impure areas of invariant nodes +typedef Firebird::SortedArray VarInvariantArray; + } // namespace Jrd #endif // JRD_EXE_H diff --git a/src/jrd/exe_proto.h b/src/jrd/exe_proto.h index ea3dedcc2e..e644e97bfe 100644 --- a/src/jrd/exe_proto.h +++ b/src/jrd/exe_proto.h @@ -27,7 +27,7 @@ #include "../jrd/cmp_proto.h" namespace Jrd { - class jrd_req; + class Request; class jrd_tra; class AssignmentNode; } @@ -40,17 +40,17 @@ void EXE_assignment(Jrd::thread_db* tdbb, const Jrd::ValueExprNode* to, dsc* fro void EXE_execute_db_triggers(Jrd::thread_db*, Jrd::jrd_tra*, enum TriggerAction); void EXE_execute_ddl_triggers(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, bool preTriggers, int action); -const Jrd::StmtNode* EXE_looper(Jrd::thread_db* tdbb, Jrd::jrd_req* request, +const Jrd::StmtNode* EXE_looper(Jrd::thread_db* tdbb, Jrd::Request* request, const Jrd::StmtNode* in_node); void EXE_execute_triggers(Jrd::thread_db*, Jrd::TrigVector**, Jrd::record_param*, Jrd::record_param*, enum TriggerAction, Jrd::StmtNode::WhichTrigger); -void EXE_receive(Jrd::thread_db*, Jrd::jrd_req*, USHORT, ULONG, void*, bool = false); -void EXE_release(Jrd::thread_db*, Jrd::jrd_req*); -void EXE_send(Jrd::thread_db*, Jrd::jrd_req*, USHORT, ULONG, const void*); -void EXE_start(Jrd::thread_db*, Jrd::jrd_req*, Jrd::jrd_tra*); -void EXE_unwind(Jrd::thread_db*, Jrd::jrd_req*); +void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false); +void EXE_release(Jrd::thread_db*, Jrd::Request*); +void EXE_send(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, const void*); +void EXE_start(Jrd::thread_db*, Jrd::Request*, Jrd::jrd_tra*); +void EXE_unwind(Jrd::thread_db*, Jrd::Request*); namespace Jrd { @@ -93,16 +93,16 @@ namespace Jrd if (request) return; - request = CMP_compile2(tdbb, blr, blrLength, true); + request = CMP_compile_request(tdbb, blr, blrLength, true); cacheRequest(); } - jrd_req* operator ->() + Request* operator ->() { return request; } - operator jrd_req*() + operator Request*() { return request; } @@ -139,7 +139,7 @@ namespace Jrd private: USHORT id; USHORT which; - jrd_req* request; + Request* request; }; class AutoRequest @@ -166,15 +166,15 @@ namespace Jrd if (request) return; - request = CMP_compile2(tdbb, blr, blrLength, true); + request = CMP_compile_request(tdbb, blr, blrLength, true); } - jrd_req* operator ->() + Request* operator ->() { return request; } - operator jrd_req*() + operator Request*() { return request; } @@ -195,7 +195,7 @@ namespace Jrd } private: - jrd_req* request; + Request* request; }; } diff --git a/src/jrd/ext.cpp b/src/jrd/ext.cpp index 66b1fbf930..b3e588cbf0 100644 --- a/src/jrd/ext.cpp +++ b/src/jrd/ext.cpp @@ -42,7 +42,6 @@ #include "../jrd/req.h" #include "../jrd/val.h" #include "../jrd/exe.h" -#include "../jrd/rse.h" #include "../jrd/ext.h" #include "../jrd/tra.h" #include "../dsql/ExprNodes.h" diff --git a/src/jrd/ext_proto.h b/src/jrd/ext_proto.h index b58ee5c616..b0ac772187 100644 --- a/src/jrd/ext_proto.h +++ b/src/jrd/ext_proto.h @@ -29,7 +29,6 @@ namespace Jrd { class jrd_tra; class RecordSource; class jrd_rel; - class OptimizerBlk; struct record_param; struct bid; } diff --git a/src/jrd/extds/ExtDS.cpp b/src/jrd/extds/ExtDS.cpp index a88f5b14d2..7180d19d4a 100644 --- a/src/jrd/extds/ExtDS.cpp +++ b/src/jrd/extds/ExtDS.cpp @@ -2249,7 +2249,7 @@ void Statement::doSetInParams(thread_db* tdbb, unsigned int count, const MetaStr const NestConst* jrdVar = params; GenericMap > > paramDescs(getPool()); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); for (FB_SIZE_T i = 0; i < count; ++i, ++jrdVar) { @@ -2364,7 +2364,7 @@ void Statement::getExtBlob(thread_db* tdbb, const dsc& src, dsc& dst) { extBlob->open(tdbb, *m_transaction, src, NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); const UCHAR bpb[] = {isc_bpb_version1, isc_bpb_storage, 1, isc_bpb_storage_temp}; bid* localBlobID = (bid*) dst.dsc_address; destBlob = blb::create2(tdbb, request->req_transaction, localBlobID, sizeof(bpb), bpb); @@ -2408,7 +2408,7 @@ void Statement::putExtBlob(thread_db* tdbb, dsc& src, dsc& dst) { extBlob->create(tdbb, *m_transaction, dst, NULL); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); bid* srcBid = (bid*) src.dsc_address; UCharBuffer bpb; @@ -2478,7 +2478,7 @@ void Statement::raise(FbStatusVector* status, thread_db* tdbb, const char* sWher Arg::Str(m_connection.getDataSourceName())); } -void Statement::bindToRequest(jrd_req* request, Statement** impure) +void Statement::bindToRequest(Request* request, Statement** impure) { fb_assert(!m_boundReq); fb_assert(!m_prevInReq); diff --git a/src/jrd/extds/ExtDS.h b/src/jrd/extds/ExtDS.h index 0ed2120165..dad7527d5e 100644 --- a/src/jrd/extds/ExtDS.h +++ b/src/jrd/extds/ExtDS.h @@ -315,7 +315,7 @@ public: }; private: - class IdleTimer FB_FINAL : + class IdleTimer final : public Firebird::RefCntIface > { public: @@ -631,7 +631,7 @@ public: const Firebird::string* sQuery = NULL); // Active statement must be bound to parent jrd request - void bindToRequest(Jrd::jrd_req* request, Statement** impure); + void bindToRequest(Jrd::Request* request, Statement** impure); void unBindFromRequest(); protected: @@ -666,7 +666,7 @@ protected: Statement* m_nextFree; // next free statement - Jrd::jrd_req* m_boundReq; + Jrd::Request* m_boundReq; Statement** m_ReqImpure; Statement* m_nextInReq; Statement* m_prevInReq; @@ -693,7 +693,7 @@ protected: unsigned int m_outputs; bool m_callerPrivileges; - Jrd::jrd_req* m_preparedByReq; + Jrd::Request* m_preparedByReq; // set in preprocess Firebird::SortedObjectsArray m_sqlParamNames; diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 2f92d71725..54bce85cf1 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -126,24 +126,6 @@ InternalConnection::~InternalConnection() { } -// Status helper -class IntStatus : public Firebird::FbLocalStatus -{ -public: - explicit IntStatus(FbStatusVector *p) - : FbLocalStatus(), v(p) - {} - - ~IntStatus() - { - if (v) - fb_utils::copyStatus(v, &(*this)); - } - -private: - FbStatusVector *v; -}; - void InternalConnection::attach(thread_db* tdbb) { fb_assert(!m_attachment); @@ -344,10 +326,9 @@ void InternalTransaction::doStart(FbStatusVector* status, thread_db* tdbb, Clump JAttachment* att = m_IntConnection.getJrdAtt(); EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); - IntStatus s(status); m_transaction.assignRefNoIncr( - att->startTransaction(&s, tpb.getBufferLength(), tpb.getBuffer())); + att->startTransaction(status, tpb.getBufferLength(), tpb.getBuffer())); if (m_transaction) m_transaction->getHandle()->tra_callback_count = localTran->tra_callback_count; @@ -373,15 +354,13 @@ void InternalTransaction::doCommit(FbStatusVector* status, thread_db* tdbb, bool } else { - IntStatus s(status); - EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); if (retain) - m_transaction->commitRetaining(&s); + m_transaction->commitRetaining(status); else { - m_transaction->commit(&s); - if (!(s->getState() & IStatus::STATE_ERRORS)) + m_transaction->commit(status); + if (!(status->getState() & IStatus::STATE_ERRORS)) m_transaction.clear(); } } @@ -402,23 +381,40 @@ void InternalTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bo if (!retain) { m_transaction = NULL; // release and nullify } + return; } - else - { - IntStatus s(status); + ISC_STATUS err = 0; + { EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); if (retain) - m_transaction->rollbackRetaining(&s); + m_transaction->rollbackRetaining(status); else + m_transaction->rollback(status); + + if (status->getState() & IStatus::STATE_ERRORS) + err = status->getErrors()[1]; + + if (err == isc_cancelled) { - m_transaction->rollback(&s); - if (!(s->getState() & IStatus::STATE_ERRORS)) - m_transaction.clear(); + FbLocalStatus temp; + JAttachment* jAtt = m_IntConnection.getJrdAtt(); + jAtt->cancelOperation(&temp, fb_cancel_disable); + + status->init(); + if (retain) + m_transaction->rollbackRetaining(status); + else + m_transaction->rollback(status); + + err = (status->getState() & IStatus::STATE_ERRORS) ? + status->getErrors()[1] : 0; + + jAtt->cancelOperation(&temp, fb_cancel_enable); } } - if ((status->getErrors()[1] == isc_att_shutdown || status->getErrors()[1] == isc_shutdown) && !retain) + if ((!err || err == isc_att_shutdown || err == isc_shutdown) && !retain) { m_transaction.clear(); status->init(); @@ -463,8 +459,8 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (m_callerPrivileges) { - jrd_req* request = tdbb->getRequest(); - JrdStatement* statement = request ? request->getStatement() : NULL; + Request* request = tdbb->getRequest(); + auto statement = request ? request->getStatement() : NULL; CallerName callerName; const Routine* routine; @@ -508,13 +504,13 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) if (status->getState() & IStatus::STATE_ERRORS) raise(&status, tdbb, "JAttachment::prepare", &sql); - const DsqlCompiledStatement* statement = m_request->getHandle()->getStatement(); + const auto dsqlStatement = m_request->getHandle()->getDsqlStatement(); - if (statement->getSendMsg()) + if (dsqlStatement->getSendMsg()) { try { - PreparedStatement::parseDsqlMessage(statement->getSendMsg(), m_inDescs, + PreparedStatement::parseDsqlMessage(dsqlStatement->getSendMsg(), m_inDescs, m_inMetadata, m_in_buffer); m_inputs = m_inMetadata->getCount(); } @@ -526,11 +522,11 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) else m_inputs = 0; - if (statement->getReceiveMsg()) + if (dsqlStatement->getReceiveMsg()) { try { - PreparedStatement::parseDsqlMessage(statement->getReceiveMsg(), m_outDescs, + PreparedStatement::parseDsqlMessage(dsqlStatement->getReceiveMsg(), m_outDescs, m_outMetadata, m_out_buffer); m_outputs = m_outMetadata->getCount(); } @@ -544,35 +540,35 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) m_stmt_selectable = false; - switch (statement->getType()) + switch (dsqlStatement->getType()) { - case DsqlCompiledStatement::TYPE_SELECT: - case DsqlCompiledStatement::TYPE_RETURNING_CURSOR: - case DsqlCompiledStatement::TYPE_SELECT_UPD: - case DsqlCompiledStatement::TYPE_SELECT_BLOCK: + case DsqlStatement::TYPE_SELECT: + case DsqlStatement::TYPE_RETURNING_CURSOR: + case DsqlStatement::TYPE_SELECT_UPD: + case DsqlStatement::TYPE_SELECT_BLOCK: m_stmt_selectable = true; break; - case DsqlCompiledStatement::TYPE_START_TRANS: - case DsqlCompiledStatement::TYPE_COMMIT: - case DsqlCompiledStatement::TYPE_ROLLBACK: - case DsqlCompiledStatement::TYPE_COMMIT_RETAIN: - case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN: - case DsqlCompiledStatement::TYPE_CREATE_DB: + case DsqlStatement::TYPE_START_TRANS: + case DsqlStatement::TYPE_COMMIT: + case DsqlStatement::TYPE_ROLLBACK: + case DsqlStatement::TYPE_COMMIT_RETAIN: + case DsqlStatement::TYPE_ROLLBACK_RETAIN: + case DsqlStatement::TYPE_CREATE_DB: Arg::Gds(isc_eds_expl_tran_ctrl).copyTo(&status); raise(&status, tdbb, "JAttachment::prepare", &sql); break; - case DsqlCompiledStatement::TYPE_INSERT: - case DsqlCompiledStatement::TYPE_DELETE: - case DsqlCompiledStatement::TYPE_UPDATE: - case DsqlCompiledStatement::TYPE_UPDATE_CURSOR: - case DsqlCompiledStatement::TYPE_DELETE_CURSOR: - case DsqlCompiledStatement::TYPE_DDL: - case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE: - case DsqlCompiledStatement::TYPE_SET_GENERATOR: - case DsqlCompiledStatement::TYPE_SAVEPOINT: - case DsqlCompiledStatement::TYPE_EXEC_BLOCK: + case DsqlStatement::TYPE_INSERT: + case DsqlStatement::TYPE_DELETE: + case DsqlStatement::TYPE_UPDATE: + case DsqlStatement::TYPE_UPDATE_CURSOR: + case DsqlStatement::TYPE_DELETE_CURSOR: + case DsqlStatement::TYPE_DDL: + case DsqlStatement::TYPE_EXEC_PROCEDURE: + case DsqlStatement::TYPE_SET_GENERATOR: + case DsqlStatement::TYPE_SAVEPOINT: + case DsqlStatement::TYPE_EXEC_BLOCK: break; } } diff --git a/src/jrd/extds/IscDS.cpp b/src/jrd/extds/IscDS.cpp index 10ab7f5c70..167428b2c6 100644 --- a/src/jrd/extds/IscDS.cpp +++ b/src/jrd/extds/IscDS.cpp @@ -396,6 +396,22 @@ void IscTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bool re else m_iscProvider.isc_rollback_transaction(status, &m_handle); + if ((status->getState() & IStatus::STATE_ERRORS) && + (status->getErrors()[1] == isc_cancelled)) + { + FbLocalStatus temp; + FB_API_HANDLE db = m_iscConnection.getAPIHandle(); + m_iscProvider.fb_cancel_operation(&temp, &db, fb_cancel_disable); + + status->init(); + if (retain) + m_iscProvider.isc_rollback_retaining(status, &m_handle); + else + m_iscProvider.isc_rollback_transaction(status, &m_handle); + + m_iscProvider.fb_cancel_operation(&temp, &db, fb_cancel_enable); + } + if ((status->getState() & IStatus::STATE_ERRORS) && isConnectionBrokenError(status) && !retain) { diff --git a/src/jrd/extds/ValidatePassword.cpp b/src/jrd/extds/ValidatePassword.cpp index 4212f8ace4..5f12bc61aa 100644 --- a/src/jrd/extds/ValidatePassword.cpp +++ b/src/jrd/extds/ValidatePassword.cpp @@ -39,7 +39,7 @@ using namespace Firebird; namespace { -class DummyCryptKey FB_FINAL : +class DummyCryptKey final : public Firebird::AutoIface > { public: @@ -66,7 +66,7 @@ public: class SBlock; -class CBlock FB_FINAL : public RefCntIface > +class CBlock final : public RefCntIface > { public: CBlock(const string& p_login, const string& p_password) @@ -119,7 +119,7 @@ private: SBlock* sBlock; }; -class SBlock FB_FINAL : public AutoIface > +class SBlock final : public AutoIface > { public: explicit SBlock(CBlock* par) diff --git a/src/jrd/filters.cpp b/src/jrd/filters.cpp index 964b56dc76..ba1a1343ac 100644 --- a/src/jrd/filters.cpp +++ b/src/jrd/filters.cpp @@ -407,7 +407,7 @@ ISC_STATUS filter_format(USHORT action, BlobControl* control) d = desc; d.dsc_address = pBuff; - DescPrinter val(JRD_get_thread_data(), &d, 32); + DescPrinter val(JRD_get_thread_data(), &d, 32, CS_dynamic); str.printf(fmt2, fieldId, diff --git a/src/jrd/flu.h b/src/jrd/flu.h index d9a8bf7c26..45880a24b1 100644 --- a/src/jrd/flu.h +++ b/src/jrd/flu.h @@ -90,10 +90,10 @@ namespace Jrd public: typedef Firebird::Array LoadedModules; - Module() : interMod(0) { } + Module() + { } explicit Module(MemoryPool&) - : interMod(NULL) { } Module(MemoryPool&, const Module& m) diff --git a/src/jrd/fun.epp b/src/jrd/fun.epp index 9c6522f35b..f7fd84c68d 100644 --- a/src/jrd/fun.epp +++ b/src/jrd/fun.epp @@ -359,7 +359,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra const Parameter* return_ptr = function->getOutputFields()[0]; - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); // CVC: restoring the null flag seems like a Borland hack to try to // patch a bug with null handling. There's no evident reason to restore it // because EVL_expr() resets it every time it's called. Kept it for now. diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index 6106c09872..aabe8453cf 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -71,7 +71,7 @@ DATABASE DB = STATIC "yachts.lnk"; static void define_default_class(thread_db*, const TEXT*, MetaName&, const Acl&, jrd_tra*); static void finish_security_class(Acl&, SecurityClass::flags_t); -static void get_object_info(thread_db*, const TEXT*, SSHORT, +static void get_object_info(thread_db*, const TEXT*, ObjectType, MetaName&, MetaName&, MetaName&, bool&); static SecurityClass::flags_t get_public_privs(thread_db*, const TEXT*, SSHORT); static void get_user_privs(thread_db*, Acl&, const TEXT*, SSHORT, const MetaName&, @@ -85,7 +85,7 @@ static SecurityClass::flags_t squeeze_acl(Acl&, const MetaName&, SSHORT); static bool check_string(const UCHAR*, const MetaName&); -void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, jrd_tra* transaction) +void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, ObjectType id, jrd_tra* transaction) { /************************************** * @@ -142,7 +142,7 @@ void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, break; default: - if (id >= obj_database && id < obj_type_MAX) + if (isDdlObject(id)) priv = OWNER_PRIVS; break; } @@ -314,7 +314,7 @@ static SecurityClass::flags_t get_public_privs(thread_db* tdbb, static void get_object_info(thread_db* tdbb, const TEXT* object_name, - SSHORT obj_type, + ObjectType obj_type, MetaName& owner, MetaName& s_class, MetaName& default_class, @@ -521,7 +521,7 @@ static void get_object_info(thread_db* tdbb, } else { - s_class = get_object_name(obj_type); + s_class = getSecurityClassName(obj_type); default_class = ""; owner = tdbb->getDatabase()->dbb_owner; view = false; diff --git a/src/jrd/grant_proto.h b/src/jrd/grant_proto.h index 3609fba2bd..b34db32028 100644 --- a/src/jrd/grant_proto.h +++ b/src/jrd/grant_proto.h @@ -25,12 +25,13 @@ #define JRD_GRANT_PROTO_H #include "../common/classes/fb_string.h" +#include "../jrd/obj.h" namespace Jrd { class DeferredWork; } -void GRANT_privileges(Jrd::thread_db*, const Firebird::string&, USHORT, Jrd::jrd_tra*); +void GRANT_privileges(Jrd::thread_db*, const Firebird::string&, ObjectType, Jrd::jrd_tra*); #endif // JRD_GRANT_PROTO_H diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 16d781ed0b..e066c48a47 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -45,7 +45,6 @@ #include "../jrd/exe.h" #include "../jrd/scl.h" #include "../jrd/lck.h" -#include "../jrd/rse.h" #include "../jrd/cch.h" #include "../common/gdsassert.h" #include "../jrd/btr_proto.h" @@ -168,11 +167,11 @@ void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_ CMP_post_access(tdbb, csb, referenced_relation->rel_security_name, (view ? view->rel_id : 0), - SCL_references, SCL_object_table, + SCL_references, obj_relations, referenced_relation->rel_name); CMP_post_access(tdbb, csb, referenced_field->fld_security_name, 0, - SCL_references, SCL_object_column, + SCL_references, obj_column, referenced_field->fld_name, referenced_relation->rel_name); } @@ -406,7 +405,8 @@ void IDX_create_index(thread_db* tdbb, { Record* record = stack.pop(); - result = BTR_key(tdbb, relation, record, idx, &key, false); + result = BTR_key(tdbb, relation, record, idx, &key, + ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); if (result == idx_e_ok) { @@ -749,7 +749,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec1 = stack1.object(); - idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, false); + idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + if (result != idx_e_ok) { if (result == idx_e_conversion) @@ -766,7 +768,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec2 = stack2.object(); - result = BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, false); + result = BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + if (result != idx_e_ok) { if (result == idx_e_conversion) @@ -789,7 +793,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec3 = stack3.object(); - result = BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, false); + result = BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + if (result != idx_e_ok) { if (result == idx_e_conversion) @@ -861,14 +867,16 @@ void IDX_modify(thread_db* tdbb, idx_e error_code; if ((error_code = BTR_key(tdbb, new_rpb->rpb_relation, - new_rpb->rpb_record, &idx, &key1, false))) + new_rpb->rpb_record, &idx, &key1, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, new_rpb->rpb_record); } if ((error_code = BTR_key(tdbb, org_rpb->rpb_relation, - org_rpb->rpb_record, &idx, &key2, false))) + org_rpb->rpb_record, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, org_rpb->rpb_record); @@ -935,14 +943,16 @@ void IDX_modify_check_constraints(thread_db* tdbb, idx_e error_code; if ((error_code = BTR_key(tdbb, new_rpb->rpb_relation, - new_rpb->rpb_record, &idx, &key1, false))) + new_rpb->rpb_record, &idx, &key1, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, new_rpb->rpb_record); } if ((error_code = BTR_key(tdbb, org_rpb->rpb_relation, - org_rpb->rpb_record, &idx, &key2, false))) + org_rpb->rpb_record, &idx, &key2, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, org_rpb->rpb_record); @@ -1081,7 +1091,8 @@ void IDX_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) IndexErrorContext context(rpb->rpb_relation, &idx); idx_e error_code; - if ( (error_code = BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, false)) ) + if ((error_code = BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, + ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)))) { CCH_RELEASE(tdbb, &window); context.raise(tdbb, error_code, rpb->rpb_record); @@ -1430,7 +1441,12 @@ static idx_e check_partner_index(thread_db* tdbb, // tmpIndex.idx_flags |= idx_unique; tmpIndex.idx_flags = (tmpIndex.idx_flags & ~idx_unique) | (partner_idx.idx_flags & idx_unique); temporary_key key; - result = BTR_key(tdbb, relation, record, &tmpIndex, &key, starting, segment); + + const USHORT keyType = starting ? + INTL_KEY_PARTIAL : + (tmpIndex.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT; + + result = BTR_key(tdbb, relation, record, &tmpIndex, &key, keyType, segment); CCH_RELEASE(tdbb, &window); // now check for current duplicates diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index e8c6804a81..96ed00b384 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -54,7 +54,6 @@ #include "../jrd/cvt_proto.h" #include "../jrd/inf_proto.h" #include "../common/isc_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/os/pio_proto.h" #include "../jrd/tra_proto.h" @@ -1009,7 +1008,7 @@ UCHAR* INF_put_item(UCHAR item, } -ULONG INF_request_info(const jrd_req* request, +ULONG INF_request_info(const Request* request, const ULONG item_length, const UCHAR* items, const ULONG output_length, @@ -1091,9 +1090,9 @@ ULONG INF_request_info(const jrd_req* request, else { auto state = isc_info_req_active; - if (request->req_operation == jrd_req::req_send) + if (request->req_operation == Request::req_send) state = isc_info_req_send; - else if (request->req_operation == jrd_req::req_receive) + else if (request->req_operation == Request::req_receive) { const StmtNode* node = request->req_next; @@ -1102,7 +1101,7 @@ ULONG INF_request_info(const jrd_req* request, else state = isc_info_req_receive; } - else if ((request->req_operation == jrd_req::req_return) && + else if ((request->req_operation == Request::req_return) && (request->req_flags & req_stall)) { state = isc_info_req_sql_stall; @@ -1114,8 +1113,8 @@ ULONG INF_request_info(const jrd_req* request, case isc_info_message_number: case isc_info_message_size: if (!(request->req_flags & req_active) || - (request->req_operation != jrd_req::req_receive && - request->req_operation != jrd_req::req_send)) + (request->req_operation != Request::req_receive && + request->req_operation != Request::req_send)) { buffer_ptr[0] = item; item = isc_info_error; diff --git a/src/jrd/inf_proto.h b/src/jrd/inf_proto.h index d19f5b9a0c..2478689bfc 100644 --- a/src/jrd/inf_proto.h +++ b/src/jrd/inf_proto.h @@ -25,7 +25,7 @@ #define JRD_INF_PROTO_H namespace Jrd { - class jrd_req; + class Request; class jrd_tra; class blb; } @@ -34,7 +34,7 @@ void INF_blob_info(const Jrd::blb*, const ULONG, const UCHAR*, const ULONG, UCHA USHORT INF_convert(SINT64, UCHAR*); void INF_database_info(Jrd::thread_db*, const ULONG, const UCHAR*, const ULONG, UCHAR*); UCHAR* INF_put_item(UCHAR, ULONG, const void*, UCHAR*, const UCHAR*, const bool inserting = false); -ULONG INF_request_info(const Jrd::jrd_req*, const ULONG, const UCHAR*, const ULONG, UCHAR*); +ULONG INF_request_info(const Jrd::Request*, const ULONG, const UCHAR*, const ULONG, UCHAR*); void INF_transaction_info(const Jrd::jrd_tra*, const ULONG, const UCHAR*, const ULONG, UCHAR*); #endif // JRD_INF_PROTO_H diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 2054ad85f5..da745683b5 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -524,9 +524,10 @@ void INI_format(const char* owner, const char* charset) length = acl - buffer; - for (int ddl_obj = obj_database + 1; ddl_obj < obj_type_MAX; ++ddl_obj) + for (int obj = obj_database + 1; obj < obj_type_MAX; obj++) { - add_security_class(tdbb, reqAddSC, get_object_name(ddl_obj), length, buffer); + if (isDdlObject(obj)) + add_security_class(tdbb, reqAddSC, getSecurityClassName(obj), length, buffer); } { // scope diff --git a/src/jrd/intl.cpp b/src/jrd/intl.cpp index 530531dbdd..f03f3773e6 100644 --- a/src/jrd/intl.cpp +++ b/src/jrd/intl.cpp @@ -133,7 +133,7 @@ using namespace Firebird; static bool allSpaces(CharSet*, const BYTE*, ULONG, ULONG); static int blocking_ast_collation(void* ast_object); static void pad_spaces(thread_db*, CHARSET_ID, BYTE *, ULONG); -static INTL_BOOL lookup_texttype(texttype* tt, const SubtypeInfo* info); +static void lookup_texttype(texttype* tt, const SubtypeInfo* info); static GlobalPtr createCollationMtx; @@ -387,15 +387,10 @@ Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id) } Attachment* const att = tdbb->getAttachment(); - texttype* tt = FB_NEW_POOL(*att->att_pool) texttype; + AutoPtr tt(FB_NEW_POOL(*att->att_pool) texttype); memset(tt, 0, sizeof(texttype)); - if (!lookup_texttype(tt, &info)) - { - delete tt; - ERR_post(Arg::Gds(isc_collation_not_installed) << Arg::Str(info.collationName) << - Arg::Str(info.charsetName)); - } + lookup_texttype(tt, &info); if (charset_collations.getCount() <= id) charset_collations.grow(id + 1); @@ -415,9 +410,12 @@ Collation* CharSetContainer::lookupCollation(thread_db* tdbb, USHORT tt_id) } } - charset_collations[id] = Collation::createInstance(*att->att_pool, tt_id, tt, info.attributes, charset); + charset_collations[id] = Collation::createInstance(*att->att_pool, tt_id, + tt, info.attributes, charset); charset_collations[id]->name = info.collationName; + tt.release(); + // we don't need a lock in the charset if (id != 0) { @@ -489,9 +487,9 @@ void CharSetContainer::unloadCollation(thread_db* tdbb, USHORT tt_id) } -static INTL_BOOL lookup_texttype(texttype* tt, const SubtypeInfo* info) +static void lookup_texttype(texttype* tt, const SubtypeInfo* info) { - return IntlManager::lookupCollation(info->baseCollationName.c_str(), info->charsetName.c_str(), + IntlManager::lookupCollation(info->baseCollationName.c_str(), info->charsetName.c_str(), info->attributes, info->specificAttributes.begin(), info->specificAttributes.getCount(), info->ignoreAttributes, tt); } @@ -1170,12 +1168,19 @@ bool INTL_texttype_validate(Jrd::thread_db* tdbb, const SubtypeInfo* info) texttype tt; memset(&tt, 0, sizeof(tt)); - bool ret = lookup_texttype(&tt, info); + try + { + lookup_texttype(&tt, info); - if (ret && tt.texttype_fn_destroy) - tt.texttype_fn_destroy(&tt); + if (tt.texttype_fn_destroy) + tt.texttype_fn_destroy(&tt); - return ret; + return true; + } + catch (const Exception&) + { + return false; + } } @@ -1272,6 +1277,7 @@ USHORT INTL_string_to_key(thread_db* tdbb, case ttype_binary: case ttype_ascii: case ttype_none: + fb_assert(key_type != INTL_KEY_MULTI_STARTING); while (len-- && destLen-- > 0) *dest++ = *src++; // strip off ending pad characters @@ -1286,6 +1292,7 @@ USHORT INTL_string_to_key(thread_db* tdbb, break; default: TextType* obj = INTL_texttype_lookup(tdbb, ttype); + fb_assert(key_type != INTL_KEY_MULTI_STARTING || (obj->getFlags() & TEXTTYPE_MULTI_STARTING_KEY)); outlen = obj->string_to_key(len, src, pByte->dsc_length, dest, key_type); break; } diff --git a/src/jrd/intl_builtin.cpp b/src/jrd/intl_builtin.cpp index da94a9ac10..4eefd9943f 100644 --- a/src/jrd/intl_builtin.cpp +++ b/src/jrd/intl_builtin.cpp @@ -1658,10 +1658,12 @@ INTL_BOOL INTL_builtin_lookup_charset(charset* cs, const ASCII* charset_name, co } -INTL_BOOL INTL_builtin_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info) +INTL_BOOL INTL_builtin_lookup_texttype_status( + char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info) { if (ignore_attributes) { @@ -1700,8 +1702,29 @@ INTL_BOOL INTL_builtin_lookup_texttype(texttype* tt, const ASCII* texttype_name, if (func) { - return func(tt, texttype_name, charset_name, attributes, - specific_attributes, specific_attributes_length, ignore_attributes, config_info); + Firebird::string errorMsg; + + try + { + return func(tt, texttype_name, charset_name, attributes, + specific_attributes, specific_attributes_length, ignore_attributes, config_info); + } + catch (const Firebird::status_exception& ex) + { + auto status = ex.value(); + TEXT temp[BUFFER_LARGE]; + + while (fb_interpret(temp, sizeof(temp), &status)) + { + if (errorMsg.hasData()) + errorMsg += "\n-"; + + errorMsg += temp; + } + + if (status_buffer_length) + fb_utils::copy_terminate(status_buffer, errorMsg.c_str(), status_buffer_length); + } } return false; diff --git a/src/jrd/intl_proto.h b/src/jrd/intl_proto.h index 58c3b479f8..add4b4af78 100644 --- a/src/jrd/intl_proto.h +++ b/src/jrd/intl_proto.h @@ -56,10 +56,12 @@ USHORT INTL_string_to_key(Jrd::thread_db*, USHORT, const dsc*, dsc*, USHORT); // Built-in charsets/texttypes interface INTL_BOOL INTL_builtin_lookup_charset(charset* cs, const ASCII* charset_name, const ASCII* config_info); -INTL_BOOL INTL_builtin_lookup_texttype(texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, - USHORT attributes, const UCHAR* specific_attributes, - ULONG specific_attributes_length, INTL_BOOL ignore_attributes, - const ASCII* config_info); +INTL_BOOL INTL_builtin_lookup_texttype_status( + char* status_buffer, ULONG status_buffer_length, + texttype* tt, const ASCII* texttype_name, const ASCII* charset_name, + USHORT attributes, const UCHAR* specific_attributes, + ULONG specific_attributes_length, INTL_BOOL ignore_attributes, + const ASCII* config_info); ULONG INTL_builtin_setup_attributes(const ASCII* textTypeName, const ASCII* charSetName, const ASCII* configInfo, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 77eeabb577..e16361bc9b 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -63,7 +63,6 @@ #include "../jrd/exe.h" #include "../jrd/extds/ExtDS.h" #include "../jrd/val.h" -#include "../jrd/rse.h" #include "../jrd/intl.h" #include "../jrd/sbm.h" #include "../jrd/svc.h" @@ -94,7 +93,6 @@ #include "../jrd/lck_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/par_proto.h" #include "../jrd/os/pio_proto.h" @@ -136,6 +134,7 @@ #include "../dsql/dsql.h" #include "../dsql/dsql_proto.h" #include "../dsql/DsqlBatch.h" +#include "../dsql/DsqlStatementCache.h" #ifdef WIN_NT #include @@ -335,7 +334,7 @@ JResultSet::JResultSet(DsqlCursor* handle, JStatement* aStatement) { } -JRequest::JRequest(JrdStatement* handle, StableAttachmentPart* sa) +JRequest::JRequest(Statement* handle, StableAttachmentPart* sa) : rq(handle), sAtt(sa) { } @@ -345,7 +344,7 @@ JEvents::JEvents(int aId, StableAttachmentPart* sa, Firebird::IEventCallback* aC { } -JStatement::JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array& meta) +JStatement::JStatement(DsqlRequest* handle, StableAttachmentPart* sa, Firebird::Array& meta) : statement(handle), sAtt(sa), metadata(getPool(), this, sAtt) { metadata.parse(meta.getCount(), meta.begin()); @@ -432,7 +431,7 @@ public: { if (getUnloadDetector()->unloadStarted()) { - Arg::Gds(isc_shutdown).raise(); + Arg::Gds(isc_att_shut_engine).raise(); } IPluginBase* p = FB_NEW JProvider(factoryParameter); @@ -465,7 +464,7 @@ void registerEngine(IPluginManager* iPlugin) } // namespace Jrd -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { CachedMasterInterface::set(master); registerEngine(PluginManagerInterfacePtr()); @@ -649,7 +648,7 @@ namespace tdbb->setTransaction(transaction); } - inline void validateHandle(thread_db* tdbb, JrdStatement* const statement) + inline void validateHandle(thread_db* tdbb, Statement* const statement) { if (!statement) status_exception::raise(Arg::Gds(isc_bad_req_handle)); @@ -657,7 +656,7 @@ namespace validateHandle(tdbb, statement->requests[0]->req_attachment); } - inline void validateHandle(thread_db* tdbb, dsql_req* const statement) + inline void validateHandle(thread_db* tdbb, DsqlRequest* const statement) { if (!statement) status_exception::raise(Arg::Gds(isc_bad_req_handle)); @@ -944,10 +943,10 @@ void Trigger::compile(thread_db* tdbb) statement->triggerInvoker = att->getUserId(owner); if (sysTrigger) - statement->flags |= JrdStatement::FLAG_SYS_TRIGGER; + statement->flags |= Statement::FLAG_SYS_TRIGGER; if (flags & TRG_ignore_perm) - statement->flags |= JrdStatement::FLAG_IGNORE_PERM; + statement->flags |= Statement::FLAG_IGNORE_PERM; } } @@ -1085,6 +1084,7 @@ namespace Jrd ULONG dpb_remote_flags; ReplicaMode dpb_replica_mode; bool dpb_set_db_replica; + bool dpb_clear_map; // here begin compound objects // for constructor to work properly dpb_user_name @@ -1312,7 +1312,7 @@ static bool drop_files(const jrd_file*); static void find_intl_charset(thread_db*, Jrd::Attachment*, const DatabaseOptions*); static void init_database_lock(thread_db*); static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction); -static jrd_req* verify_request_synchronization(JrdStatement* statement, USHORT level); +static Request* verify_request_synchronization(Statement* statement, USHORT level); static void purge_transactions(thread_db*, Jrd::Attachment*, const bool); static void check_single_maintenance(thread_db* tdbb); @@ -1332,7 +1332,7 @@ static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tr static void rollback(thread_db*, jrd_tra*, const bool); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); static void getUserInfo(UserId&, const DatabaseOptions&, const char*, - const RefPtr*, bool, Mapping& mapping); + const RefPtr*, bool, Mapping& mapping, bool); static void waitForShutdown(Semaphore&); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1348,7 +1348,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas { Mapping mapping(Mapping::MAP_ERROR_HANDLER, NULL); mapping.setAuthBlock(m_options->dpb_auth_block); - getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping); + getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping, false); } @@ -1358,7 +1358,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas // do it here to prevent committing every record update // in a statement // -static void check_autocommit(thread_db* tdbb, jrd_req* request) +static void check_autocommit(thread_db* tdbb, Request* request) { jrd_tra* const transaction = request->req_transaction; @@ -1931,6 +1931,12 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch break; } + // Clear old mapping cache data on request. + // Unfortunately have to do it w/o access rights check - to check access rights engine + // needs correct mapping which sometimes can't be guaranteed before cleaning cache. + if (options.dpb_clear_map) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + // Check for correct credentials supplied UserId userId; @@ -1942,7 +1948,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch try { mapping.setDb(filename, expanded_name.c_str(), jAtt); - getUserInfo(userId, options, filename, &config, false, mapping); + getUserInfo(userId, options, filename, &config, false, mapping, options.dpb_reset_icu); } catch(const Exception&) { @@ -1954,7 +1960,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch } userId.makeRoleName(options.dpb_sql_dialect); - UserId::sclInit(tdbb, false, userId); + userId.sclInit(tdbb, false); Monitoring::publishAttachment(tdbb); @@ -2026,11 +2032,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (!allow_access) { // Note we throw exception here when entering full-shutdown mode - Arg::Gds v(isc_shutdown); - v << Arg::Str(org_filename); - if (attachment->att_user && attachment->att_user->testFlag(USR_mapdown)) - v << Arg::Gds(isc_map_down); - ERR_post(v); + ERR_post(Arg::Gds(isc_shutdown) << org_filename); } } @@ -2067,6 +2069,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch // Can't allow garbage collection during database validation. + AutoSetRestoreFlag noCleanup(&attachment->att_flags, ATT_no_cleanup, true); VIO_fini(tdbb); if (!VAL_validate(tdbb, options.dpb_verify)) @@ -2077,6 +2080,10 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch { validateAccess(tdbb, attachment, USE_GFIX_UTILITY); DFW_reset_icu(tdbb); + + // force system privileges recheck for sysdba + fb_assert(attachment->att_user); // set by UserId::sclInit() + attachment->att_user->setFlag(USR_newrole); } if (options.dpb_journal.hasData()) @@ -2676,7 +2683,7 @@ JRequest* JAttachment::compileRequest(CheckStatusWrapper* user_status, * Functional description * **************************************/ - JrdStatement* stmt = NULL; + Statement* stmt = NULL; try { @@ -2686,11 +2693,14 @@ JRequest* JAttachment::compileRequest(CheckStatusWrapper* user_status, TraceBlrCompile trace(tdbb, blr_length, blr); try { - jrd_req* request = NULL; - JRD_compile(tdbb, getHandle(), &request, blr_length, blr, RefStrPtr(), 0, NULL, false); - stmt = request->getStatement(); + stmt = CMP_compile(tdbb, blr, blr_length, false, 0, nullptr); - trace.finish(request, ITracePlugin::RESULT_SUCCESS); + const auto attachment = tdbb->getAttachment(); + const auto rootRequest = stmt->getRequest(tdbb, 0); + rootRequest->setAttachment(attachment); + attachment->att_requests.add(rootRequest); + + trace.finish(stmt, ITracePlugin::RESULT_SUCCESS); } catch (const Exception& ex) { @@ -2833,7 +2843,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch // Check for correct credentials supplied mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr); - getUserInfo(userId, options, filename, &config, true, mapping); + getUserInfo(userId, options, filename, &config, true, mapping, false); #ifdef WIN_NT guardDbInit.enter(); // Required to correctly expand name of just created database @@ -3029,7 +3039,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch INI_init(tdbb); PAG_init(tdbb); - UserId::sclInit(tdbb, true, userId); + userId.sclInit(tdbb, true); if (options.dpb_set_page_buffers) dbb->dbb_page_buffers = options.dpb_page_buffers; @@ -3048,6 +3058,9 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch else dbb->dbb_database_name = dbb->dbb_filename; + // Clear old mapping cache data (if present) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + // Initialize backup difference subsystem. This must be done before WAL and shadowing // is enabled because nbackup it is a lower level subsystem dbb->dbb_backup_manager = FB_NEW_POOL(*dbb->dbb_permanent) BackupManager(tdbb, @@ -3300,15 +3313,15 @@ void JAttachment::freeEngineData(CheckStatusWrapper* user_status, bool forceFree unsigned flags = PURGE_LINGER; - if (engineShutdown || + if (engineShutdown) + flags |= PURGE_FORCE; + + if (forceFree || (dbb->dbb_ast_flags & DBB_shutdown) || (attachment->att_flags & ATT_shutdown)) { - flags |= PURGE_FORCE; - } - - if (forceFree) flags |= PURGE_NOCHECK; + } ISC_STATUS reason = 0; if (!forceFree) @@ -3869,7 +3882,7 @@ void JRequest::receive(CheckStatusWrapper* user_status, int level, unsigned int EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -4006,7 +4019,7 @@ void JRequest::getInfo(CheckStatusWrapper* user_status, int level, unsigned int EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -4209,7 +4222,7 @@ void JRequest::send(CheckStatusWrapper* user_status, int level, unsigned int msg EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -4436,7 +4449,7 @@ void JRequest::startAndSend(CheckStatusWrapper* user_status, ITransaction* tra, validateHandle(tdbb, transaction); check_database(tdbb); - jrd_req* request = getHandle()->getRequest(tdbb, level); + Request* request = getHandle()->getRequest(tdbb, level); try { @@ -4494,7 +4507,7 @@ void JRequest::start(CheckStatusWrapper* user_status, ITransaction* tra, int lev validateHandle(tdbb, transaction); check_database(tdbb); - jrd_req* request = getHandle()->getRequest(tdbb, level); + Request* request = getHandle()->getRequest(tdbb, level); try { @@ -4692,7 +4705,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* const MessageNode* inMessage = NULL; const MessageNode* outMessage = NULL; - jrd_req* request = NULL; + Request* request = NULL; MemoryPool* new_pool = att->createPool(); try @@ -4702,14 +4715,9 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* CompilerScratch* csb = PAR_parse(tdbb, reinterpret_cast(blr), blr_length, false); - request = JrdStatement::makeRequest(tdbb, csb, false); - request->getStatement()->verifyAccess(tdbb); - for (FB_SIZE_T i = 0; i < csb->csb_rpt.getCount(); i++) { - - const MessageNode* node = csb->csb_rpt[i].csb_message; - if (node) + if (const auto node = csb->csb_rpt[i].csb_message) { if (node->messageNumber == 0) inMessage = node; @@ -4717,6 +4725,9 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* outMessage = node; } } + + request = Statement::makeRequest(tdbb, csb, false); + request->getStatement()->verifyAccess(tdbb); } catch (const Exception&) { @@ -4912,7 +4923,7 @@ void JRequest::unwind(CheckStatusWrapper* user_status, int level) EngineContextHolder tdbb(user_status, this, FB_FUNCTION); check_database(tdbb); - jrd_req* request = verify_request_synchronization(getHandle(), level); + Request* request = verify_request_synchronization(getHandle(), level); try { @@ -5056,7 +5067,7 @@ JResultSet* JStatement::openCursor(CheckStatusWrapper* user_status, ITransaction } } - DsqlCursor* const cursor = DSQL_open(tdbb, &tra, getHandle(), + const auto cursor = getHandle()->openCursor(tdbb, &tra, inMetadata, static_cast(inBuffer), outMetadata, flags); rs = FB_NEW JResultSet(cursor, this); @@ -5560,7 +5571,7 @@ JStatement* JAttachment::prepare(CheckStatusWrapper* user_status, ITransaction* validateHandle(tdbb, tra); check_database(tdbb); - dsql_req* statement = NULL; + DsqlRequest* statement = NULL; try { @@ -5832,7 +5843,7 @@ void JResultSet::setDelayedOutputFormat(CheckStatusWrapper* user_status, Firebir try { - dsql_req* req = statement->getHandle(); + DsqlRequest* req = statement->getHandle(); fb_assert(req); req->setDelayedFormat(tdbb, outMetadata); } @@ -5892,7 +5903,7 @@ unsigned int JStatement::getTimeout(CheckStatusWrapper* user_status) try { - Jrd::dsql_req* req = getHandle(); + Jrd::DsqlRequest* req = getHandle(); return req->getTimeout(); } catch (const Exception& ex) @@ -5922,7 +5933,7 @@ void JStatement::setTimeout(CheckStatusWrapper* user_status, unsigned int timeOu try { - Jrd::dsql_req* req = getHandle(); + Jrd::DsqlRequest* req = getHandle(); req->setTimeout(timeOut); } catch (const Exception& ex) @@ -5964,11 +5975,11 @@ JBatch* JStatement::createBatch(Firebird::CheckStatusWrapper* status, Firebird:: } } - DsqlBatch* const b = DsqlBatch::open(tdbb, getHandle(), inMetadata, parLength, par); + const auto dsqlBatch = getHandle()->openBatch(tdbb, inMetadata, parLength, par); - batch = FB_NEW JBatch(b, this, inMetadata); + batch = FB_NEW JBatch(dsqlBatch, this, inMetadata); batch->addRef(); - b->setInterfacePtr(batch); + dsqlBatch->setInterfacePtr(batch); tdbb->getAttachment()->registerBatch(batch); } catch (const Exception& ex) @@ -7184,6 +7195,10 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli rdr.getString(dpb_decfloat_traps); break; + case isc_dpb_clear_map: + dpb_clear_map = rdr.getBoolean(); + break; + default: break; } @@ -7564,6 +7579,9 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment) attachment->att_replicator = nullptr; + if (attachment->att_dsql_instance) + attachment->att_dsql_instance->dbb_statement_cache->purge(tdbb); + while (attachment->att_repl_appliers.hasData()) { AutoPtr cleanupApplier(attachment->att_repl_appliers.pop()); @@ -8125,7 +8143,7 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign if (attachment && attachment->att_purge_tid == Thread::getId()) { - fb_assert(false); // recursive call - impossible ? +// fb_assert(false); // recursive call - impossible ? return; } @@ -8210,15 +8228,32 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign transaction = TRA_start(tdbb, 0, NULL); attachment->att_flags = save_flags; + // Allow cancelling while ON DISCONNECT triggers are running + tdbb->tdbb_flags &= ~TDBB_detaching; + // run ON DISCONNECT triggers EXE_execute_db_triggers(tdbb, transaction, TRIGGER_DISCONNECT); + tdbb->tdbb_flags |= TDBB_detaching; + // and commit the transaction TRA_commit(tdbb, transaction, false); } catch (const Exception& ex) { attachment->att_flags = save_flags; + tdbb->tdbb_flags |= TDBB_detaching; + + if (attachment->att_trace_manager->needs(ITraceFactory::TRACE_EVENT_ERROR)) + { + FbLocalStatus status; + ex.stuffException(&status); + + TraceConnectionImpl conn(attachment); + TraceStatusVectorImpl traceStatus(&status, TraceStatusVectorImpl::TS_ERRORS); + + attachment->att_trace_manager->event_error(&conn, &traceStatus, FB_FUNCTION); + } string s; s.printf("Database: %s\n\tError at disconnect:", attachment->att_filename.c_str()); @@ -8349,7 +8384,7 @@ static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction) // // @param request The incoming, parent request to be replaced. // @param level The level of the sub-request we need to find. -static jrd_req* verify_request_synchronization(JrdStatement* statement, USHORT level) +static Request* verify_request_synchronization(Statement* statement, USHORT level) { if (level) { @@ -8423,7 +8458,7 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status **/ static void getUserInfo(UserId& user, const DatabaseOptions& options, const char* aliasName, - const RefPtr* config, bool creating, Mapping& mapping) + const RefPtr* config, bool creating, Mapping& mapping, bool icuReset) { bool wheel = false; int id = -1, group = -1; // CVC: This var contained trash @@ -8489,6 +8524,8 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, const char if (wheel) { name = DBA_USER_NAME; + if (icuReset) + user.setFlag(USR_sysdba); } if (name.length() > USERNAME_LENGTH) @@ -8611,7 +8648,7 @@ namespace { // purge attachment, rollback any open transactions attachment->att_use_count++; - purge_attachment(tdbb, sAtt, PURGE_FORCE); + purge_attachment(tdbb, sAtt, engineShutdown ? PURGE_FORCE : PURGE_NOCHECK); } catch (const Exception& ex) { @@ -8909,7 +8946,7 @@ void thread_db::setTransaction(jrd_tra* val) traStat = val ? &val->tra_stats : RuntimeStatistics::getDummy(); } -void thread_db::setRequest(jrd_req* val) +void thread_db::setRequest(Request* val) { request = val; reqStat = val ? &val->req_stats : RuntimeStatistics::getDummy(); @@ -8917,8 +8954,10 @@ void thread_db::setRequest(jrd_req* val) SSHORT thread_db::getCharSet() const { - if (request && request->charSetId != CS_dynamic) - return request->charSetId; + USHORT charSetId; + + if (request && (charSetId = request->getStatement()->charSetId) != CS_dynamic) + return charSetId; return attachment->att_charset; } @@ -8933,11 +8972,8 @@ ISC_STATUS thread_db::getCancelState(ISC_STATUS* secondary) if (tdbb_flags & (TDBB_verb_cleanup | TDBB_dfw_cleanup | TDBB_detaching | TDBB_wait_cancel_disable)) return FB_SUCCESS; - if (attachment) + if (attachment && attachment->att_purge_tid != Thread::getId()) { - if (attachment->att_purge_tid == Thread::getId()) - return FB_SUCCESS; - if (attachment->att_flags & ATT_shutdown) { if (database->dbb_ast_flags & DBB_shutdown) @@ -8959,7 +8995,7 @@ ISC_STATUS thread_db::getCancelState(ISC_STATUS* secondary) if ((!request || !(request->getStatement()->flags & // temporary change to fix shutdown - (/*JrdStatement::FLAG_INTERNAL | */JrdStatement::FLAG_SYS_TRIGGER))) && + (/*Statement::FLAG_INTERNAL | */Statement::FLAG_SYS_TRIGGER))) && (!transaction || !(transaction->tra_flags & TRA_system))) { return isc_cancelled; @@ -9017,21 +9053,22 @@ void thread_db::reschedule() checkCancelState(); StableAttachmentPart::Sync* sync = this->getAttachment()->getStable()->getSync(); - Database* dbb = this->getDatabase(); if (sync->hasContention()) { FB_UINT64 cnt = sync->getLockCounter(); - EngineCheckout cout(this, FB_FUNCTION); - Thread::yield(); + { // scope + EngineCheckout cout(this, FB_FUNCTION); + Thread::yield(); - while (sync->hasContention() && (sync->getLockCounter() == cnt)) - Thread::sleep(1); + while (sync->hasContention() && (sync->getLockCounter() == cnt)) + Thread::sleep(1); + } + + checkCancelState(); } - checkCancelState(); - Monitoring::checkState(this); if (tdbb_quantum <= 0) @@ -9098,7 +9135,7 @@ void JRD_autocommit_ddl(thread_db* tdbb, jrd_tra* transaction) } -void JRD_receive(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_length, void* msg) +void JRD_receive(thread_db* tdbb, Request* request, USHORT msg_type, ULONG msg_length, void* msg) { /************************************** * @@ -9122,7 +9159,7 @@ void JRD_receive(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_l } -void JRD_send(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_length, const void* msg) +void JRD_send(thread_db* tdbb, Request* request, USHORT msg_type, ULONG msg_length, const void* msg) { /************************************** * @@ -9146,7 +9183,7 @@ void JRD_send(thread_db* tdbb, jrd_req* request, USHORT msg_type, ULONG msg_leng } -void JRD_start(Jrd::thread_db* tdbb, jrd_req* request, jrd_tra* transaction) +void JRD_start(Jrd::thread_db* tdbb, Request* request, jrd_tra* transaction) { /************************************** * @@ -9235,7 +9272,7 @@ void JRD_rollback_retaining(thread_db* tdbb, jrd_tra* transaction) } -void JRD_start_and_send(thread_db* tdbb, jrd_req* request, jrd_tra* transaction, +void JRD_start_and_send(thread_db* tdbb, Request* request, jrd_tra* transaction, USHORT msg_type, ULONG msg_length, const void* msg) { /************************************** @@ -9363,7 +9400,7 @@ void JRD_start_transaction(thread_db* tdbb, jrd_tra** transaction, } -void JRD_unwind_request(thread_db* tdbb, jrd_req* request) +void JRD_unwind_request(thread_db* tdbb, Request* request) { /************************************** * @@ -9381,47 +9418,6 @@ void JRD_unwind_request(thread_db* tdbb, jrd_req* request) } -void JRD_compile(thread_db* tdbb, - Jrd::Attachment* attachment, - jrd_req** req_handle, - ULONG blr_length, - const UCHAR* blr, - RefStrPtr ref_str, - ULONG dbginfo_length, - const UCHAR* dbginfo, - bool isInternalRequest) -{ -/************************************** - * - * J R D _ c o m p i l e - * - ************************************** - * - * Functional description - * Compile a request passing the SQL text and debug information. - * - **************************************/ - if (*req_handle) - status_exception::raise(Arg::Gds(isc_bad_req_handle)); - - jrd_req* request = CMP_compile2(tdbb, blr, blr_length, isInternalRequest, dbginfo_length, dbginfo); - request->req_attachment = attachment; - attachment->att_requests.add(request); - - JrdStatement* statement = request->getStatement(); - - if (ref_str) - statement->sqlText = ref_str; - - fb_assert(statement->blr.isEmpty()); - - if (attachment->getDebugOptions().getDsqlKeepBlr()) - statement->blr.insert(0, blr, blr_length); - - *req_handle = request; -} - - namespace { class DatabaseDirList : public DirectoryList diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 66c7e5206f..5818ce3efb 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -106,8 +106,8 @@ const unsigned MAX_CALLBACKS = 50; class thread_db; class Attachment; class jrd_tra; -class jrd_req; -class JrdStatement; +class Request; +class Statement; class jrd_file; class Format; class BufferDesc; @@ -136,7 +136,7 @@ class Trigger public: Firebird::HalfStaticArray blr; // BLR code Firebird::HalfStaticArray debugInfo; // Debug info - JrdStatement* statement; // Compiled statement + Statement* statement; // Compiled statement bool releaseInProgress; bool sysTrigger; FB_UINT64 type; // Trigger type @@ -248,7 +248,7 @@ public: virtual SLONG getSclType() const { - return SCL_object_procedure; + return obj_procedures; } virtual void releaseFormat() @@ -305,7 +305,7 @@ class IndexBlock : public pool_alloc public: IndexBlock* idb_next; ValueExprNode* idb_expression; // node tree for index expression - JrdStatement* idb_expression_statement; // statement for index expression evaluation + Statement* idb_expression_statement; // statement for index expression evaluation dsc idb_expression_desc; // descriptor for expression result Lock* idb_lock; // lock to synchronize changes to index USHORT idb_id; @@ -369,7 +369,7 @@ const USHORT WIN_garbage_collect = 8; // scan left a page for garbage collector #ifdef USE_ITIMER -class TimeoutTimer FB_FINAL : +class TimeoutTimer final : public Firebird::RefCntIface > { public: @@ -506,7 +506,7 @@ private: Database* database; Attachment* attachment; jrd_tra* transaction; - jrd_req* request; + Request* request; RuntimeStatistics *reqStat, *traStat, *attStat, *dbbStat; public: @@ -591,17 +591,17 @@ public: void setTransaction(jrd_tra* val); - jrd_req* getRequest() + Request* getRequest() { return request; } - const jrd_req* getRequest() const + const Request* getRequest() const { return request; } - void setRequest(jrd_req* val); + void setRequest(Request* val); SSHORT getCharSet() const; @@ -1092,6 +1092,18 @@ namespace Jrd { m_ref->getSync()->leave(); } + EngineCheckout(Attachment* att, const char* from) + : m_tdbb(nullptr), m_from(from) + { + fb_assert(att); + + if (att && att->att_use_count) + { + m_ref = att->getStable(); + m_ref->getSync()->leave(); + } + } + ~EngineCheckout() { if (m_ref.hasData()) diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index b1569e0fba..d7f4024eea 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -35,12 +35,12 @@ namespace Jrd { class jrd_tra; class blb; struct bid; - class jrd_req; - class JrdStatement; + class Request; + class Statement; class Service; class thread_db; struct teb; - class dsql_req; + class DsqlRequest; class MetaName; } @@ -55,25 +55,22 @@ void JRD_print_procedure_info(Jrd::thread_db*, const char*); void JRD_autocommit_ddl(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); -void JRD_receive(Jrd::thread_db* tdbb, Jrd::jrd_req* request, USHORT msg_type, ULONG msg_length, +void JRD_receive(Jrd::thread_db* tdbb, Jrd::Request* request, USHORT msg_type, ULONG msg_length, void* msg); -void JRD_start(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tra* transaction); +void JRD_start(Jrd::thread_db* tdbb, Jrd::Request* request, Jrd::jrd_tra* transaction); void JRD_commit_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_commit_retaining(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_rollback_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_rollback_retaining(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_run_trans_start_triggers(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); -void JRD_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, USHORT msg_type, ULONG msg_length, +void JRD_send(Jrd::thread_db* tdbb, Jrd::Request* request, USHORT msg_type, ULONG msg_length, const void* msg); -void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tra* transaction, +void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::Request* request, Jrd::jrd_tra* transaction, USHORT msg_type, ULONG msg_length, const void* msg); void JRD_start_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction, Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb); -void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::jrd_req* request); -void JRD_compile(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, Jrd::jrd_req** req_handle, - ULONG blr_length, const UCHAR* blr, Firebird::RefStrPtr, - ULONG dbginfo_length, const UCHAR* dbginfo, bool isInternalRequest); +void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::Request* request); bool JRD_verify_database_access(const Firebird::PathName&); void JRD_shutdown_attachment(Jrd::Attachment* attachment); void JRD_shutdown_attachments(Jrd::Database* dbb); diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index 62863b5e72..af2b76ab11 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -584,6 +584,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_record_gc: case LCK_alter_database: case LCK_repl_tables: + case LCK_dsql_statement_cache: owner_type = LCK_OWNER_attachment; break; diff --git a/src/jrd/lck.h b/src/jrd/lck.h index 892aa1fe21..1efa69cf26 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -74,7 +74,8 @@ enum lck_t { LCK_record_gc, // Record-level GC lock LCK_alter_database, // ALTER DATABASE lock LCK_repl_state, // Replication state lock - LCK_repl_tables // Replication set lock + LCK_repl_tables, // Replication set lock + LCK_dsql_statement_cache // DSQL statement cache lock }; // Lock owner types diff --git a/src/jrd/license.h b/src/jrd/license.h index 1fbcff04d9..c162f1d3bc 100644 --- a/src/jrd/license.h +++ b/src/jrd/license.h @@ -125,8 +125,8 @@ #if defined(__ppc__) || defined(__ppc64__) #define FB_PLATFORM "UP" // Darwin/PowerPC #endif -#if defined(ARM) -#define FB_PLATFORM "UA" +#if defined(ARM) || defined(__aarch64__) +#define FB_PLATFORM "UA" // Darwin/ARM #endif #endif diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 662a2aa3d8..de67421056 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -122,7 +122,7 @@ static void make_relation_scope_name(const TEXT*, const USHORT, string& str); static ValueExprNode* parse_field_default_blr(thread_db* tdbb, bid* blob_id); static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, const MetaName name); static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*); -static void save_trigger_data(thread_db*, TrigVector**, jrd_rel*, JrdStatement*, blb*, blb*, +static void save_trigger_data(thread_db*, TrigVector**, jrd_rel*, Statement*, blb*, blb*, const TEXT*, FB_UINT64, bool, USHORT, const MetaName&, const string&, const bid*, Nullable ssDefiner); static void scan_partners(thread_db*, jrd_rel*); @@ -131,10 +131,10 @@ static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*, static bool verify_TRG_ignore_perm(thread_db*, const MetaName&); -static void inc_int_use_count(JrdStatement* statement) +static void inc_int_use_count(Statement* statement) { // Handle sub-statements - for (JrdStatement** subStatement = statement->subStatements.begin(); + for (Statement** subStatement = statement->subStatements.begin(); subStatement != statement->subStatements.end(); ++subStatement) { @@ -175,7 +175,7 @@ static void post_used_procedures(TrigVector* vector) for (FB_SIZE_T i = 0; i < vector->getCount(); i++) { - JrdStatement* stmt = (*vector)[i].statement; + Statement* stmt = (*vector)[i].statement; if (stmt && !stmt->isActive()) inc_int_use_count(stmt); } @@ -1648,7 +1648,7 @@ DmlNode* MET_get_dependencies(thread_db* tdbb, const ULONG blob_length, CompilerScratch* view_csb, bid* blob_id, - JrdStatement** statementPtr, + Statement** statementPtr, CompilerScratch** csb_ptr, const MetaName& object_name, int type, @@ -2620,16 +2620,15 @@ void MET_lookup_index_expression(thread_db* tdbb, jrd_rel* relation, index_desc* { // scope Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); - idx->idx_expression = static_cast(MET_parse_blob( - tdbb, relation, &IDX.RDB$EXPRESSION_BLR, &csb, - &idx->idx_expression_statement, false, false)); + + MET_parse_blob(tdbb, relation, &IDX.RDB$EXPRESSION_BLR, &csb, nullptr, false, false); + + idx->idx_expression_statement = Statement::makeValueExpression(tdbb, + idx->idx_expression, idx->idx_expression_desc, csb, false); } // end scope } END_FOR - if (csb) - idx->idx_expression->getDesc(tdbb, csb, &idx->idx_expression_desc); - delete csb; // if there is no existing index block for this index, create @@ -3073,7 +3072,7 @@ DmlNode* MET_parse_blob(thread_db* tdbb, jrd_rel* relation, bid* blob_id, CompilerScratch** csb_ptr, - JrdStatement** statementPtr, + Statement** statementPtr, const bool trigger, bool validationExpr) { @@ -3198,7 +3197,7 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) else par_flags |= csb_post_trigger; - JrdStatement* statement = NULL; + Statement* statement = NULL; { Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); @@ -3207,9 +3206,9 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) statement->triggerName = name; - statement->flags |= JrdStatement::FLAG_SYS_TRIGGER; + statement->flags |= Statement::FLAG_SYS_TRIGGER; if (trig_flags & TRG_ignore_perm) - statement->flags |= JrdStatement::FLAG_IGNORE_PERM; + statement->flags |= Statement::FLAG_IGNORE_PERM; save_trigger_data(tdbb, ptr, relation, statement, NULL, NULL, NULL, type, true, 0, "", "", NULL, Nullable()); @@ -3402,6 +3401,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) procedure->invoker = attachment->getUserId(procedure->owner); procedure->setImplemented(true); + procedure->setDefined(true); procedure->getInputFields().resize(P.RDB$PROCEDURE_INPUTS); procedure->getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS); procedure->setDefaultCount(0); @@ -3526,6 +3526,9 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) dbb->dbb_extManager->makeProcedure(tdbb, csb, procedure, P.RDB$ENGINE_NAME, (P.RDB$ENTRYPOINT.NULL ? "" : P.RDB$ENTRYPOINT), body.begin()); + + if (!procedure->getExternal()) + procedure->setDefined(false); } else { @@ -3554,7 +3557,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags) throw; } - fb_assert(procedure->getStatement()->procedure == procedure); + fb_assert(!procedure->isDefined() || procedure->getStatement()->procedure == procedure); } else { @@ -4771,7 +4774,7 @@ void MET_release_trigger(thread_db* tdbb, TrigVector** vector_ptr, const MetaNam { if (vector[i].name == name) { - JrdStatement* stmt = vector[i].statement; + Statement* stmt = vector[i].statement; if (stmt) { if (stmt->isActive()) @@ -4933,7 +4936,7 @@ static bool resolve_charset_and_collation(thread_db* tdbb, static void save_trigger_data(thread_db* tdbb, TrigVector** ptr, jrd_rel* relation, - JrdStatement* statement, blb* blrBlob, blb* debugInfoBlob, + Statement* statement, blb* blrBlob, blb* debugInfoBlob, const TEXT* name, FB_UINT64 type, bool sys_trigger, USHORT flags, const MetaName& engine, const string& entryPoint, diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index 1c87a5274a..bd90314f21 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -31,8 +31,8 @@ struct dsc; namespace Jrd { class jrd_tra; - class jrd_req; - class JrdStatement; + class Request; + class Statement; class jrd_prc; class Format; class jrd_rel; @@ -86,7 +86,7 @@ Jrd::Format* MET_format(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); bool MET_get_char_coll_subtype(Jrd::thread_db*, USHORT*, const UCHAR*, USHORT); bool MET_get_char_coll_subtype_info(Jrd::thread_db*, USHORT, SubtypeInfo* info); Jrd::DmlNode* MET_get_dependencies(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, const ULONG, - Jrd::CompilerScratch*, Jrd::bid*, Jrd::JrdStatement**, + Jrd::CompilerScratch*, Jrd::bid*, Jrd::Statement**, Jrd::CompilerScratch**, const Jrd::MetaName&, int, USHORT, Jrd::jrd_tra*, const Jrd::MetaName& = Jrd::MetaName()); Jrd::jrd_fld* MET_get_field(const Jrd::jrd_rel*, USHORT); @@ -115,7 +115,7 @@ Jrd::jrd_prc* MET_lookup_procedure_id(Jrd::thread_db*, USHORT, bool, bool, USHOR Jrd::jrd_rel* MET_lookup_relation(Jrd::thread_db*, const Jrd::MetaName&); Jrd::jrd_rel* MET_lookup_relation_id(Jrd::thread_db*, SLONG, bool); Jrd::DmlNode* MET_parse_blob(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::bid*, Jrd::CompilerScratch**, - Jrd::JrdStatement**, bool, bool); + Jrd::Statement**, bool, bool); void MET_parse_sys_trigger(Jrd::thread_db*, Jrd::jrd_rel*); void MET_post_existence(Jrd::thread_db*, Jrd::jrd_rel*); void MET_prepare(Jrd::thread_db*, Jrd::jrd_tra*, USHORT, const UCHAR*); diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index 8b2323d5aa..1e8a618171 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -488,7 +488,7 @@ Int128 MOV_get_int128(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) namespace Jrd { -DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) +DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHORT charSetId) : maxLen(mLen) { const char* const NULL_KEY_STRING = "NULL"; @@ -501,8 +501,8 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) fb_assert(!desc->isBlob()); - const bool isBinary = (desc->isText() && desc->getTextType() == ttype_binary); - value = MOV_make_string2(tdbb, desc, isBinary ? ttype_binary : ttype_dynamic); + const bool isBinary = (desc->isText() && desc->getCharSet() == CS_BINARY); + value = MOV_make_string2(tdbb, desc, isBinary ? CS_BINARY : charSetId); const char* const str = value.c_str(); @@ -510,7 +510,7 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) { if (desc->dsc_dtype == dtype_text) { - const char* const pad = (desc->dsc_sub_type == ttype_binary) ? "\0" : " "; + const char* const pad = (desc->getCharSet() == CS_BINARY) ? "\0" : " "; value.rtrim(pad); } @@ -542,7 +542,7 @@ DescPrinter::DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen) value.resize(maxLen); - const CharSet* const cs = INTL_charset_lookup(tdbb, desc->getCharSet()); + const CharSet* const cs = INTL_charset_lookup(tdbb, charSetId); while (value.hasData() && !cs->wellFormed(value.length(), (const UCHAR*) value.c_str())) value.resize(value.length() - 1); diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index 816842a4b2..8edfd15923 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -62,7 +62,7 @@ namespace Jrd class DescPrinter { public: - DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen); + DescPrinter(thread_db* tdbb, const dsc* desc, FB_SIZE_T mLen, USHORT charSetId); const Firebird::string& get() const { diff --git a/src/jrd/names.h b/src/jrd/names.h index ece88e406d..1c81cafc3c 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -457,3 +457,6 @@ NAME("MON$SESSION_TIMEZONE", nam_mon_session_tz) NAME("RDB$KEYWORDS", nam_keywords) NAME("RDB$KEYWORD_NAME", nam_keyword_name) NAME("RDB$KEYWORD_RESERVED", nam_keyword_reserved) + +NAME("MON$COMPILED_STATEMENTS", nam_mon_compiled_statements) +NAME("MON$COMPILED_STATEMENT_ID", nam_mon_cmp_stmt_id) diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 1bb0bf9b7e..887dfd9a56 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -24,56 +24,92 @@ #ifndef JRD_OBJ_H #define JRD_OBJ_H -// Object types used in RDB$DEPENDENCIES and RDB$USER_PRIVILEGES +// Object types used in RDB$DEPENDENCIES and RDB$USER_PRIVILEGES and stored in backup. // Note: some values are hard coded in grant.gdl +// Keep existing constants unchanged. -const int obj_relation = 0; -const int obj_view = 1; -const int obj_trigger = 2; -const int obj_computed = 3; -const int obj_validation = 4; -const int obj_procedure = 5; -const int obj_expression_index = 6; -const int obj_exception = 7; -const int obj_user = 8; -const int obj_field = 9; -const int obj_index = 10; -const int obj_charset = 11; -const int obj_user_group = 12; -const int obj_sql_role = 13; -const int obj_generator = 14; -const int obj_udf = 15; -const int obj_blob_filter = 16; -const int obj_collation = 17; -const int obj_package_header = 18; -const int obj_package_body = 19; -const int obj_privilege = 20; +typedef SSHORT ObjectType; -const int obj_last_non_ddl = 20; // keep in sync!!! +const ObjectType obj_relation = 0; +const ObjectType obj_view = 1; +const ObjectType obj_trigger = 2; +const ObjectType obj_computed = 3; +const ObjectType obj_validation = 4; +const ObjectType obj_procedure = 5; +const ObjectType obj_expression_index = 6; +const ObjectType obj_exception = 7; +const ObjectType obj_user = 8; +const ObjectType obj_field = 9; +const ObjectType obj_index = 10; +const ObjectType obj_charset = 11; +const ObjectType obj_user_group = 12; +const ObjectType obj_sql_role = 13; +const ObjectType obj_generator = 14; +const ObjectType obj_udf = 15; +const ObjectType obj_blob_filter = 16; +const ObjectType obj_collation = 17; +const ObjectType obj_package_header = 18; +const ObjectType obj_package_body = 19; +const ObjectType obj_privilege = 20; // objects types for ddl operations -const int obj_database = obj_last_non_ddl + 1; -const int obj_relations = obj_last_non_ddl + 2; -const int obj_views = obj_last_non_ddl + 3; -const int obj_procedures = obj_last_non_ddl + 4; -const int obj_functions = obj_last_non_ddl + 5; -const int obj_packages = obj_last_non_ddl + 6; -const int obj_generators = obj_last_non_ddl + 7; -const int obj_domains = obj_last_non_ddl + 8; -const int obj_exceptions = obj_last_non_ddl + 9; -const int obj_roles = obj_last_non_ddl + 10; -const int obj_charsets = obj_last_non_ddl + 11; -const int obj_collations = obj_last_non_ddl + 12; -const int obj_filters = obj_last_non_ddl + 13; +const ObjectType obj_database = 21; +const ObjectType obj_relations = 22; +const ObjectType obj_views = 23; +const ObjectType obj_procedures = 24; +const ObjectType obj_functions = 25; +const ObjectType obj_packages = 26; +const ObjectType obj_generators = 27; +const ObjectType obj_domains = 28; +const ObjectType obj_exceptions = 29; +const ObjectType obj_roles = 30; +const ObjectType obj_charsets = 31; +const ObjectType obj_collations = 32; +const ObjectType obj_filters = 33; -const int obj_type_MAX = obj_last_non_ddl + 14; // keep this last! +// Add new codes here if they are used in RDB$DEPENDENCIES or RDB$USER_PRIVILEGES or stored in backup +// Codes for DDL operations add in isDdlObject function as well (find it below). +const ObjectType obj_jobs = 34; +const ObjectType obj_tablespace = 35; +const ObjectType obj_tablespaces = 36; + +const ObjectType obj_type_MAX = 37; // used in the parser only / no relation with obj_type_MAX (should be greater) -const int obj_user_or_role = 100; -const int obj_schema = 101; -const int obj_parameter = 102; +const ObjectType obj_user_or_role= 100; +const ObjectType obj_parameter = 101; +const ObjectType obj_column = 102; -inline const char* get_object_name(int object_type) +const ObjectType obj_any = 255; + + +inline bool isDdlObject(ObjectType object_type) +{ + switch (object_type) + { + case obj_database: + case obj_relations: + case obj_views: + case obj_procedures: + case obj_functions: + case obj_packages: + case obj_generators: + case obj_filters: + case obj_domains: + case obj_exceptions: + case obj_roles: + case obj_charsets: + case obj_collations: + case obj_jobs: + case obj_tablespaces: + return true; + default: + return false; + } +} + + +inline const char* getSecurityClassName(ObjectType object_type) { switch (object_type) { @@ -103,9 +139,57 @@ inline const char* get_object_name(int object_type) return "SQL$CHARSETS"; case obj_collations: return "SQL$COLLATIONS"; + case obj_tablespaces: + return "SQL$TABLESPACES"; + case obj_jobs: + return "SQL$JOBS"; default: return ""; } } + +inline const char* getDdlObjectName(ObjectType object_type) +{ + switch (object_type) + { + case obj_database: + return "DATABASE"; + case obj_relations: + return "TABLE"; + case obj_packages: + return "PACKAGE"; + case obj_procedures: + return "PROCEDURE"; + case obj_functions: + return "FUNCTION"; + case obj_column: + return "COLUMN"; + case obj_charsets: + return "CHARACTER SET"; + case obj_collations: + return "COLLATION"; + case obj_domains: + return "DOMAIN"; + case obj_exceptions: + return "EXCEPTION"; + case obj_generators: + return "GENERATOR"; + case obj_views: + return "VIEW"; + case obj_roles: + return "ROLE"; + case obj_filters: + return "FILTER"; + case obj_tablespaces: + return "TABLESPACE"; + case obj_jobs: + return "JOB"; + default: + fb_assert(false); + return ""; + } +} + + #endif // JRD_OBJ_H diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp deleted file mode 100644 index d212417ed1..0000000000 --- a/src/jrd/opt.cpp +++ /dev/null @@ -1,3807 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: opt.cpp - * DESCRIPTION: Optimizer / record selection expression compiler - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - * 2002.10.12: Nickolay Samofatov: Fixed problems with wrong results produced by - * outer joins - * 2001.07.28: John Bellardo: Added code to handle rse_skip nodes. - * 2001.07.17 Claudio Valderrama: Stop crash with indices and recursive calls - * of OPT_compile: indicator csb_indices set to zero after used memory is - * returned to the free pool. - * 2001.02.15: Claudio Valderrama: Don't obfuscate the plan output if a selectable - * stored procedure doesn't access tables, views or other procedures directly. - * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port - * 2002.10.30: Arno Brinkman: Changes made to gen_retrieval, OPT_compile and make_inversion. - * Procedure sort_indices added. The changes in gen_retrieval are that now - * an index with high field-count has priority to build an index from. - * Procedure make_inversion is changed so that it not pick every index - * that comes away, this was slow performance with bad selectivity indices - * which most are foreign_keys with a reference to a few records. - * 2002.11.01: Arno Brinkman: Added match_indices for better support of OR handling - * in INNER JOIN (gen_join) statements. - * 2002.12.15: Arno Brinkman: Added find_used_streams, so that inside opt_compile all the - * streams are marked active. This causes that more indices can be used for - * a retrieval. With this change BUG SF #219525 is solved too. - */ - -#include "firebird.h" -#include -#include -#include "../jrd/jrd.h" -#include "../jrd/align.h" -#include "../jrd/val.h" -#include "../jrd/req.h" -#include "../jrd/exe.h" -#include "../jrd/lls.h" -#include "../jrd/ods.h" -#include "../jrd/btr.h" -#include "../jrd/sort.h" -#include "../jrd/rse.h" -#include "../jrd/ini.h" -#include "../jrd/intl.h" -#include "../jrd/Collation.h" -#include "../common/gdsassert.h" -#include "../jrd/btr_proto.h" -#include "../jrd/cch_proto.h" -#include "../jrd/cmp_proto.h" -#include "../jrd/cvt2_proto.h" -#include "../jrd/dpm_proto.h" -#include "../common/dsc_proto.h" -#include "../jrd/err_proto.h" -#include "../jrd/ext_proto.h" -#include "../jrd/intl_proto.h" - -#include "../jrd/lck_proto.h" -#include "../jrd/met_proto.h" -#include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" -#include "../jrd/par_proto.h" -#include "../yvalve/gds_proto.h" -#include "../jrd/DataTypeUtil.h" -#include "../jrd/KeywordsTable.h" -#include "../jrd/RecordSourceNodes.h" -#include "../jrd/VirtualTable.h" -#include "../jrd/Monitoring.h" -#include "../jrd/TimeZone.h" -#include "../jrd/UserManagement.h" -#include "../common/classes/array.h" -#include "../common/classes/objects_array.h" -#include "../common/os/os_utils.h" -#include "../jrd/recsrc/RecordSource.h" -#include "../jrd/recsrc/Cursor.h" -#include "../jrd/Mapping.h" -#include "../jrd/DbCreators.h" - -#include "../jrd/Optimizer.h" -#include "../dsql/BoolNodes.h" -#include "../dsql/ExprNodes.h" -#include "../dsql/StmtNodes.h" -#include "../jrd/ConfigTable.h" - -using namespace Jrd; -using namespace Firebird; - -#ifdef DEV_BUILD -#define OPT_DEBUG -#endif - - -namespace -{ - class River; - typedef HalfStaticArray RiverList; - - inline void compose(MemoryPool& pool, BoolExprNode** node1, BoolExprNode* node2) - { - if (node2) - *node1 = (*node1) ? FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and, *node1, node2) : node2; - } - - struct SortField - { - SortField() : stream(INVALID_STREAM), id(0), desc(NULL) - {} - - SortField(StreamType _stream, ULONG _id, const dsc* _desc) - : stream(_stream), id(_id), desc(_desc) - {} - - StreamType stream; - ULONG id; - const dsc* desc; - }; - - class River - { - public: - River(CompilerScratch* csb, RecordSource* rsb, RecordSourceNode* node, const StreamList& streams) - : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) - { - if (node) - m_nodes.add(node); - - m_streams.assign(streams); - } - - River(CompilerScratch* csb, RecordSource* rsb, RiverList& rivers) - : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) - { - for (River** iter = rivers.begin(); iter < rivers.end(); iter++) - { - River* const sub_river = *iter; - - const size_t count = m_streams.getCount(); - const size_t delta = sub_river->m_streams.getCount(); - if (count + delta >= MAX_STREAMS) - ERR_post(Arg::Gds(isc_too_many_contexts)); - - m_nodes.join(sub_river->m_nodes); - m_streams.join(sub_river->m_streams); - } - } - - RecordSource* getRecordSource() const - { - return m_rsb; - } - - const StreamList& getStreams() const - { - return m_streams; - } - - void activate(CompilerScratch* csb) - { - for (const StreamType* iter = m_streams.begin(); iter < m_streams.end(); iter++) - csb->csb_rpt[*iter].activate(); - } - - void deactivate(CompilerScratch* csb) - { - for (const StreamType* iter = m_streams.begin(); iter < m_streams.end(); iter++) - csb->csb_rpt[*iter].deactivate(); - } - - bool isReferenced(const ExprNode* node) const - { - SortedStreamList nodeStreams; - node->collectStreams(nodeStreams); - - if (!nodeStreams.hasData()) - return false; - - for (const StreamType* iter = nodeStreams.begin(); iter != nodeStreams.end(); ++iter) - { - if (!m_streams.exist(*iter)) - return false; - } - - return true; - } - - bool isComputable(CompilerScratch* csb) const - { - for (RecordSourceNode* const* iter = m_nodes.begin(); iter < m_nodes.end(); iter++) - { - if (!(*iter)->computable(csb, INVALID_STREAM, false)) - return false; - } - - return true; - } - - RecordSource* applyLocalBoolean(OptimizerBlk* opt) - { - fb_assert(m_rsb); - - CompilerScratch* const csb = opt->opt_csb; - - StreamStateHolder stateHolder(csb); - stateHolder.deactivate(); - - activate(csb); - - BoolExprNode* boolean = NULL; - - const OptimizerBlk::opt_conjunct* const opt_end = - opt->opt_conjuncts.begin() + opt->opt_base_conjuncts; - - for (OptimizerBlk::opt_conjunct* tail = opt->opt_conjuncts.begin(); - tail < opt_end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - compose(csb->csb_pool, &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - - if (boolean) - m_rsb = FB_NEW_POOL(csb->csb_pool) FilteredStream(csb, m_rsb, boolean); - - return m_rsb; - } - - protected: - RecordSource* m_rsb; - HalfStaticArray m_nodes; - StreamList m_streams; - }; - - class CrossJoin : public River - { - public: - CrossJoin(CompilerScratch* csb, RiverList& rivers) - : River(csb, NULL, rivers) - { - // Save states of the underlying streams and restore them afterwards - - StreamStateHolder stateHolder(csb, m_streams); - - // Generate record source objects - - const FB_SIZE_T riverCount = rivers.getCount(); - - if (riverCount == 1) - { - River* const sub_river = rivers.pop(); - m_rsb = sub_river->getRecordSource(); - } - else - { - HalfStaticArray rsbs(riverCount); - - // Reorder input rivers according to their possible inter-dependencies - - while (rivers.hasData()) - { - const auto orgCount = rsbs.getCount(); - - for (auto& subRiver : rivers) - { - const auto subRsb = subRiver->getRecordSource(); - fb_assert(!rsbs.exist(subRsb)); - - subRiver->activate(csb); - - if (subRiver->isComputable(csb)) - { - rsbs.add(subRsb); - rivers.remove(&subRiver); - break; - } - - subRiver->deactivate(csb); - } - - if (rsbs.getCount() == orgCount) - break; - } - - if (rivers.hasData()) - { - // Ideally, we should never get here. But just in case it happened, handle it. - - fb_assert(false); - - for (auto& subRiver : rivers) - { - const auto subRsb = subRiver->getRecordSource(); - fb_assert(!rsbs.exist(subRsb)); - - const auto pos = &subRiver - rivers.begin(); - rsbs.insert(pos, subRsb); - } - - rivers.clear(); - } - - m_rsb = FB_NEW_POOL(csb->csb_pool) NestedLoopJoin(csb, rsbs.getCount(), rsbs.begin()); - } - } - }; -} // namespace - -static bool augment_stack(ValueExprNode*, ValueExprNodeStack&); -static bool augment_stack(BoolExprNode*, BoolExprNodeStack&); -static void check_indices(const CompilerScratch::csb_repeat*); -static void check_sorts(CompilerScratch*, RseNode*); -static void class_mask(USHORT, ValueExprNode**, ULONG*); -static SLONG decompose(thread_db* tdbb, BoolExprNode* boolNode, BoolExprNodeStack& stack, - CompilerScratch* csb); -static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratch* csb, - USHORT base_count); -static void find_index_relationship_streams(thread_db* tdbb, OptimizerBlk* opt, - const StreamList& streams, StreamList& dependent_streams, StreamList& free_streams); -static void form_rivers(thread_db* tdbb, OptimizerBlk* opt, const StreamList& streams, - RiverList& river_list, SortNode** sort_clause, PlanNode* plan_clause); -static bool form_river(thread_db* tdbb, OptimizerBlk* opt, StreamType count, size_t stream_count, - StreamList& temp, RiverList& river_list, SortNode** sort_clause); -static void gen_join(thread_db* tdbb, OptimizerBlk* opt, const StreamList& streams, - RiverList& river_list, SortNode** sort_clause, PlanNode* plan_clause); -static RecordSource* gen_outer(thread_db* tdbb, OptimizerBlk* opt, RseNode* rse, - RiverList& river_list, SortNode** sort_clause); -static RecordSource* gen_residual_boolean(thread_db* tdbb, OptimizerBlk* opt, RecordSource* prior_rsb); -static RecordSource* gen_retrieval(thread_db* tdbb, OptimizerBlk* opt, StreamType stream, - SortNode** sort_ptr, bool outer_flag, bool inner_flag, BoolExprNode** return_boolean); -static bool gen_equi_join(thread_db*, OptimizerBlk*, RiverList&); -static double get_cardinality(thread_db*, jrd_rel*, const Format*); -static BoolExprNode* make_inference_node(CompilerScratch*, BoolExprNode*, ValueExprNode*, ValueExprNode*); -static bool map_equal(const ValueExprNode*, const ValueExprNode*, const MapNode*); -static void mark_indices(CompilerScratch::csb_repeat* csbTail, SSHORT relationId); -static bool node_equality(const ValueExprNode*, const ValueExprNode*); -static bool node_equality(const BoolExprNode*, const BoolExprNode*); -static ValueExprNode* optimize_like_similar(thread_db*, CompilerScratch*, ComparativeBoolNode*); -static USHORT river_count(USHORT count, ValueExprNode** eq_class); -static bool search_stack(const ValueExprNode*, const ValueExprNodeStack&); -static void set_direction(SortNode*, SortNode*); -static void set_position(const SortNode*, SortNode*, const MapNode*); -static void sort_indices_by_selectivity(CompilerScratch::csb_repeat* csbTail); - - -// macro definitions - -#ifdef OPT_DEBUG -//const int DEBUG_PUNT = 5; -//const int DEBUG_RELATIONSHIPS = 4; -//const int DEBUG_ALL = 3; -//const int DEBUG_CANDIDATE = 2; -//const int DEBUG_BEST = 1; -const int DEBUG_NONE = 0; - -FILE *opt_debug_file = 0; -static int opt_debug_flag = DEBUG_NONE; -#endif - -inline void SET_DEP_BIT(ULONG* array, const SLONG bit) -{ - array[bit / BITS_PER_LONG] |= (1L << (bit % BITS_PER_LONG)); -} - -/* -inline void CLEAR_DEP_BIT(ULONG* array, const SLONG bit) -{ - array[bit / BITS_PER_LONG] &= ~(1L << (bit % BITS_PER_LONG)); -} -*/ - -inline bool TEST_DEP_BIT(const ULONG* array, const ULONG bit) -{ - return (array[bit / BITS_PER_LONG] & (1L << (bit % BITS_PER_LONG))) != 0; -} - -/* -inline bool TEST_DEP_ARRAYS(const ULONG* ar1, const ULONG* ar2) -{ - //return (ar1[0] & ar2[0]) || (ar1[1] & ar2[1]) || (ar1[2] & ar2[2]) || (ar1[3] & ar2[3]) || - // (ar1[4] & ar2[4]) || (ar1[5] & ar2[5]) || (ar1[6] & ar2[6]) || (ar1[7] & ar2[7]); - for (SLONG i = 0; i < BITS_PER_LONG; i++) - { - if (ar1[i] & ar2[i]) - return true; - } - return false; -} -*/ - -const int CACHE_PAGES_PER_STREAM = 15; - -// enumeration of sort datatypes - -static const UCHAR sort_dtypes[] = -{ - 0, // dtype_unknown - SKD_text, // dtype_text - SKD_cstring, // dtype_cstring - SKD_varying, // dtype_varying - 0, - 0, - 0, // dtype_packed - 0, // dtype_byte - SKD_short, // dtype_short - SKD_long, // dtype_long - SKD_quad, // dtype_quad - SKD_float, // dtype_real - SKD_double, // dtype_double - SKD_double, // dtype_d_float - SKD_sql_date, // dtype_sql_date - SKD_sql_time, // dtype_sql_time - SKD_timestamp, // dtype_timestamp - SKD_quad, // dtype_blob - 0, // dtype_array - SKD_int64, // dtype_int64 - SKD_text, // dtype_dbkey - use text sort for backward compatibility - SKD_bytes, // dtype_boolean - SKD_dec64, // dtype_dec64 - SKD_dec128, // dtype_dec128 - SKD_int128, // dtype_int128 - SKD_sql_time_tz, // dtype_sql_time_tz - SKD_timestamp_tz // dtype_timestamp_tz -}; - - -string OPT_get_plan(thread_db* tdbb, const jrd_req* request, bool detailed) -{ -/************************************** - * - * O P T _ g e t _ p l a n - * - ************************************** - * - * Functional description - * Returns a formatted textual plan for all RseNode's in the specified request. - * - **************************************/ - string plan; - - if (request) - { - const Array& fors = request->getStatement()->fors; - - for (FB_SIZE_T i = 0; i < fors.getCount(); i++) - { - plan += detailed ? "\nSelect Expression" : "\nPLAN "; - fors[i]->print(tdbb, plan, detailed, 0); - } - } - - return plan; -} - - -// Compile and optimize a record selection expression into a set of record source blocks (rsb's). -RecordSource* OPT_compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, - BoolExprNodeStack* parent_stack) -{ - DEV_BLKCHK(csb, type_csb); - DEV_BLKCHK(rse, type_nod); - - SET_TDBB(tdbb); - -#ifdef OPT_DEBUG - if (opt_debug_flag != DEBUG_NONE && !opt_debug_file) - opt_debug_file = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "w"); -#endif - - // If there is a boolean, there is some work to be done. First, - // decompose the boolean into conjunctions. Then get descriptions - // of all indices for all relations in the RseNode. This will give - // us the info necessary to allocate a optimizer block big - // enough to hold this crud. - - // Do not allocate the index_desc struct. Let BTR_all do the job. The allocated - // memory will then be in csb->csb_rpt[stream].csb_idx_allocation, which - // gets cleaned up before this function exits. - - MemoryPool* const pool = tdbb->getDefaultPool(); - - AutoPtr opt(FB_NEW_POOL(*pool) OptimizerBlk(pool, rse)); - opt->opt_streams.grow(csb->csb_n_stream); - opt->favorFirstRows = (rse->flags & RseNode::FLAG_OPT_FIRST_ROWS) != 0; - - RecordSource* rsb = NULL; - - try { - - opt->opt_csb = csb; - - RiverList rivers; - - check_sorts(csb, rse); - SortNode* sort = rse->rse_sorted; - SortNode* project = rse->rse_projection; - SortNode* aggregate = rse->rse_aggregate; - - BoolExprNodeStack conjunct_stack; - SLONG conjunct_count = 0; - - // put any additional booleans on the conjunct stack, and see if we - // can generate additional booleans by associativity--this will help - // to utilize indices that we might not have noticed - if (rse->rse_boolean) - conjunct_count = decompose(tdbb, rse->rse_boolean, conjunct_stack, csb); - - conjunct_count += distribute_equalities(conjunct_stack, csb, conjunct_count); - - // AB: If we have limit our retrieval with FIRST / SKIP syntax then - // we may not deliver above conditions (from higher rse's) to this - // rse, because the results should be consistent. - if (rse->rse_skip || rse->rse_first) - parent_stack = NULL; - - // Set base-point before the parent/distributed nodes begin. - const USHORT base_count = (USHORT) conjunct_count; - opt->opt_base_conjuncts = base_count; - - // AB: Add parent conjunctions to conjunct_stack, keep in mind - // the outer-streams! For outer streams put missing (IS NULL) - // conjunctions in the missing_stack. - // - // opt_rpt[0..opt_base_conjuncts-1] = defined conjunctions to this stream - // opt_rpt[0..opt_base_parent_conjuncts-1] = defined conjunctions to this - // stream and allowed distributed conjunctions (with parent) - // opt_rpt[0..opt_base_missing_conjuncts-1] = defined conjunctions to this - // stream and allowed distributed conjunctions and allowed parent - // opt_rpt[0..opt_conjuncts_count-1] = all conjunctions - // - // allowed = booleans that can never evaluate to NULL/Unknown or turn - // NULL/Unknown into a True or False. - - USHORT parent_count = 0, distributed_count = 0; - BoolExprNodeStack missing_stack; - - if (parent_stack) - { - for (BoolExprNodeStack::iterator iter(*parent_stack); - iter.hasData() && conjunct_count < MAX_CONJUNCTS; ++iter) - { - BoolExprNode* const node = iter.object(); - - if (rse->rse_jointype != blr_inner && node->possiblyUnknown()) - { - // parent missing conjunctions shouldn't be - // distributed to FULL OUTER JOIN streams at all - if (rse->rse_jointype != blr_full) - missing_stack.push(node); - } - else - { - conjunct_stack.push(node); - conjunct_count++; - parent_count++; - } - } - - // We've now merged parent, try again to make more conjunctions. - distributed_count = distribute_equalities(conjunct_stack, csb, conjunct_count); - conjunct_count += distributed_count; - } - - // The newly created conjunctions belong to the base conjunctions. - // After them are starting the parent conjunctions. - opt->opt_base_parent_conjuncts = opt->opt_base_conjuncts + distributed_count; - - // Set base-point before the parent IS NULL nodes begin - opt->opt_base_missing_conjuncts = (USHORT) conjunct_count; - - // Check if size of optimizer block exceeded. - if (conjunct_count > MAX_CONJUNCTS) - { - ERR_post(Arg::Gds(isc_optimizer_blk_exc)); - // Msg442: size of optimizer block exceeded - } - - // Put conjunctions in opt structure. - // Note that it's a stack and we get the nodes in reversed order from the stack. - - opt->opt_conjuncts.grow(conjunct_count); - SSHORT nodeBase = -1, j = -1; - - for (SLONG i = conjunct_count; i > 0; i--, j--) - { - BoolExprNode* const node = conjunct_stack.pop(); - - if (i == base_count) - { - // The base conjunctions - j = base_count - 1; - nodeBase = 0; - } - else if (i == conjunct_count - distributed_count) - { - // The parent conjunctions - j = parent_count - 1; - nodeBase = opt->opt_base_parent_conjuncts; - } - else if (i == conjunct_count) - { - // The new conjunctions created by "distribution" from the stack - j = distributed_count - 1; - nodeBase = opt->opt_base_conjuncts; - } - - fb_assert(nodeBase >= 0 && j >= 0 && nodeBase + j < MAX_CONJUNCTS); - opt->opt_conjuncts[nodeBase + j].opt_conjunct_node = node; - } - - // Put the parent missing nodes on the stack - for (BoolExprNodeStack::iterator iter(missing_stack); - iter.hasData() && conjunct_count < MAX_CONJUNCTS; ++iter) - { - BoolExprNode* const node = iter.object(); - - opt->opt_conjuncts.grow(conjunct_count + 1); - opt->opt_conjuncts[conjunct_count].opt_conjunct_node = node; - conjunct_count++; - } - - // clear the csb_active flag of all streams in the RseNode - StreamList rseStreams; - rse->computeRseStreams(rseStreams); - - for (StreamList::iterator i = rseStreams.begin(); i != rseStreams.end(); ++i) - csb->csb_rpt[*i].deactivate(); - - // go through the record selection expression generating - // record source blocks for all streams - - NestConst* ptr = rse->rse_relations.begin(); - for (NestConst* const end = rse->rse_relations.end(); ptr != end; ++ptr) - { - const bool innerSubStream = (ptr != rse->rse_relations.begin()); - RecordSourceNode* const node = *ptr; - - opt->localStreams.clear(); - - fb_assert(sort == rse->rse_sorted); - fb_assert(aggregate == rse->rse_aggregate); - - // find the stream number and place it at the end of the beds array - // (if this is really a stream and not another RseNode) - - rsb = node->compile(tdbb, opt, innerSubStream); - - // if an rsb has been generated, we have a non-relation; - // so it forms a river of its own since it is separately - // optimized from the streams in this rsb - - if (rsb) - { - // AB: Save all outer-part streams - if (rse->rse_jointype == blr_inner || - (rse->rse_jointype == blr_left && !innerSubStream)) - { - rsb->findUsedStreams(opt->subStreams); - rsb->findUsedStreams(opt->outerStreams); - } - - const auto river = FB_NEW_POOL(*pool) River(csb, rsb, node, opt->localStreams); - river->deactivate(csb); - rivers.add(river); - } - else - { - // We have a relation, just add its stream - fb_assert(opt->beds.hasData()); - opt->outerStreams.add(opt->beds.back()); - } - } - - // this is an attempt to make sure we have a large enough cache to - // efficiently retrieve this query; make sure the cache has a minimum - // number of pages for each stream in the RseNode (the number is just a guess) - if (opt->compileStreams.getCount() > 5) - CCH_expand(tdbb, (ULONG) (opt->compileStreams.getCount() * CACHE_PAGES_PER_STREAM)); - - // At this point we are ready to start optimizing. - // We will use the opt block to hold information of - // a global nature, meaning that it needs to stick - // around for the rest of the optimization process. - - // attempt to optimize aggregates via an index, if possible - if (aggregate && !sort) - sort = aggregate; - else - rse->rse_aggregate = aggregate = NULL; - - // AB: Mark the previous used streams (sub-RseNode's) as active - for (StreamList::iterator i = opt->subStreams.begin(); i != opt->subStreams.end(); ++i) - csb->csb_rpt[*i].activate(); - - bool sortCanBeUsed = true; - SortNode* const orgSortNode = sort; - - // When DISTINCT and ORDER BY are done on different fields, - // and ORDER BY can be mapped to an index, then the records - // are returned in the wrong order because DISTINCT sort is - // performed after the navigational walk of the index. - // For that reason, we need to de-optimize this case so that - // ORDER BY does not use an index. - if (sort && project) - { - sort = NULL; - sortCanBeUsed = false; - } - - // outer joins require some extra processing - if (rse->rse_jointype != blr_inner) - rsb = gen_outer(tdbb, opt, rse, rivers, &sort); - else - { - // AB: If previous rsb's are already on the stack we can't use - // a navigational-retrieval for an ORDER BY because the next - // streams are JOINed to the previous ones - if (rivers.hasData()) - { - sort = NULL; - sortCanBeUsed = false; - - // AB: We could already have multiple rivers at this - // point so try to do some hashing or sort/merging now. - while (gen_equi_join(tdbb, opt, rivers)) - ; - - // AB: Mark the previous used streams (sub-RseNode's) again - // as active, because a SORT/MERGE could reset the flags - for (StreamList::iterator i = opt->subStreams.begin(); i != opt->subStreams.end(); ++i) - csb->csb_rpt[*i].activate(); - } - - fb_assert(opt->compileStreams.getCount() != 1 || csb->csb_rpt[opt->compileStreams[0]].csb_relation != 0); - - while (true) - { - // AB: Determine which streams have an index relationship - // with the currently active rivers. This is needed so that - // no merge is made between a new cross river and the - // currently active rivers. Where in the new cross river - // a stream depends (index) on the active rivers. - StreamList dependent_streams, free_streams; - find_index_relationship_streams(tdbb, opt, opt->compileStreams, dependent_streams, free_streams); - - // If we have dependent and free streams then we can't rely on - // the sort node to be used for index navigation. - if (dependent_streams.getCount() && free_streams.getCount()) - { - sort = NULL; - sortCanBeUsed = false; - } - - if (dependent_streams.getCount()) - { - // copy free streams - opt->compileStreams.assign(free_streams); - - // Make rivers from the dependent streams - gen_join(tdbb, opt, dependent_streams, rivers, &sort, rse->rse_plan); - - // Generate one river which holds a cross join rsb between - // all currently available rivers - - rivers.add(FB_NEW_POOL(*pool) CrossJoin(csb, rivers)); - rivers.back()->activate(csb); - } - else - { - if (free_streams.getCount()) - { - // Deactivate streams from rivers on stack, because - // the remaining streams don't have any indexed relationship with them - for (River** iter = rivers.begin(); iter < rivers.end(); iter++) - (*iter)->deactivate(csb); - } - - break; - } - } - - // attempt to form joins in decreasing order of desirability - gen_join(tdbb, opt, opt->compileStreams, rivers, &sort, rse->rse_plan); - - // If there are multiple rivers, try some hashing or sort/merging - while (gen_equi_join(tdbb, opt, rivers)) - ; - - rsb = CrossJoin(csb, rivers).getRecordSource(); - - // Pick up any residual boolean that may have fallen thru the cracks - rsb = gen_residual_boolean(tdbb, opt, rsb); - } - - // Assign the sort node back if it wasn't used by the index navigation - if (orgSortNode && !sortCanBeUsed) - sort = orgSortNode; - - // if the aggregate was not optimized via an index, get rid of the - // sort and flag the fact to the calling routine - if (aggregate && sort) - { - rse->rse_aggregate = NULL; - sort = NULL; - } - - // check index usage in all the base streams to ensure - // that any user-specified access plan is followed - - for (StreamType i = 0; i < opt->compileStreams.getCount(); i++) - check_indices(&csb->csb_rpt[opt->compileStreams[i]]); - - if (project || sort) - { - // CVC: I'm not sure how to do this with Array in a clearer way. - // Please, once you agree with my changes or fix them, you can delete the comments. - // Eliminate any duplicate dbkey streams - const StreamType* const b_end = opt->beds.end(); - const StreamType* const k_end = opt->keyStreams.end(); - StreamType* k = opt->keyStreams.begin(); - for (const StreamType* p2 = k; p2 < k_end; ++p2) - { - const StreamType* q = opt->beds.begin(); - - while (q < b_end && *q != *p2) - q++; - - if (q >= b_end) - *k++ = *p2; - } - opt->keyStreams.shrink(k - opt->keyStreams.begin()); - - // Handle project clause, if present - if (project) - rsb = OPT_gen_sort(tdbb, opt->opt_csb, opt->beds, &opt->keyStreams, rsb, project, opt->favorFirstRows, true); - - // Handle sort clause if present - if (sort) - rsb = OPT_gen_sort(tdbb, opt->opt_csb, opt->beds, &opt->keyStreams, rsb, sort, opt->favorFirstRows, false); - } - - // Handle first and/or skip. The skip MUST (if present) - // appear in the rsb list AFTER the first. Since the gen_first and gen_skip - // functions add their nodes at the beginning of the rsb list we MUST call - // gen_skip before gen_first. - - if (rse->rse_skip) - rsb = FB_NEW_POOL(*pool) SkipRowsStream(csb, rsb, rse->rse_skip); - - if (rse->rse_first) - rsb = FB_NEW_POOL(*pool) FirstRowsStream(csb, rsb, rse->rse_first); - - if (rse->flags & RseNode::FLAG_WRITELOCK) - { - for (StreamType i = 0; i < opt->compileStreams.getCount(); ++i) - { - const StreamType loopStream = opt->compileStreams[i]; - CompilerScratch::csb_repeat* r = &csb->csb_rpt[loopStream]; - r->csb_flags |= csb_update; - - if (r->csb_relation) - { - CMP_post_access(tdbb, csb, r->csb_relation->rel_security_name, - r->csb_view ? r->csb_view->rel_id : 0, - SCL_update, SCL_object_table, r->csb_relation->rel_name); - } - } - } - - // release memory allocated for index descriptions - for (StreamType i = 0; i < opt->compileStreams.getCount(); ++i) - { - const StreamType loopStream = opt->compileStreams[i]; - delete csb->csb_rpt[loopStream].csb_idx; - csb->csb_rpt[loopStream].csb_idx = NULL; - - // CVC: The following line added because OPT_compile is recursive, both directly - // and through gen_union(), too. Otherwise, we happen to step on deallocated memory - // and this is the cause of the crashes with indices that have plagued IB since v4. - - csb->csb_rpt[loopStream].csb_indices = 0; - } - -#ifdef OPT_DEBUG - if (opt_debug_file) - { - fflush(opt_debug_file); - //fclose(opt_debug_file); - //opt_debug_file = 0; - } -#endif - - } // try - catch (const Exception&) - { - for (StreamType i = 0; i < opt->compileStreams.getCount(); ++i) - { - const StreamType loopStream = opt->compileStreams[i]; - delete csb->csb_rpt[loopStream].csb_idx; - csb->csb_rpt[loopStream].csb_idx = NULL; - csb->csb_rpt[loopStream].csb_indices = 0; // Probably needed to be safe - } - - throw; - } - - return rsb; -} - - -// Prepare relation and its indices for optimization. -void OPT_compile_relation(thread_db* tdbb, jrd_rel* relation, CompilerScratch* csb, - StreamType stream, bool needIndices) -{ - CompilerScratch::csb_repeat* const tail = &csb->csb_rpt[stream]; - RelationPages* const relPages = relation->getPages(tdbb); - - if (needIndices && !relation->rel_file && !relation->isVirtual()) - { - tail->csb_indices = BTR_all(tdbb, relation, &tail->csb_idx, relPages); - - if (tail->csb_plan) - mark_indices(tail, relation->rel_id); - else - sort_indices_by_selectivity(tail); - } - else - tail->csb_indices = 0; - - tail->csb_cardinality = - get_cardinality(tdbb, relation, CMP_format(tdbb, csb, stream)); -} - -// Add node (ValueExprNode) to stack unless node is already on stack. -static bool augment_stack(ValueExprNode* node, ValueExprNodeStack& stack) -{ -/************************************** - * - * a u g m e n t _ s t a c k - * - ************************************** - * - * Functional description - * - **************************************/ - - for (ValueExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) - { - if (node_equality(node, temp.object())) - return false; - } - - stack.push(node); - - return true; -} - -// Add node (BoolExprNode) to stack unless node is already on stack. -static bool augment_stack(BoolExprNode* node, BoolExprNodeStack& stack) -{ - for (BoolExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) - { - if (node_equality(node, temp.object())) - return false; - } - - stack.push(node); - - return true; -} - - -static void check_indices(const CompilerScratch::csb_repeat* csb_tail) -{ -/************************************** - * - * c h e c k _ i n d i c e s - * - ************************************** - * - * Functional description - * Check to make sure that the user-specified - * indices were actually utilized by the optimizer. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - - const PlanNode* plan = csb_tail->csb_plan; - if (!plan) - return; - - if (plan->type != PlanNode::TYPE_RETRIEVE) - return; - - const jrd_rel* relation = csb_tail->csb_relation; - - // if there were no indices fetched at all but the - // user specified some, error out using the first index specified - - if (!csb_tail->csb_indices && plan->accessType && !tdbb->getAttachment()->isGbak()) - { - // index %s cannot be used in the specified plan - ERR_post(Arg::Gds(isc_index_unused) << plan->accessType->items[0].indexName); - } - - // check to make sure that all indices are either used or marked not to be used, - // and that there are no unused navigational indices - MetaName index_name; - - const index_desc* idx = csb_tail->csb_idx->items; - - for (USHORT i = 0; i < csb_tail->csb_indices; i++) - { - if (!(idx->idx_runtime_flags & (idx_plan_dont_use | idx_used)) || - ((idx->idx_runtime_flags & idx_plan_navigate) && !(idx->idx_runtime_flags & idx_navigate))) - { - if (relation) - MET_lookup_index(tdbb, index_name, relation->rel_name, (USHORT) (idx->idx_id + 1)); - else - index_name = ""; - - // index %s cannot be used in the specified plan - ERR_post(Arg::Gds(isc_index_unused) << Arg::Str(index_name)); - } - - ++idx; - } -} - - -static void check_sorts(CompilerScratch* csb, RseNode* rse) -{ -/************************************** - * - * c h e c k _ s o r t s - * - ************************************** - * - * Functional description - * Try to optimize out unnecessary sorting. - * - **************************************/ - DEV_BLKCHK(rse, type_nod); - - SortNode* sort = rse->rse_sorted; - SortNode* project = rse->rse_projection; - - // check if a GROUP BY exists using the same fields as the project or sort: - // if so, the projection can be eliminated; if no projection exists, then - // the sort can be eliminated. - - SortNode* group; - RecordSourceNode* sub_rse; - - if ((project || sort) && rse->rse_relations.getCount() == 1 && (sub_rse = rse->rse_relations[0]) && - nodeIs(sub_rse) && - (group = static_cast(sub_rse)->group)) - { - MapNode* const map = static_cast(sub_rse)->map; - - // if all the fields of the project are the same as all the fields - // of the group by, get rid of the project. - - if (project && (project->expressions.getCount() == group->expressions.getCount())) - { - NestConst* project_ptr = project->expressions.begin(); - const NestConst* const project_end = project->expressions.end(); - - for (; project_ptr != project_end; ++project_ptr) - { - const NestConst* group_ptr = group->expressions.begin(); - const NestConst* const group_end = group->expressions.end(); - - for (; group_ptr != group_end; ++group_ptr) - { - if (map_equal(*group_ptr, *project_ptr, map)) - break; - } - - if (group_ptr == group_end) - break; - } - - // we can now ignore the project, but in case the project is being done - // in descending order because of an order by, do the group by the same way. - if (project_ptr == project_end) - { - set_direction(project, group); - project = rse->rse_projection = NULL; - } - } - - // if there is no projection, then we can make a similar optimization - // for sort, except that sort may have fewer fields than group by. - - if (!project && sort && (sort->expressions.getCount() <= group->expressions.getCount())) - { - const size_t count = sort->expressions.getCount(); - const NestConst* sort_ptr = sort->expressions.begin(); - const NestConst* const sort_end = sort_ptr + count; - - for (; sort_ptr != sort_end; ++sort_ptr) - { - const NestConst* group_ptr = group->expressions.begin(); - const NestConst* const group_end = group_ptr + count; - - for (; group_ptr != group_end; ++group_ptr) - { - if (map_equal(*group_ptr, *sort_ptr, map)) - break; - } - - if (group_ptr == group_end) - break; - } - - // if all the fields in the sort list match the first n fields in the - // project list, we can ignore the sort, but update the sort order - // (ascending/descending) to match that in the sort list - - if (sort_ptr == sort_end) - { - set_direction(sort, group); - set_position(sort, group, static_cast(sub_rse)->map); - sort = rse->rse_sorted = NULL; - } - } - - } - - // examine the ORDER BY and DISTINCT clauses; if all the fields in the - // ORDER BY match the first n fields in the DISTINCT in any order, the - // ORDER BY can be removed, changing the fields in the DISTINCT to match - // the ordering of fields in the ORDER BY. - - if (sort && project && (sort->expressions.getCount() <= project->expressions.getCount())) - { - const size_t count = sort->expressions.getCount(); - const NestConst* sort_ptr = sort->expressions.begin(); - const NestConst* const sort_end = sort_ptr + count; - - for (; sort_ptr != sort_end; ++sort_ptr) - { - const NestConst* project_ptr = project->expressions.begin(); - const NestConst* const project_end = project_ptr + count; - - for (; project_ptr != project_end; ++project_ptr) - { - const FieldNode* sortField = nodeAs(*sort_ptr); - const FieldNode* projectField = nodeAs(*project_ptr); - - if (sortField && projectField && - sortField->fieldStream == projectField->fieldStream && - sortField->fieldId == projectField->fieldId) - { - break; - } - } - - if (project_ptr == project_end) - break; - } - - // if all the fields in the sort list match the first n fields - // in the project list, we can ignore the sort, but update - // the project to match the sort. - if (sort_ptr == sort_end) - { - set_direction(sort, project); - set_position(sort, project, NULL); - sort = rse->rse_sorted = NULL; - } - } - - // RP: optimize sort with OUTER JOIN - // if all the fields in the sort list are from one stream, check the stream is - // the most outer stream, if true update rse and ignore the sort - if (sort && !project) - { - StreamType sort_stream = 0; - bool usableSort = true; - NestConst* sort_ptr = sort->expressions.begin(); - const NestConst* const sort_end = sort->expressions.end(); - - for (; sort_ptr != sort_end; ++sort_ptr) - { - const FieldNode* sortField; - - if ((sortField = nodeAs(*sort_ptr))) - { - // Get stream for this field at this position. - const StreamType current_stream = sortField->fieldStream; - - // If this is the first position node, save this stream. - if (sort_ptr == sort->expressions.begin()) - sort_stream = current_stream; - else if (current_stream != sort_stream) - { - // If the current stream is different then the previous stream - // then we can't use this sort for an indexed order retrieval. - usableSort = false; - break; - } - } - else - { - // If this is not the first position node, reject this sort. - // Two expressions cannot be mapped to a single index. - if (sort_ptr > sort->expressions.begin()) - { - usableSort = false; - break; - } - - // This position doesn't use a simple field, thus we should - // check the expression internals. - SortedStreamList streams; - (*sort_ptr)->collectStreams(streams); - - // We can use this sort only if there's a single stream - // referenced by the expression. - if (streams.getCount() == 1) - sort_stream = streams[0]; - else - { - usableSort = false; - break; - } - } - } - - if (usableSort) - { - RseNode* new_rse = NULL; - RecordSourceNode* node = rse; - - while (node) - { - if (nodeIs(node)) - { - new_rse = static_cast(node); - - // AB: Don't distribute the sort when a FIRST/SKIP is supplied, - // because that will affect the behaviour from the deeper RSE. - // dimitr: the same rule applies to explicit/implicit user-defined sorts. - if (new_rse != rse && - (new_rse->rse_first || new_rse->rse_skip || - new_rse->rse_sorted || new_rse->rse_projection)) - { - node = NULL; - break; - } - - // Walk trough the relations of the RSE and see if a - // matching stream can be found. - if (new_rse->rse_jointype == blr_inner) - { - if (new_rse->rse_relations.getCount() == 1) - node = new_rse->rse_relations[0]; - else - { - bool sortStreamFound = false; - for (FB_SIZE_T i = 0; i < new_rse->rse_relations.getCount(); i++) - { - RecordSourceNode* subNode = new_rse->rse_relations[i]; - - if ((nodeIs(subNode) || nodeIs(subNode)) && - subNode->getStream() == sort_stream && - new_rse != rse) - { - // We have found the correct stream - sortStreamFound = true; - break; - } - } - - if (sortStreamFound) - { - // Set the sort to the found stream and clear the original sort - new_rse->rse_sorted = sort; - sort = rse->rse_sorted = NULL; - } - - node = NULL; - } - } - else if (new_rse->rse_jointype == blr_left) - node = new_rse->rse_relations[0]; - else - node = NULL; - } - else - { - if ((nodeIs(node) || nodeIs(node)) && - node->getStream() == sort_stream && - new_rse && new_rse != rse) - { - // We have found the correct stream, thus apply the sort here - new_rse->rse_sorted = sort; - sort = rse->rse_sorted = NULL; - } - - node = NULL; - } - } - } - } -} - - -static void class_mask(USHORT count, ValueExprNode** eq_class, ULONG* mask) -{ -/************************************** - * - * c l a s s _ m a s k - * - ************************************** - * - * Functional description - * Given an sort/merge join equivalence class (vector of node pointers - * of representative values for rivers), return a bit mask of rivers - * with values. - * - **************************************/ - if (count > MAX_CONJUNCTS) - { - ERR_post(Arg::Gds(isc_optimizer_blk_exc)); - // Msg442: size of optimizer block exceeded - } - - for (SLONG i = 0; i < OPT_STREAM_BITS; i++) - mask[i] = 0; - - for (SLONG i = 0; i < count; i++, eq_class++) - { - if (*eq_class) - { - SET_DEP_BIT(mask, i); - DEV_BLKCHK(*eq_class, type_nod); - } - } -} - - -static SLONG decompose(thread_db* tdbb, BoolExprNode* boolNode, BoolExprNodeStack& stack, - CompilerScratch* csb) -{ -/************************************** - * - * d e c o m p o s e - * - ************************************** - * - * Functional description - * Decompose a boolean into a stack of conjuctions. - * - **************************************/ - DEV_BLKCHK(csb, type_csb); - - BinaryBoolNode* binaryNode = nodeAs(boolNode); - ComparativeBoolNode* cmpNode = nodeAs(boolNode); - - if (binaryNode) - { - if (binaryNode->blrOp == blr_and) - { - SLONG count = decompose(tdbb, binaryNode->arg1, stack, csb); - count += decompose(tdbb, binaryNode->arg2, stack, csb); - return count; - } - else if (binaryNode->blrOp == blr_or) - { - BoolExprNodeStack or_stack; - - if (decompose(tdbb, binaryNode->arg1, or_stack, csb) >= 2) - { - binaryNode->arg1 = or_stack.pop(); - - while (or_stack.hasData()) - { - BinaryBoolNode* newBoolNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode( - csb->csb_pool, blr_and); - newBoolNode->arg1 = or_stack.pop(); - newBoolNode->arg2 = binaryNode->arg1; - - binaryNode->arg1 = newBoolNode; - } - } - - or_stack.clear(); - - if (decompose(tdbb, binaryNode->arg2, or_stack, csb) >= 2) - { - binaryNode->arg2 = or_stack.pop(); - - while (or_stack.hasData()) - { - BinaryBoolNode* newBoolNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode( - csb->csb_pool, blr_and); - newBoolNode->arg1 = or_stack.pop(); - newBoolNode->arg2 = binaryNode->arg2; - - binaryNode->arg2 = newBoolNode; - } - } - } - } - else if (cmpNode) - { - // turn a between into (a greater than or equal) AND (a less than or equal) - - if (cmpNode->blrOp == blr_between) - { - ComparativeBoolNode* newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode( - csb->csb_pool, blr_geq); - newCmpNode->arg1 = cmpNode->arg1; - newCmpNode->arg2 = cmpNode->arg2; - - stack.push(newCmpNode); - - newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode(csb->csb_pool, blr_leq); - newCmpNode->arg1 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg1); - newCmpNode->arg2 = cmpNode->arg3; - - stack.push(newCmpNode); - - return 2; - } - - // turn a LIKE/SIMILAR into a LIKE/SIMILAR and a STARTING WITH, if it starts - // with anything other than a pattern-matching character - - ValueExprNode* arg; - - if ((cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar) && - (arg = optimize_like_similar(tdbb, csb, cmpNode))) - { - ComparativeBoolNode* newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode( - csb->csb_pool, blr_starting); - newCmpNode->arg1 = cmpNode->arg1; - newCmpNode->arg2 = arg; - - stack.push(newCmpNode); - stack.push(boolNode); - - return 2; - } - } - - stack.push(boolNode); - - return 1; -} - - -static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratch* csb, - USHORT base_count) -{ -/************************************** - * - * d i s t r i b u t e _ e q u a l i t i e s - * - ************************************** - * - * Functional description - * Given a stack of conjunctions, generate some simple - * inferences. In general, find classes of equalities, - * then find operations based on members of those classes. - * If we find any, generate additional conjunctions. In - * short: - * - * If (a == b) and (a $ c) --> (b $ c) for any - * operation '$'. - * - **************************************/ - - // dimitr: Dumb protection against too many injected conjuncts (see CORE-5381). - // Don't produce more additional conjuncts than we originally had - // (i.e. this routine should never more than double the number of conjuncts). - // Ideally, we need two separate limits here: - // 1) number of injected conjuncts (affects required impure size) - // 2) number of input conjuncts (affects search time inside this routine) - - if (base_count * 2 > MAX_CONJUNCTS) - return 0; - - ObjectsArray classes; - ObjectsArray::iterator eq_class; - - DEV_BLKCHK(csb, type_csb); - - // Zip thru stack of booleans looking for field equalities - - for (BoolExprNodeStack::iterator iter(org_stack); iter.hasData(); ++iter) - { - BoolExprNode* const boolean = iter.object(); - - if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) - continue; - - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - - if (!cmpNode || cmpNode->blrOp != blr_eql) - continue; - - ValueExprNode* const node1 = cmpNode->arg1; - if (!nodeIs(node1)) - continue; - - ValueExprNode* const node2 = cmpNode->arg2; - if (!nodeIs(node2)) - continue; - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - if (search_stack(node1, *eq_class)) - { - augment_stack(node2, *eq_class); - break; - } - else if (search_stack(node2, *eq_class)) - { - eq_class->push(node1); - break; - } - } - - if (eq_class == classes.end()) - { - ValueExprNodeStack& s = classes.add(); - s.push(node1); - s.push(node2); - eq_class = classes.back(); - } - } - - if (classes.isEmpty()) - return 0; - - // Make another pass looking for any equality relationships that may have crept - // in between classes (this could result from the sequence (A = B, C = D, B = C) - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - for (ValueExprNodeStack::const_iterator iter(*eq_class); iter.hasData(); ++iter) - { - for (ObjectsArray::iterator eq_class2(eq_class); - ++eq_class2 != classes.end();) - { - if (search_stack(iter.object(), *eq_class2)) - { - while (eq_class2->hasData()) - augment_stack(eq_class2->pop(), *eq_class); - } - } - } - } - - USHORT count = 0; - - // Start by making a pass distributing field equalities - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - if (eq_class->hasMore(2)) - { - for (ValueExprNodeStack::iterator outer(*eq_class); outer.hasData(); ++outer) - { - for (ValueExprNodeStack::iterator inner(outer); (++inner).hasData(); ) - { - if (count < base_count) - { - AutoPtr cmpNode(FB_NEW_POOL(csb->csb_pool) - ComparativeBoolNode(csb->csb_pool, blr_eql)); - cmpNode->arg1 = outer.object(); - cmpNode->arg2 = inner.object(); - - if (augment_stack(cmpNode, org_stack)) - { - count++; - cmpNode.release(); - } - } - } - } - } - } - - // Now make a second pass looking for non-field equalities - - for (BoolExprNodeStack::iterator iter(org_stack); iter.hasData(); ++iter) - { - BoolExprNode* const boolean = iter.object(); - ComparativeBoolNode* const cmpNode = nodeAs(boolean); - ValueExprNode* node1; - ValueExprNode* node2; - - if (cmpNode && - (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_gtr || cmpNode->blrOp == blr_geq || - cmpNode->blrOp == blr_leq || cmpNode->blrOp == blr_lss || - cmpNode->blrOp == blr_matching || cmpNode->blrOp == blr_containing || - cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar)) - { - node1 = cmpNode->arg1; - node2 = cmpNode->arg2; - } - else - continue; - - bool reverse = false; - - if (!nodeIs(node1)) - { - ValueExprNode* swap_node = node1; - node1 = node2; - node2 = swap_node; - reverse = true; - } - - if (!nodeIs(node1)) - continue; - - if (!nodeIs(node2) && !nodeIs(node2) && !nodeIs(node2)) - continue; - - for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) - { - if (search_stack(node1, *eq_class)) - { - for (ValueExprNodeStack::iterator temp(*eq_class); temp.hasData(); ++temp) - { - if (!node_equality(node1, temp.object()) && count < base_count) - { - ValueExprNode* arg1; - ValueExprNode* arg2; - - if (reverse) - { - arg1 = cmpNode->arg1; - arg2 = temp.object(); - } - else - { - arg1 = temp.object(); - arg2 = cmpNode->arg2; - } - - // From the conjuncts X(A,B) and A=C, infer the conjunct X(C,B) - AutoPtr newNode(make_inference_node(csb, boolean, arg1, arg2)); - - if (augment_stack(newNode, org_stack)) - { - ++count; - newNode.release(); - } - } - } - - break; - } - } - } - - return count; -} - - -static void find_index_relationship_streams(thread_db* tdbb, - OptimizerBlk* opt, - const StreamList& streams, - StreamList& dependent_streams, - StreamList& free_streams) -{ -/************************************** - * - * f i n d _ i n d e x _ r e l a t i o n s h i p _ s t r e a m s - * - ************************************** - * - * Functional description - * Find the streams that can use an index - * with the currently active streams. - * - **************************************/ - - DEV_BLKCHK(opt, type_opt); - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; - const StreamType* end_stream = streams.end(); - for (const StreamType* stream = streams.begin(); stream < end_stream; stream++) - { - CompilerScratch::csb_repeat* const csb_tail = &csb->csb_rpt[*stream]; - - // Set temporary active flag for this stream - csb_tail->activate(); - - bool indexed_relationship = false; - - if (opt->opt_conjuncts.getCount()) - { - // Calculate the inversion for this stream. - // The returning candidate contains the streams that will be used for - // index retrieval. This meant that if some stream is used this stream - // depends on already active streams and can not be used in a separate - // SORT/MERGE. - - OptimizerRetrieval optimizerRetrieval(*tdbb->getDefaultPool(), opt, *stream, - false, false, NULL); - AutoPtr candidate(optimizerRetrieval.getCost()); - - if (candidate->dependentFromStreams.hasData()) - { - indexed_relationship = true; - } - } - - if (indexed_relationship) - { - dependent_streams.add(*stream); - } - else - { - free_streams.add(*stream); - } - - // Reset active flag - csb_tail->deactivate(); - } -} - - -static void form_rivers(thread_db* tdbb, - OptimizerBlk* opt, - const StreamList& streams, - RiverList& river_list, - SortNode** sort_clause, - PlanNode* plan_clause) -{ -/************************************** - * - * f o r m _ r i v e r s - * - ************************************** - * - * Functional description - * Form streams into rivers according - * to the user-specified plan. - * - **************************************/ - SET_TDBB(tdbb); - DEV_BLKCHK(opt, type_opt); - - StreamList temp; - - // this must be a join or a merge node, so go through - // the substreams and place them into the temp vector - // for formation into a river. - PlanNode* plan_node = NULL; - NestConst* ptr = plan_clause->subNodes.begin(); - - for (const NestConst* const end = plan_clause->subNodes.end(); ptr != end; ++ptr) - { - plan_node = *ptr; - - if (plan_node->type == PlanNode::TYPE_JOIN) - { - form_rivers(tdbb, opt, streams, river_list, sort_clause, plan_node); - continue; - } - - // at this point we must have a retrieval node, so put - // the stream into the river. - fb_assert(plan_node->type == PlanNode::TYPE_RETRIEVE); - - const StreamType stream = plan_node->relationNode->getStream(); - - // dimitr: the plan may contain more retrievals than the "streams" - // array (some streams could already be joined to the active - // rivers), so we populate the "temp" array only with the - // streams that appear in both the plan and the "streams" - // array. - - const StreamType* ptr_stream = streams.begin(); - const StreamType* const end_stream = streams.end(); - - while (ptr_stream < end_stream) - { - if (*ptr_stream++ == stream) - { - temp.add(stream); - break; - } - } - } - - // just because the user specified a join does not mean that - // we are able to form a river; thus form as many rivers out - // of the join are as necessary to exhaust the streams. - // AB: Only form rivers when any retrieval node is seen, for - // example a MERGE on two JOINs will come with no retrievals - // at this point. - // CVC: Notice "plan_node" is pointing to the last element in the loop above. - // If the loop didn't execute, we had garbage in "plan_node". - - if (temp.getCount() != 0) - { - OptimizerInnerJoin innerJoin(*tdbb->getDefaultPool(), opt, temp, - (sort_clause ? *sort_clause : NULL), plan_clause); - - StreamType count; - do { - count = innerJoin.findJoinOrder(); - } while (form_river(tdbb, opt, count, streams.getCount(), temp, river_list, sort_clause)); - } -} - - -static bool form_river(thread_db* tdbb, - OptimizerBlk* opt, - StreamType count, - size_t stream_count, - StreamList& temp, - RiverList& river_list, - SortNode** sort_clause) -{ -/************************************** - * - * f o r m _ r i v e r - * - ************************************** - * - * Functional description - * Form streams into rivers (combinations of streams). - * - **************************************/ - fb_assert(count); - - DEV_BLKCHK(opt, type_opt); - DEV_BLKCHK(plan_clause, type_nod); - - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; - - HalfStaticArray rsbs; - rsbs.resize(count); - RecordSource** ptr = rsbs.begin(); - - StreamList streams; - streams.resize(count); - StreamType* stream = streams.begin(); - - if (count != stream_count) - sort_clause = NULL; - - const OptimizerBlk::opt_stream* const opt_end = opt->opt_streams.begin() + count; - for (OptimizerBlk::opt_stream* tail = opt->opt_streams.begin(); - tail < opt_end; tail++, stream++, ptr++) - { - *stream = tail->opt_best_stream; - *ptr = gen_retrieval(tdbb, opt, *stream, sort_clause, false, false, NULL); - sort_clause = NULL; - } - - RecordSource* const rsb = (count == 1) ? rsbs[0] : - FB_NEW_POOL(*tdbb->getDefaultPool()) NestedLoopJoin(csb, count, rsbs.begin()); - - // Allocate a river block and move the best order into it - River* const river = FB_NEW_POOL(*tdbb->getDefaultPool()) River(csb, rsb, NULL, streams); - river->deactivate(csb); - river_list.push(river); - - stream = temp.begin(); - const StreamType* const end_stream = temp.end(); - - fb_assert(temp.getCount() >= count); - temp.shrink(temp.getCount() - count); - if (!temp.getCount()) - return false; - - // Reform "temp" from streams not consumed - for (const StreamType* t2 = stream; t2 < end_stream; t2++) - { - bool used = false; - - for (OptimizerBlk::opt_stream* tail = opt->opt_streams.begin(); tail < opt_end; tail++) - { - if (*t2 == tail->opt_best_stream) - { - used = true; - break; - } - } - - if (!used) - *stream++ = *t2; - } - - return true; -} - - -// Generate a separate AggregateSort (Aggregate SortedStream Block) for each distinct operation. -// Note that this should be optimized to use indices if possible. -void OPT_gen_aggregate_distincts(thread_db* tdbb, CompilerScratch* csb, MapNode* map) -{ - DSC descriptor; - DSC* desc = &descriptor; - NestConst* ptr = map->sourceList.begin(); - - for (const NestConst* const end = map->sourceList.end(); ptr != end; ++ptr) - { - ValueExprNode* from = *ptr; - AggNode* aggNode = nodeAs(from); - - if (aggNode && aggNode->distinct) - { - // Build the sort key definition. Turn cstrings into varying text. - aggNode->arg->getDesc(tdbb, csb, desc); - - if (desc->dsc_dtype == dtype_cstring) - { - desc->dsc_dtype = dtype_varying; - desc->dsc_length++; - } - - AggregateSort* asb = FB_NEW_POOL(*tdbb->getDefaultPool()) AggregateSort( - *tdbb->getDefaultPool()); - asb->intl = desc->isText() && desc->getTextType() != ttype_none && - desc->getTextType() != ttype_binary && desc->getTextType() != ttype_ascii; - - sort_key_def* sort_key = asb->keyItems.getBuffer(asb->intl ? 2 : 1); - sort_key->setSkdOffset(); - - if (asb->intl) - { - const USHORT key_length = ROUNDUP(INTL_key_length(tdbb, - INTL_TEXT_TO_INDEX(desc->getTextType()), desc->getStringLength()), sizeof(SINT64)); - - sort_key->setSkdLength(SKD_bytes, key_length); - sort_key->skd_flags = SKD_ascending; - sort_key->skd_vary_offset = 0; - - ++sort_key; - sort_key->setSkdOffset(&sort_key[-1]); - asb->length = sort_key->getSkdOffset(); - } - - fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); - sort_key->setSkdLength(sort_dtypes[desc->dsc_dtype], desc->dsc_length); - - if (!sort_key->skd_dtype) - ERR_post(Arg::Gds(isc_invalid_sort_datatype) << Arg::Str(DSC_dtype_tostring(desc->dsc_dtype))); - - if (desc->dsc_dtype == dtype_varying) - { - // allocate space to store varying length - sort_key->skd_vary_offset = sort_key->getSkdOffset() + ROUNDUP(desc->dsc_length, sizeof(SLONG)); - asb->length = sort_key->skd_vary_offset + sizeof(USHORT); - } - else - asb->length += sort_key->getSkdLength(); - - asb->length = ROUNDUP(asb->length, sizeof(SLONG)); - // dimitr: allocate an extra longword for the purely artificial counter, - // see AggNode::aggPass() for details; the length remains rounded properly - asb->length += sizeof(ULONG); - - sort_key->skd_flags = SKD_ascending; - asb->impure = csb->allocImpure(); - asb->desc = *desc; - - aggNode->asb = asb; - } - } -} - - -static void gen_join(thread_db* tdbb, - OptimizerBlk* opt, - const StreamList& streams, - RiverList& river_list, - SortNode** sort_clause, - PlanNode* plan_clause) -{ -/************************************** - * - * g e n _ j o i n - * - ************************************** - * - * Functional description - * Find all indexed relationships between streams, - * then form streams into rivers (combinations of - * streams). - * - **************************************/ - DEV_BLKCHK(opt, type_opt); - SET_TDBB(tdbb); - - if (!streams.getCount()) - return; - - if (plan_clause && streams.getCount() > 1) - { - // this routine expects a join/merge - form_rivers(tdbb, opt, streams, river_list, sort_clause, plan_clause); - return; - } - - OptimizerInnerJoin innerJoin(*tdbb->getDefaultPool(), opt, streams, - (sort_clause ? *sort_clause : NULL), plan_clause); - - StreamList temp; - temp.assign(streams); - - StreamType count; - do { - count = innerJoin.findJoinOrder(); - } while (form_river(tdbb, opt, count, streams.getCount(), temp, river_list, sort_clause)); -} - - -static RecordSource* gen_outer(thread_db* tdbb, OptimizerBlk* opt, RseNode* rse, - RiverList& river_list, SortNode** sort_clause) -{ -/************************************** - * - * g e n _ o u t e r - * - ************************************** - * - * Functional description - * Generate a top level outer join. The "outer" and "inner" - * sub-streams must be handled differently from each other. - * The inner is like other streams. The outer stream isn't - * because conjuncts may not eliminate records from the - * stream. They only determine if a join with an inner - * stream record is to be attempted. - * - **************************************/ - struct { - RecordSource* stream_rsb; - StreamType stream_num; - } stream_o, stream_i, *stream_ptr[2]; - - DEV_BLKCHK(opt, type_opt); - DEV_BLKCHK(rse, type_nod); - SET_TDBB(tdbb); - - // Determine which stream should be outer and which is inner. - // In the case of a left join, the syntactically left stream is the - // outer, and the right stream is the inner. For all others, swap - // the sense of inner and outer, though for a full join it doesn't - // matter and we should probably try both orders to see which is - // more efficient. - if (rse->rse_jointype != blr_left) - { - stream_ptr[1] = &stream_o; - stream_ptr[0] = &stream_i; - } - else - { - stream_ptr[0] = &stream_o; - stream_ptr[1] = &stream_i; - } - - // Loop through the outer join sub-streams in - // reverse order because rivers may have been PUSHed - for (int i = 1; i >= 0; i--) - { - const RecordSourceNode* node = rse->rse_relations[i]; - - if (nodeIs(node) || nodeIs(node)) - { - stream_ptr[i]->stream_rsb = NULL; - stream_ptr[i]->stream_num = node->getStream(); - } - else - { - River* const river = river_list.pop(); - stream_ptr[i]->stream_rsb = river->getRecordSource(); - } - } - - CompilerScratch* const csb = opt->opt_csb; - - const bool isFullJoin = (rse->rse_jointype == blr_full); - - if (!isFullJoin) - { - // Generate rsbs for the sub-streams. - // For the left sub-stream we also will get a boolean back. - BoolExprNode* boolean = NULL; - - if (!stream_o.stream_rsb) - { - stream_o.stream_rsb = gen_retrieval(tdbb, opt, stream_o.stream_num, sort_clause, - true, false, &boolean); - } - - if (!stream_i.stream_rsb) - { - // AB: the sort clause for the inner stream of an OUTER JOIN - // should never be used for the index retrieval - stream_i.stream_rsb = - gen_retrieval(tdbb, opt, stream_i.stream_num, NULL, false, true, NULL); - } - - // generate a parent boolean rsb for any remaining booleans that - // were not satisfied via an index lookup - stream_i.stream_rsb = gen_residual_boolean(tdbb, opt, stream_i.stream_rsb); - - // Allocate and fill in the rsb - return FB_NEW_POOL(*tdbb->getDefaultPool()) - NestedLoopJoin(csb, stream_o.stream_rsb, stream_i.stream_rsb, - boolean, OUTER_JOIN); - } - - bool hasOuterRsb = true, hasInnerRsb = true; - BoolExprNode* boolean = NULL; - - if (!stream_o.stream_rsb) - { - hasOuterRsb = false; - stream_o.stream_rsb = - gen_retrieval(tdbb, opt, stream_o.stream_num, NULL, true, false, &boolean); - } - - if (!stream_i.stream_rsb) - { - hasInnerRsb = false; - stream_i.stream_rsb = - gen_retrieval(tdbb, opt, stream_i.stream_num, NULL, false, true, NULL); - } - - RecordSource* const innerRsb = gen_residual_boolean(tdbb, opt, stream_i.stream_rsb); - - RecordSource* const rsb1 = FB_NEW_POOL(*tdbb->getDefaultPool()) - NestedLoopJoin(csb, stream_o.stream_rsb, innerRsb, boolean, OUTER_JOIN); - - for (FB_SIZE_T i = 0; i < opt->opt_conjuncts.getCount(); i++) - { - if (opt->opt_conjuncts[i].opt_conjunct_flags & opt_conjunct_used) - { - BoolExprNode* const org_node = opt->opt_conjuncts[i].opt_conjunct_node; - opt->opt_conjuncts[i].opt_conjunct_node = CMP_clone_node_opt(tdbb, csb, org_node); - opt->opt_conjuncts[i].opt_conjunct_flags = 0; - } - } - - if (!hasInnerRsb) - csb->csb_rpt[stream_i.stream_num].deactivate(); - - if (!hasOuterRsb) - csb->csb_rpt[stream_o.stream_num].deactivate(); - - boolean = NULL; - - if (!hasInnerRsb) - { - stream_i.stream_rsb = - gen_retrieval(tdbb, opt, stream_i.stream_num, NULL, true, false, &boolean); - } - - if (!hasOuterRsb) - { - stream_o.stream_rsb = - gen_retrieval(tdbb, opt, stream_o.stream_num, NULL, false, false, NULL); - } - - RecordSource* const outerRsb = gen_residual_boolean(tdbb, opt, stream_o.stream_rsb); - - RecordSource* const rsb2 = FB_NEW_POOL(*tdbb->getDefaultPool()) - NestedLoopJoin(csb, stream_i.stream_rsb, outerRsb, boolean, ANTI_JOIN); - - return FB_NEW_POOL(*tdbb->getDefaultPool()) FullOuterJoin(csb, rsb1, rsb2); -} - - -static RecordSource* gen_residual_boolean(thread_db* tdbb, OptimizerBlk* opt, RecordSource* prior_rsb) -{ -/************************************** - * - * g e n _ r e s i d u a l _ b o o l e a n - * - ************************************** - * - * Functional description - * Pick up any residual boolean remaining, - * meaning those that have not been used - * as part of some join. These booleans - * must still be applied to the result stream. - * - **************************************/ - SET_TDBB(tdbb); - DEV_BLKCHK(opt, type_opt); - DEV_BLKCHK(prior_rsb, type_rsb); - - BoolExprNode* boolean = NULL; - const OptimizerBlk::opt_conjunct* const opt_end = - opt->opt_conjuncts.begin() + opt->opt_base_conjuncts; - - for (OptimizerBlk::opt_conjunct* tail = opt->opt_conjuncts.begin(); tail < opt_end; tail++) - { - BoolExprNode* node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used)) - { - compose(*tdbb->getDefaultPool(), &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - - return boolean ? - FB_NEW_POOL(*tdbb->getDefaultPool()) FilteredStream(opt->opt_csb, prior_rsb, boolean) : - prior_rsb; -} - - -static RecordSource* gen_retrieval(thread_db* tdbb, - OptimizerBlk* opt, - StreamType stream, - SortNode** sort_ptr, - bool outer_flag, - bool inner_flag, - BoolExprNode** return_boolean) -{ -/************************************** - * - * g e n _ r e t r i e v a l - * - ************************************** - * - * Functional description - * Compile and optimize a record selection expression into a - * set of record source blocks (rsb's). - * - **************************************/ - OptimizerBlk::opt_conjunct* tail; - - SET_TDBB(tdbb); - - DEV_BLKCHK(opt, type_opt); - - CompilerScratch* const csb = opt->opt_csb; - CompilerScratch::csb_repeat* const csb_tail = &csb->csb_rpt[stream]; - jrd_rel* const relation = csb_tail->csb_relation; - - fb_assert(relation); - - const string alias = OPT_make_alias(csb, stream); - csb_tail->activate(); - - // Time to find inversions. For each index on the relation - // match all unused booleans against the index looking for upper - // and lower bounds that can be computed by the index. When - // all unused conjunctions are exhausted, see if there is enough - // information for an index retrieval. If so, build up an - // inversion component of the boolean. - - // It's recalculated later. - const OptimizerBlk::opt_conjunct* opt_end = opt->opt_conjuncts.begin() + - (inner_flag ? opt->opt_base_missing_conjuncts : opt->opt_conjuncts.getCount()); - - RecordSource* rsb = NULL; - InversionNode* inversion = NULL; - BoolExprNode* condition = NULL; - Array dbkeyRanges; - - if (relation->rel_file) - { - // External table - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ExternalTableScan(csb, alias, stream, relation); - } - else if (relation->isVirtual()) - { - // Virtual table: monitoring or security - switch (relation->rel_id) - { - case rel_global_auth_mapping: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) GlobalMappingScan(csb, alias, stream, relation); - break; - - case rel_sec_users: - case rel_sec_user_attributes: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) UsersTableScan(csb, alias, stream, relation); - break; - - case rel_sec_db_creators: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) DbCreatorsScan(csb, alias, stream, relation); - break; - - case rel_time_zones: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) TimeZonesTableScan(csb, alias, stream, relation); - break; - - case rel_config: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ConfigTableScan(csb, alias, stream, relation); - break; - - case rel_keywords: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) KeywordsTableScan(csb, alias, stream, relation); - break; - - default: - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation); - break; - } - } - else - { - // Persistent table - OptimizerRetrieval optimizerRetrieval(*tdbb->getDefaultPool(), opt, stream, - outer_flag, inner_flag, - (sort_ptr ? *sort_ptr : NULL)); - AutoPtr candidate(optimizerRetrieval.getInversion()); - - if (candidate) - { - inversion = candidate->inversion; - condition = candidate->condition; - dbkeyRanges.assign(candidate->dbkeyRanges); - - // Just for safety sake, this condition must be already checked - // inside OptimizerRetrieval::matchOnIndexes() - - if (inversion && condition && - !condition->computable(csb, stream, false)) - { - fb_assert(false); - inversion = NULL; - condition = NULL; - dbkeyRanges.clear(); - } - } - - IndexTableScan* const nav_rsb = optimizerRetrieval.getNavigation(); - - if (nav_rsb) - { - if (sort_ptr) - *sort_ptr = NULL; - - nav_rsb->setInversion(inversion, condition); - - rsb = nav_rsb; - } - } - - if (outer_flag) - { - fb_assert(return_boolean); - // Now make another pass thru the outer conjuncts only, finding unused, - // computable booleans. When one is found, roll it into a final - // boolean and mark it used. - *return_boolean = NULL; - opt_end = opt->opt_conjuncts.begin() + opt->opt_base_conjuncts; - - for (tail = opt->opt_conjuncts.begin(); tail < opt_end; tail++) - { - BoolExprNode* node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - compose(*tdbb->getDefaultPool(), return_boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - } - - // Now make another pass thru the conjuncts finding unused, computable - // booleans. When one is found, roll it into a final boolean and mark - // it used. If a computable boolean didn't match against an index then - // mark the stream to denote unmatched booleans. - BoolExprNode* boolean = NULL; - opt_end = opt->opt_conjuncts.begin() + (inner_flag ? opt->opt_base_missing_conjuncts : opt->opt_conjuncts.getCount()); - tail = opt->opt_conjuncts.begin(); - - if (outer_flag) - tail += opt->opt_base_parent_conjuncts; - - for (; tail < opt_end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - // If inversion is available, utilize all conjuncts that refer to - // the stream being retrieved. Otherwise, utilize only conjuncts - // that are local to this stream. The remaining ones are left in piece - // as possible candidates for a merge/hash join. - - if ((inversion && node->containsStream(stream)) || - (!inversion && node->computable(csb, stream, true))) - { - compose(*tdbb->getDefaultPool(), &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - - if (!outer_flag && !(tail->opt_conjunct_flags & opt_conjunct_matched)) - csb_tail->csb_flags |= csb_unmatched; - } - } - } - - if (!rsb) - { - if (inversion && condition) - { - RecordSource* const rsb1 = - FB_NEW_POOL(*tdbb->getDefaultPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); - RecordSource* const rsb2 = - FB_NEW_POOL(*tdbb->getDefaultPool()) BitmapTableScan(csb, alias, stream, relation, inversion); - - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ConditionalStream(csb, rsb1, rsb2, condition); - } - else if (inversion) - { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) BitmapTableScan(csb, alias, stream, relation, inversion); - } - else - { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); - - if (boolean) - csb->csb_rpt[stream].csb_flags |= csb_unmatched; - } - } - - return boolean ? FB_NEW_POOL(*tdbb->getDefaultPool()) FilteredStream(csb, rsb, boolean) : rsb; -} - - -SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamList& streams, - const StreamList* dbkey_streams, RecordSource* prior_rsb, SortNode* sort, - bool refetch_flag, bool project_flag) -{ -/************************************** - * - * g e n _ s o r t - * - ************************************** - * - * Functional description - * Generate a record source block to handle either a sort or a project. - * The two case are virtual identical -- the only difference is that - * project eliminates duplicates. However, since duplicates are - * recognized and handled by sort, the JRD processing is identical. - * - **************************************/ - DEV_BLKCHK(prior_rsb, type_rsb); - SET_TDBB(tdbb); - - /* We already know the number of keys, but we also need to compute the - total number of fields, keys and non-keys, to be pumped thru sort. Starting - with the number of keys, count the other field referenced. Since a field - is often a key, check for overlap to keep the length of the sort record - down. */ - - /* Along with the record number, the transaction id of the - * record will also be stored in the sort file. This will - * be used to detect update conflict in read committed - * transactions. */ - - ULONG items = sort->expressions.getCount() + - 3 * streams.getCount() + 2 * (dbkey_streams ? dbkey_streams->getCount() : 0); - const NestConst* const end_node = sort->expressions.end(); - - // Collect all fields involved into the sort - - HalfStaticArray fields; - ULONG totalLength = 0; - - for (const auto stream : streams) - { - UInt32Bitmap::Accessor accessor(csb->csb_rpt[stream].csb_fields); - - if (accessor.getFirst()) - { - do - { - const auto id = accessor.current(); - - const auto format = CMP_format(tdbb, csb, stream); - const auto desc = &format->fmt_desc[id]; - - if (id >= format->fmt_count || desc->isUnknown()) - IBERROR(157); // msg 157 cannot sort on a field that does not exist - - fields.push(SortField(stream, id, desc)); - totalLength += desc->dsc_length; - - // If the field has already been mentioned as a sort key, don't bother to repeat it. - // Unless this key is computed/volatile and thus cannot be restored after sorting. - - for (auto expr : sort->expressions) - { - const auto fieldNode = nodeAs(expr); - - if (fieldNode && fieldNode->fieldStream == stream && fieldNode->fieldId == id) - { - if (!SortedStream::hasVolatileKey(desc)) - { - totalLength -= desc->dsc_length; - fields.pop(); - } - - break; - } - } - - } while (accessor.getNext()); - } - } - - auto fieldCount = fields.getCount(); - - // Unless refetching is requested explicitly (e.g. FIRST ROWS optimization mode), - // validate the sort record length against the configured threshold for inline storage - - if (!refetch_flag) - { - const auto dbb = tdbb->getDatabase(); - const auto threshold = dbb->dbb_config->getInlineSortThreshold(); - - refetch_flag = (totalLength > threshold); - } - - // Check for persistent fields to be excluded from the sort. - // If nothing is excluded, there's no point in the refetch mode. - - if (refetch_flag) - { - for (auto& item : fields) - { - const auto relation = csb->csb_rpt[item.stream].csb_relation; - - if (relation && - !relation->rel_file && - !relation->rel_view_rse && - !relation->isVirtual()) - { - item.desc = NULL; - --fieldCount; - } - } - - refetch_flag = (fieldCount != fields.getCount()); - } - - items += fieldCount; - - // Now that we know the number of items, allocate a sort map block. - SortedStream::SortMap* map = - FB_NEW_POOL(*tdbb->getDefaultPool()) SortedStream::SortMap(*tdbb->getDefaultPool()); - - if (project_flag) - map->flags |= SortedStream::FLAG_PROJECT; - - if (refetch_flag) - map->flags |= SortedStream::FLAG_REFETCH; - - if (sort->unique) - map->flags |= SortedStream::FLAG_UNIQUE; - - sort_key_def* prev_key = nullptr; - - // Loop thru sort keys building sort keys. Actually, to handle null values - // correctly, two sort keys are made for each field, one for the null flag - // and one for field itself. - - dsc descriptor; - - SortedStream::SortMap::Item* map_item = map->items.getBuffer(items); - sort_key_def* sort_key = map->keyItems.getBuffer(2 * sort->expressions.getCount()); - const SortDirection* direction = sort->direction.begin(); - const NullsPlacement* nullOrder = sort->nullOrder.begin(); - - for (NestConst* node_ptr = sort->expressions.begin(); - node_ptr != end_node; - ++node_ptr, ++nullOrder, ++direction, ++map_item) - { - // Pick up sort key expression. - - NestConst node = *node_ptr; - dsc* desc = &descriptor; - node->getDesc(tdbb, csb, desc); - - // Allow for "key" forms of International text to grow - if (IS_INTL_DATA(desc)) - { - // Turn varying text and cstrings into text. - - if (desc->dsc_dtype == dtype_varying) - { - desc->dsc_dtype = dtype_text; - desc->dsc_length -= sizeof(USHORT); - } - else if (desc->dsc_dtype == dtype_cstring) - { - desc->dsc_dtype = dtype_text; - desc->dsc_length--; - } - - desc->dsc_length = INTL_key_length(tdbb, INTL_INDEX_TYPE(desc), desc->dsc_length); - } - - // Make key for null flag - sort_key->setSkdLength(SKD_text, 1); - sort_key->setSkdOffset(prev_key); - - // Handle nulls placement - sort_key->skd_flags = SKD_ascending; - - // Have SQL-compliant nulls ordering for ODS11+ - if ((*nullOrder == NULLS_DEFAULT && *direction != ORDER_DESC) || *nullOrder == NULLS_FIRST) - sort_key->skd_flags |= SKD_descending; - - prev_key = sort_key++; - - // Make key for sort key proper - fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); - sort_key->setSkdLength(sort_dtypes[desc->dsc_dtype], desc->dsc_length); - sort_key->setSkdOffset(&sort_key[-1], desc); - sort_key->skd_flags = SKD_ascending; - if (*direction == ORDER_DESC) - sort_key->skd_flags |= SKD_descending; - - if (!sort_key->skd_dtype) - ERR_post(Arg::Gds(isc_invalid_sort_datatype) << Arg::Str(DSC_dtype_tostring(desc->dsc_dtype))); - - if (sort_key->skd_dtype == SKD_varying || sort_key->skd_dtype == SKD_cstring) - { - if (desc->dsc_ttype() == ttype_binary) - sort_key->skd_flags |= SKD_binary; - } - - if (SortedStream::hasVolatileKey(desc) && !refetch_flag) - sort_key->skd_flags |= SKD_separate_data; - - map_item->clear(); - map_item->node = node; - map_item->flagOffset = prev_key->getSkdOffset(); - map_item->desc = *desc; - map_item->desc.dsc_address = (UCHAR*)(IPTR) sort_key->getSkdOffset(); - - prev_key = sort_key++; - - FieldNode* fieldNode; - - if ( (fieldNode = nodeAs(node)) ) - { - map_item->stream = fieldNode->fieldStream; - map_item->fieldId = fieldNode->fieldId; - } - } - - fb_assert(prev_key); - ULONG map_length = prev_key ? ROUNDUP(prev_key->getSkdOffset() + prev_key->getSkdLength(), sizeof(SLONG)) : 0; - map->keyLength = map_length; - ULONG flag_offset = map_length; - map_length += fieldCount; - - // Now go back and process all to fields involved with the sort - - for (const auto& item : fields) - { - if (!item.desc) - continue; - - if (item.desc->dsc_dtype >= dtype_aligned) - map_length = FB_ALIGN(map_length, type_alignments[item.desc->dsc_dtype]); - - map_item->clear(); - map_item->fieldId = (SSHORT) item.id; - map_item->stream = item.stream; - map_item->flagOffset = flag_offset++; - map_item->desc = *item.desc; - map_item->desc.dsc_address = (UCHAR*)(IPTR) map_length; - map_length += item.desc->dsc_length; - map_item++; - } - - // Make fields for record numbers and transaction ids for all streams - - map_length = ROUNDUP(map_length, sizeof(SINT64)); - for (const auto stream : streams) - { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_int64; - desc->dsc_length = sizeof(SINT64); - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; - map_item++; - - map_item->clear(); - map_item->fieldId = SortedStream::ID_TRANS; - map_item->stream = stream; - desc = &map_item->desc; - desc->dsc_dtype = dtype_int64; - desc->dsc_length = sizeof(SINT64); - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; - map_item++; - } - - if (dbkey_streams && dbkey_streams->hasData()) - { - map_length = ROUNDUP(map_length, sizeof(SINT64)); - - for (const auto stream : *dbkey_streams) - { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_int64; - desc->dsc_length = sizeof(SINT64); - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; - map_item++; - } - - for (const auto stream : *dbkey_streams) - { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY_VALID; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = CS_BINARY; - desc->dsc_length = 1; - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; - map_item++; - } - } - - for (const auto stream : streams) - { - map_item->clear(); - map_item->fieldId = SortedStream::ID_DBKEY_VALID; - map_item->stream = stream; - dsc* desc = &map_item->desc; - desc->dsc_dtype = dtype_text; - desc->dsc_ttype() = CS_BINARY; - desc->dsc_length = 1; - desc->dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; - map_item++; - } - - fb_assert(map_item == map->items.end()); - fb_assert(sort_key == map->keyItems.end()); - - map_length = ROUNDUP(map_length, sizeof(SLONG)); - - // Make fields to store varying and cstring length - - for (auto& sortKey : map->keyItems) - { - fb_assert(sortKey.skd_dtype != 0); - - if (sortKey.skd_dtype == SKD_varying || sortKey.skd_dtype == SKD_cstring) - { - sortKey.skd_vary_offset = map_length; - map_length += sizeof(USHORT); - map->flags |= SortedStream::FLAG_KEY_VARY; - } - } - - if (map_length > MAX_SORT_RECORD) - { - ERR_post(Arg::Gds(isc_sort_rec_size_err) << Arg::Num(map_length)); - // Msg438: sort record size of %ld bytes is too big - } - - map->length = map_length; - - // That was most unpleasant. Never the less, it's done (except for the debugging). - // All that remains is to build the record source block for the sort. - return FB_NEW_POOL(*tdbb->getDefaultPool()) SortedStream(csb, prior_rsb, map); -} - - -static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_rivers) -{ -/************************************** - * - * g e n _ e q u i _ j o i n - * - ************************************** - * - * Functional description - * We've got a set of rivers that may or may not be amenable to - * a hash join or a sort/merge join, and it's time to find out. - * If there are, build an appropriate join RecordSource, - * push it on the rsb stack, and update rivers accordingly. - * If two or more rivers were successfully joined, return true. - * If the whole things is a moby no-op, return false. - * - **************************************/ - ULONG selected_rivers[OPT_STREAM_BITS], selected_rivers2[OPT_STREAM_BITS]; - ValueExprNode** eq_class; - DEV_BLKCHK(opt, type_opt); - SET_TDBB(tdbb); - - CompilerScratch* const csb = opt->opt_csb; - - // Count the number of "rivers" involved in the operation, then allocate - // a scratch block large enough to hold values to compute equality - // classes. - - const USHORT cnt = (USHORT) org_rivers.getCount(); - - if (cnt < 2) - return false; - - HalfStaticArray scratch; - scratch.grow(opt->opt_base_conjuncts * cnt); - ValueExprNode** classes = scratch.begin(); - - // Compute equivalence classes among streams. This involves finding groups - // of streams joined by field equalities. - - ValueExprNode** last_class = classes; - OptimizerBlk::opt_conjunct* tail = opt->opt_conjuncts.begin(); - const OptimizerBlk::opt_conjunct* const end = tail + opt->opt_base_conjuncts; - - for (; tail < end; tail++) - { - if (tail->opt_conjunct_flags & opt_conjunct_used) - continue; - - BoolExprNode* const node = tail->opt_conjunct_node; - ComparativeBoolNode* cmpNode = nodeAs(node); - - if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv)) - continue; - - ValueExprNode* node1 = cmpNode->arg1; - ValueExprNode* node2 = cmpNode->arg2; - - dsc result, desc1, desc2; - node1->getDesc(tdbb, csb, &desc1); - node2->getDesc(tdbb, csb, &desc2); - - // Ensure that arguments can be compared in the binary form - if (!CVT2_get_binary_comparable_desc(&result, &desc1, &desc2)) - continue; - - // Cast the arguments, if required - if (!DSC_EQUIV(&result, &desc1, true) || !DSC_EQUIV(&result, &desc2, true)) - { - if (!DSC_EQUIV(&result, &desc1, true)) - { - CastNode* cast = FB_NEW_POOL(*tdbb->getDefaultPool()) CastNode(*tdbb->getDefaultPool()); - cast->source = node1; - cast->castDesc = result; - cast->impureOffset = csb->allocImpure(); - node1 = cast; - } - - if (!DSC_EQUIV(&result, &desc2, true)) - { - CastNode* cast = FB_NEW_POOL(*tdbb->getDefaultPool()) CastNode(*tdbb->getDefaultPool()); - cast->source = node2; - cast->castDesc = result; - cast->impureOffset = csb->allocImpure(); - node2 = cast; - } - } - - USHORT number1 = 0; - - for (River** iter1 = org_rivers.begin(); iter1 < org_rivers.end(); iter1++, number1++) - { - River* const river1 = *iter1; - - if (!river1->isReferenced(node1)) - { - if (!river1->isReferenced(node2)) - continue; - - ValueExprNode* const temp = node1; - node1 = node2; - node2 = temp; - } - - USHORT number2 = number1 + 1; - - for (River** iter2 = iter1 + 1; iter2 < org_rivers.end(); iter2++, number2++) - { - River* const river2 = *iter2; - - if (river2->isReferenced(node2)) - { - for (eq_class = classes; eq_class < last_class; eq_class += cnt) - { - if (node_equality(node1, classes[number1]) || - node_equality(node2, classes[number2])) - { - break; - } - } - - eq_class[number1] = node1; - eq_class[number2] = node2; - - if (eq_class == last_class) - last_class += cnt; - } - } - } - } - - // Pick both a set of classes and a set of rivers on which to join. - // Obviously, if the set of classes is empty, return false - // to indicate that nothing could be done. - - USHORT river_cnt = 0; - HalfStaticArray selected_classes(cnt); - - for (eq_class = classes; eq_class < last_class; eq_class += cnt) - { - USHORT i = river_count(cnt, eq_class); - - if (i > river_cnt) - { - river_cnt = i; - selected_classes.shrink(0); - selected_classes.add(eq_class); - class_mask(cnt, eq_class, selected_rivers); - } - else - { - class_mask(cnt, eq_class, selected_rivers2); - - for (i = 0; i < OPT_STREAM_BITS; i++) - { - if ((selected_rivers[i] & selected_rivers2[i]) != selected_rivers[i]) - break; - } - - if (i == OPT_STREAM_BITS) - selected_classes.add(eq_class); - } - } - - if (!river_cnt) - return false; - - // AB: Deactivate currently all streams from every river, because we - // need to know which nodes are computable between the rivers used - // for the merge. - - StreamStateHolder stateHolder(csb); - stateHolder.deactivate(); - - HalfStaticArray rsbs; - HalfStaticArray keys; - - // Unconditionally disable merge joins in favor of hash joins. - // This is a temporary debugging measure. - bool prefer_merge_over_hash = false; - - // AB: Get the lowest river position from the rivers that are merged - - RiverList rivers_to_merge; - USHORT lowest_river_position = MAX_USHORT; - USHORT number = 0; - - for (River** iter = org_rivers.begin(); iter < org_rivers.end(); number++) - { - River* const river = *iter; - - if (!(TEST_DEP_BIT(selected_rivers, number))) - { - iter++; - continue; - } - - if (number < lowest_river_position) - lowest_river_position = number; - - rivers_to_merge.add(river); - org_rivers.remove(iter); - - // Apply local river booleans, if any - - RecordSource* rsb = river->applyLocalBoolean(opt); - - // Collect RSBs and keys to join - - SortNode* const key = FB_NEW_POOL(*tdbb->getDefaultPool()) SortNode(*tdbb->getDefaultPool()); - - if (prefer_merge_over_hash) - { - ValueExprNode*** selected_class; - - for (selected_class = selected_classes.begin(); - selected_class != selected_classes.end(); ++selected_class) - { - key->direction.add(ORDER_ASC); // Ascending sort - key->nullOrder.add(NULLS_DEFAULT); // Default nulls placement - key->expressions.add((*selected_class)[number]); - } - - StreamList streams; - streams.assign(river->getStreams()); - rsb = OPT_gen_sort(tdbb, opt->opt_csb, streams, NULL, rsb, key, opt->favorFirstRows, false); - } - else - { - ValueExprNode*** selected_class; - - for (selected_class = selected_classes.begin(); - selected_class != selected_classes.end(); ++selected_class) - { - key->expressions.add((*selected_class)[number]); - } - } - - // It seems that rivers are already sorted by their cardinality. - // For a hash join, we need to choose the smallest ones as inner sub-streams, - // hence we reverse the order when storing them in the temporary arrays. - - if (prefer_merge_over_hash) - { - rsbs.add(rsb); - keys.add(&key->expressions); - } - else - { - rsbs.insert(0, rsb); - keys.insert(0, &key->expressions); - } - } - - fb_assert(rsbs.getCount() == keys.getCount()); - - // Build a join stream - - RecordSource* rsb = NULL; - - if (prefer_merge_over_hash) - { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) - MergeJoin(csb, rsbs.getCount(), (SortedStream**) rsbs.begin(), keys.begin()); - } - else - { - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) - HashJoin(tdbb, csb, rsbs.getCount(), rsbs.begin(), keys.begin()); - } - - // Activate streams of all the rivers being merged - - for (River** iter = rivers_to_merge.begin(); iter < rivers_to_merge.end(); iter++) - (*iter)->activate(csb); - - // Pick up any boolean that may apply - - BoolExprNode* boolean = NULL; - - for (tail = opt->opt_conjuncts.begin(); tail < end; tail++) - { - BoolExprNode* const node = tail->opt_conjunct_node; - - if (!(tail->opt_conjunct_flags & opt_conjunct_used) && - !(node->nodFlags & ExprNode::FLAG_RESIDUAL) && - node->computable(csb, INVALID_STREAM, false)) - { - compose(*tdbb->getDefaultPool(), &boolean, node); - tail->opt_conjunct_flags |= opt_conjunct_used; - } - } - - if (boolean) - rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) FilteredStream(csb, rsb, boolean); - - River* const merged_river = FB_NEW_POOL(*tdbb->getDefaultPool()) River(csb, rsb, rivers_to_merge); - - org_rivers.insert(lowest_river_position, merged_river); - - return true; -} - - -static double get_cardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) -{ -/************************************** - * - * g e t _ c a r d i n a l i t y - * - ************************************** - * - * Functional description - * Return the estimated cardinality for - * the given relation. - * - **************************************/ - SET_TDBB(tdbb); - - if (relation->isVirtual()) - { - // Just a dumb estimation - return 100.0; - } - - if (relation->rel_file) - { - return EXT_cardinality(tdbb, relation); - } - - MET_post_existence(tdbb, relation); - const double cardinality = DPM_cardinality(tdbb, relation, format); - MET_release_existence(tdbb, relation); - - return cardinality; -} - - -static BoolExprNode* make_inference_node(CompilerScratch* csb, BoolExprNode* boolean, - ValueExprNode* arg1, ValueExprNode* arg2) -{ -/************************************** - * - * m a k e _ i n f e r e n c e _ n o d e - * - ************************************** - * - * Defined - * 1996-Jan-15 David Schnepper - * - * Functional description - * From the predicate, boolean, and infer a new - * predicate using arg1 & arg2 as the first two - * parameters to the predicate. - * - * This is used when the engine knows A(boolean); - fb_assert(cmpNode); // see our caller - - // Clone the input predicate - ComparativeBoolNode* newCmpNode = FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode( - csb->csb_pool, cmpNode->blrOp); - - // We may safely copy invariantness flag because - // (1) we only distribute field equalities - // (2) invariantness of second argument of STARTING WITH or LIKE is solely - // determined by its dependency on any of the fields - // If provisions above change the line below will have to be modified - newCmpNode->nodFlags = cmpNode->nodFlags; - - // Share impure area for cached invariant value used to hold pre-compiled - // pattern for new LIKE and CONTAINING algorithms. - // Proper cloning of impure area for this node would require careful accounting - // of new invariant dependencies - we avoid such hassles via using single - // cached pattern value for all node clones. This is faster too. - if (newCmpNode->nodFlags & ExprNode::FLAG_INVARIANT) - newCmpNode->impureOffset = cmpNode->impureOffset; - - // But substitute new values for some of the predicate arguments - newCmpNode->arg1 = CMP_clone_node_opt(tdbb, csb, arg1); - newCmpNode->arg2 = CMP_clone_node_opt(tdbb, csb, arg2); - - // Arguments after the first two are just cloned (eg: LIKE ESCAPE clause) - if (cmpNode->arg3) - newCmpNode->arg3 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg3); - - return newCmpNode; -} - - -static bool map_equal(const ValueExprNode* field1, const ValueExprNode* field2, const MapNode* map) -{ -/************************************** - * - * m a p _ e q u a l - * - ************************************** - * - * Functional description - * Test to see if two fields are equal, where the fields - * are in two different streams possibly mapped to each other. - * Order of the input fields is important. - * - **************************************/ - const FieldNode* fieldNode1 = nodeAs(field1); - const FieldNode* fieldNode2 = nodeAs(field2); - - if (!fieldNode1 || !fieldNode2) - return false; - - // look through the mapping and see if we can find an equivalence. - const NestConst* sourcePtr = map->sourceList.begin(); - const NestConst* targetPtr = map->targetList.begin(); - - for (const NestConst* const sourceEnd = map->sourceList.end(); - sourcePtr != sourceEnd; - ++sourcePtr, ++targetPtr) - { - const FieldNode* mapFrom = nodeAs(*sourcePtr); - const FieldNode* mapTo = nodeAs(*targetPtr); - - if (!mapFrom || !mapTo) - continue; - - if (fieldNode1->fieldStream != mapFrom->fieldStream || fieldNode1->fieldId != mapFrom->fieldId) - continue; - - if (fieldNode2->fieldStream != mapTo->fieldStream || fieldNode2->fieldId != mapTo->fieldId) - continue; - - return true; - } - - return false; -} - - -// Mark indices that were not included in the user-specified access plan. -static void mark_indices(CompilerScratch::csb_repeat* tail, SSHORT relationId) -{ - const PlanNode* const plan = tail->csb_plan; - - if (plan->type != PlanNode::TYPE_RETRIEVE) - return; - - // Go through each of the indices and mark it unusable - // for indexed retrieval unless it was specifically mentioned - // in the plan; also mark indices for navigational access. - - // If there were none indices, this is a sequential retrieval. - - index_desc* idx = tail->csb_idx->items; - - for (USHORT i = 0; i < tail->csb_indices; i++) - { - if (plan->accessType) - { - ObjectsArray::iterator arg = plan->accessType->items.begin(); - const ObjectsArray::iterator end = plan->accessType->items.end(); - - for (; arg != end; ++arg) - { - if (relationId != arg->relationId) - { - // index %s cannot be used in the specified plan - ERR_post(Arg::Gds(isc_index_unused) << arg->indexName); - } - - if (idx->idx_id == arg->indexId) - { - if (plan->accessType->type == PlanNode::AccessType::TYPE_NAVIGATIONAL && - arg == plan->accessType->items.begin()) - { - // dimitr: navigational access can use only one index, - // hence the extra check added (see the line above) - idx->idx_runtime_flags |= idx_plan_navigate; - } - else - { - // nod_indices - break; - } - } - } - - if (arg == end) - idx->idx_runtime_flags |= idx_plan_dont_use; - } - else - idx->idx_runtime_flags |= idx_plan_dont_use; - - ++idx; - } -} - - -// Test two field node pointers for symbolic equality. - -static bool node_equality(const ValueExprNode* node1, const ValueExprNode* node2) -{ - if (!node1 || !node2) - return false; - - if (node1->getType() != node2->getType()) - return false; - - if (node1 == node2) - return true; - - const FieldNode* fieldNode1 = nodeAs(node1); - const FieldNode* fieldNode2 = nodeAs(node2); - - if (fieldNode1 && fieldNode2) - { - return fieldNode1->fieldStream == fieldNode2->fieldStream && - fieldNode1->fieldId == fieldNode2->fieldId; - } - - return false; -} - -static bool node_equality(const BoolExprNode* node1, const BoolExprNode* node2) -{ - DEV_BLKCHK(node1, type_nod); - DEV_BLKCHK(node2, type_nod); - - if (!node1 || !node2) - return false; - - if (node1->getType() != node2->getType()) - return false; - - if (node1 == node2) - return true; - - const ComparativeBoolNode* cmpNode = nodeAs(node1); - const ComparativeBoolNode* cmpNode2 = nodeAs(node2); - - if (cmpNode && cmpNode2 && cmpNode->blrOp == cmpNode2->blrOp && - (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) - { - if (node_equality(cmpNode->arg1, cmpNode2->arg1) && - node_equality(cmpNode->arg2, cmpNode2->arg2)) - { - return true; - } - - if (node_equality(cmpNode->arg1, cmpNode2->arg2) && - node_equality(cmpNode->arg2, cmpNode2->arg1)) - { - return true; - } - } - - return false; -} - - -static ValueExprNode* optimize_like_similar(thread_db* tdbb, CompilerScratch* csb, ComparativeBoolNode* cmpNode) -{ -/************************************** - * - * o p t i m i z e _ l i k e _ s i m i l a r - * - ************************************** - * - * Functional description - * Optimize a LIKE/SIMILAR expression, if possible, - * into a "starting with" AND a "LIKE/SIMILAR". This - * will allow us to use the index for the - * starting with, and the LIKE/SIMILAR can just tag - * along for the ride. - * But on the ride it does useful work, consider - * match LIKE/SIMILAR "ab%c". This is optimized by adding - * AND starting_with "ab", but the LIKE/SIMILAR clause is - * still needed. - * - **************************************/ - SET_TDBB(tdbb); - - ValueExprNode* matchNode = cmpNode->arg1; - ValueExprNode* patternNode = cmpNode->arg2; - ValueExprNode* escapeNode = cmpNode->arg3; - - // if the pattern string or the escape string can't be - // evaluated at compile time, forget it - if (!nodeIs(patternNode) || (escapeNode && !nodeIs(escapeNode))) - return nullptr; - - dsc matchDesc; - matchNode->getDesc(tdbb, csb, &matchDesc); - - dsc* patternDesc = &nodeAs(patternNode)->litDesc; - dsc* escapeDesc = nullptr; - - if (escapeNode) - escapeDesc = &nodeAs(escapeNode)->litDesc; - - // if either is not a character expression, forget it - if ((matchDesc.dsc_dtype > dtype_any_text) || - (patternDesc->dsc_dtype > dtype_any_text) || - (escapeNode && escapeDesc->dsc_dtype > dtype_any_text)) - { - return nullptr; - } - - TextType* matchTextType = INTL_texttype_lookup(tdbb, INTL_TTYPE(&matchDesc)); - CharSet* matchCharset = matchTextType->getCharSet(); - TextType* patternTextType = INTL_texttype_lookup(tdbb, INTL_TTYPE(patternDesc)); - CharSet* patternCharset = patternTextType->getCharSet(); - - if (cmpNode->blrOp == blr_like) - { - UCHAR escape_canonic[sizeof(ULONG)]; - UCHAR first_ch[sizeof(ULONG)]; - ULONG first_len; - UCHAR* p; - USHORT p_count; - MoveBuffer escapeBuffer; - - // Get the escape character, if any - if (escapeNode) - { - // Ensure escape string is same character set as match string - p_count = MOV_make_string2(tdbb, escapeDesc, INTL_TTYPE(&matchDesc), &p, escapeBuffer); - - first_len = matchCharset->substring(p_count, p, sizeof(first_ch), first_ch, 0, 1); - matchTextType->canonical(first_len, p, sizeof(escape_canonic), escape_canonic); - } - - MoveBuffer patternBuffer; - p_count = MOV_make_string2(tdbb, patternDesc, INTL_TTYPE(&matchDesc), &p, patternBuffer); - - first_len = matchCharset->substring(p_count, p, sizeof(first_ch), first_ch, 0, 1); - - UCHAR first_canonic[sizeof(ULONG)]; - matchTextType->canonical(first_len, p, sizeof(first_canonic), first_canonic); - - const BYTE canWidth = matchTextType->getCanonicalWidth(); - - const UCHAR* matchOneChar = matchCharset->getSqlMatchOneLength() != 0 ? - matchTextType->getCanonicalChar(TextType::CHAR_SQL_MATCH_ONE) : nullptr; - const UCHAR* matchAnyChar = matchCharset->getSqlMatchAnyLength() != 0 ? - matchTextType->getCanonicalChar(TextType::CHAR_SQL_MATCH_ANY) : nullptr; - - // If the first character is a wildcard char, forget it. - if ((!escapeNode || memcmp(first_canonic, escape_canonic, canWidth) != 0) && - ((matchOneChar && memcmp(first_canonic, matchOneChar, canWidth) == 0) || - (matchAnyChar && memcmp(first_canonic, matchAnyChar, canWidth) == 0))) - { - return nullptr; - } - - // allocate a literal node to store the starting with string; - // assume it will be shorter than the pattern string - - LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); - literal->litDesc = *patternDesc; - UCHAR* q = literal->litDesc.dsc_address = FB_NEW_POOL(csb->csb_pool) UCHAR[literal->litDesc.dsc_length]; - - // Set the string length to point till the first wildcard character. - - HalfStaticArray patternCanonical; - ULONG patternCanonicalLen = p_count / matchCharset->minBytesPerChar() * canWidth; - - patternCanonicalLen = matchTextType->canonical(p_count, p, - patternCanonicalLen, patternCanonical.getBuffer(patternCanonicalLen)); - - for (const UCHAR* patternPtr = patternCanonical.begin(); patternPtr < patternCanonical.end(); ) - { - // if there are escape characters, skip past them and don't treat the next char as a wildcard - const UCHAR* patternPtrStart = patternPtr; - patternPtr += canWidth; - - if (escapeNode && (memcmp(patternPtrStart, escape_canonic, canWidth) == 0)) - { - // Check for Escape character at end of string - if (!(patternPtr < patternCanonical.end())) - break; - - patternPtrStart = patternPtr; - patternPtr += canWidth; - } - else if ((matchOneChar && memcmp(patternPtrStart, matchOneChar, canWidth) == 0) || - (matchAnyChar && memcmp(patternPtrStart, matchAnyChar, canWidth) == 0)) - { - break; - } - - q += patternCharset->substring(patternDesc->dsc_length, - patternDesc->dsc_address, - literal->litDesc.dsc_length - (q - literal->litDesc.dsc_address), q, - (patternPtrStart - patternCanonical.begin()) / canWidth, 1); - } - - literal->litDesc.dsc_length = q - literal->litDesc.dsc_address; - - return literal; - } - else - { - fb_assert(cmpNode->blrOp == blr_similar); - - MoveBuffer escapeBuffer; - UCHAR* escapeStart = nullptr; - ULONG escapeLen = 0; - - // Get the escape character, if any - if (escapeNode) - { - // Ensure escape string is same character set as match string - escapeLen = MOV_make_string2(tdbb, escapeDesc, INTL_TTYPE(&matchDesc), &escapeStart, escapeBuffer); - } - - MoveBuffer patternBuffer; - UCHAR* patternStart; - ULONG patternLen = MOV_make_string2(tdbb, patternDesc, INTL_TTYPE(&matchDesc), &patternStart, patternBuffer); - const auto patternEnd = patternStart + patternLen; - const UCHAR* patternPtr = patternStart; - - MoveBuffer prefixBuffer; - ULONG charLen = 0; - - while (IntlUtil::readOneChar(matchCharset, &patternPtr, patternEnd, &charLen)) - { - if (escapeNode && charLen == escapeLen && memcmp(patternPtr, escapeStart, escapeLen) == 0) - { - if (!IntlUtil::readOneChar(matchCharset, &patternPtr, patternEnd, &charLen) || - !((charLen == escapeLen && memcmp(patternPtr, escapeStart, escapeLen) == 0) || - (charLen == 1 && SimilarToRegex::isSpecialChar(*patternPtr)))) - { - // Invalid escape. - return nullptr; - } - } - else if (charLen == 1 && SimilarToRegex::isSpecialChar(*patternPtr)) - break; - - prefixBuffer.push(patternPtr, charLen); - } - - if (prefixBuffer.isEmpty()) - return nullptr; - - // Allocate a literal node to store the starting with string. - // Use the match text type as the pattern string is converted to it. - - LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); - literal->litDesc.makeText(prefixBuffer.getCount(), INTL_TTYPE(&matchDesc), - FB_NEW_POOL(csb->csb_pool) UCHAR[prefixBuffer.getCount()]); - memcpy(literal->litDesc.dsc_address, prefixBuffer.begin(), prefixBuffer.getCount()); - - return literal; - } -} - - -static USHORT river_count(USHORT count, ValueExprNode** eq_class) -{ -/************************************** - * - * r i v e r _ c o u n t - * - ************************************** - * - * Functional description - * Given an sort/merge join equivalence class (vector of node pointers - * of representative values for rivers), return the count of rivers - * with values. - * - **************************************/ - USHORT cnt = 0; - - for (USHORT i = 0; i < count; i++, eq_class++) - { - if (*eq_class) - { - cnt++; - DEV_BLKCHK(*eq_class, type_nod); - } - } - - return cnt; -} - - -static bool search_stack(const ValueExprNode* node, const ValueExprNodeStack& stack) -{ -/************************************** - * - * s e a r c h _ s t a c k - * - ************************************** - * - * Functional description - * Search a stack for the presence of a particular value. - * - **************************************/ - for (ValueExprNodeStack::const_iterator iter(stack); iter.hasData(); ++iter) - { - if (node_equality(node, iter.object())) - return true; - } - - return false; -} - - -static void set_direction(SortNode* fromClause, SortNode* toClause) -{ -/************************************** - * - * s e t _ d i r e c t i o n - * - ************************************** - * - * Functional description - * Update the direction of a GROUP BY, DISTINCT, or ORDER BY - * clause to the same direction as another clause. Do the same - * for the nulls placement flag. - * - **************************************/ - const size_t fromCount = fromClause->expressions.getCount(); - - fb_assert(fromCount <= toClause->expressions.getCount()); - fb_assert(fromCount == fromClause->direction.getCount() && - fromCount == fromClause->nullOrder.getCount()); - fb_assert(toClause->expressions.getCount() == toClause->direction.getCount() && - toClause->expressions.getCount() == toClause->nullOrder.getCount()); - - for (FB_SIZE_T i = 0; i < fromCount; ++i) - { - toClause->direction[i] = fromClause->direction[i]; - toClause->nullOrder[i] = fromClause->nullOrder[i]; - } -} - - -static void set_position(const SortNode* from_clause, SortNode* to_clause, const MapNode* map) -{ -/************************************** - * - * s e t _ p o s i t i o n - * - ************************************** - * - * Functional description - * Update the fields in a GROUP BY, DISTINCT, or ORDER BY - * clause to the same position as another clause, possibly - * using a mapping between the streams. - * - **************************************/ - DEV_BLKCHK(from_clause, type_nod); - - // Track the position in the from list with "to_swap", and find the corresponding - // field in the from list with "to_ptr", then swap the two fields. By the time - // we get to the end of the from list, all fields in the to list will be reordered. - - NestConst* to_swap = to_clause->expressions.begin(); - - // We need to process no more than the number of nodes in the "from" clause - - const size_t count = from_clause->expressions.getCount(); - fb_assert(count <= to_clause->expressions.getCount()); - - const NestConst* from_ptr = from_clause->expressions.begin(); - - for (const NestConst* const from_end = from_ptr + count; - from_ptr != from_end; ++from_ptr) - { - NestConst* to_ptr = to_clause->expressions.begin(); - - for (const NestConst* const to_end = to_ptr + count; - to_ptr != to_end; ++to_ptr) - { - const FieldNode* const fromField = nodeAs(*from_ptr); - const FieldNode* const toField = nodeAs(*to_ptr); - - if ((map && map_equal(*to_ptr, *from_ptr, map)) || - (!map && fromField && toField && - fromField->fieldStream == toField->fieldStream && - fromField->fieldId == toField->fieldId)) - { - ValueExprNode* swap = *to_swap; - *to_swap = *to_ptr; - *to_ptr = swap; - } - } - - ++to_swap; - } - -} - - -static void sort_indices_by_selectivity(CompilerScratch::csb_repeat* tail) -{ -/************************************** - * - * s o r t _ i n d i c e s _ b y _ s e l e c t i v i t y - * - ************************************** - * - * Functional description - * Sort indices based on their selectivity. Lowest selectivy as first, highest as last. - * - **************************************/ - - index_desc* selectedIdx = NULL; - Array idxSort(tail->csb_indices); - bool sameSelectivity = false; - - // Walk through the indices and sort them into into idxSort - // where idxSort[0] contains the lowest selectivity (best) and - // idxSort[csbTail->csb_indices - 1] the highest (worst) - - if (tail->csb_idx && (tail->csb_indices > 1)) - { - for (USHORT j = 0; j < tail->csb_indices; j++) - { - float selectivity = 1; // Maximum selectivity is 1 (when all keys are the same) - index_desc* idx = tail->csb_idx->items; - - for (USHORT i = 0; i < tail->csb_indices; i++) - { - // Prefer ASC indices in the case of almost the same selectivities - if (selectivity > idx->idx_selectivity) - sameSelectivity = ((selectivity - idx->idx_selectivity) <= 0.00001); - else - sameSelectivity = ((idx->idx_selectivity - selectivity) <= 0.00001); - - if (!(idx->idx_runtime_flags & idx_marker) && - (idx->idx_selectivity <= selectivity) && - !((idx->idx_flags & idx_descending) && sameSelectivity)) - { - selectivity = idx->idx_selectivity; - selectedIdx = idx; - } - - ++idx; - } - - // If no index was found than pick the first one available out of the list - if ((!selectedIdx) || (selectedIdx->idx_runtime_flags & idx_marker)) - { - idx = tail->csb_idx->items; - - for (USHORT i = 0; i < tail->csb_indices; i++) - { - if (!(idx->idx_runtime_flags & idx_marker)) - { - selectedIdx = idx; - break; - } - - ++idx; - } - } - - selectedIdx->idx_runtime_flags |= idx_marker; - idxSort.add(*selectedIdx); - } - - // Finally store the right order in cbs_tail->csb_idx - index_desc* idx = tail->csb_idx->items; - - for (USHORT j = 0; j < tail->csb_indices; j++) - { - idx->idx_runtime_flags &= ~idx_marker; - memcpy(idx, &idxSort[j], sizeof(index_desc)); - ++idx; - } - } -} diff --git a/src/jrd/opt_proto.h b/src/jrd/opt_proto.h deleted file mode 100644 index af6284dbd3..0000000000 --- a/src/jrd/opt_proto.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: opt_proto.h - * DESCRIPTION: Prototype header file for opt.cpp - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifndef JRD_OPT_PROTO_H -#define JRD_OPT_PROTO_H - -#include "../jrd/jrd.h" -#include "../jrd/btr.h" -#include "../jrd/rse.h" -#include "../jrd/lls.h" - -namespace Jrd { - class jrd_req; - class jrd_rel; - class RecordSource; - struct index_desc; - class CompilerScratch; - class OptimizerBlk; - class SortedStream; - class SortNode; - class MapNode; -} - -Firebird::string OPT_get_plan(Jrd::thread_db* tdbb, const Jrd::jrd_req* request, bool detailed); -Jrd::RecordSource* OPT_compile(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, - Jrd::RseNode* rse, Jrd::BoolExprNodeStack* parent_stack); -void OPT_compile_relation(Jrd::thread_db* tdbb, Jrd::jrd_rel* relation, Jrd::CompilerScratch* csb, - StreamType stream, bool needIndices); -void OPT_gen_aggregate_distincts(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, Jrd::MapNode* map); -Jrd::SortedStream* OPT_gen_sort(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, const Jrd::StreamList& streams, - const Jrd::StreamList* dbkey_streams, Jrd::RecordSource* prior_rsb, Jrd::SortNode* sort, - bool refetch_flag, bool project_flag); - -#endif // JRD_OPT_PROTO_H diff --git a/src/jrd/optimizer/InnerJoin.cpp b/src/jrd/optimizer/InnerJoin.cpp new file mode 100644 index 0000000000..6e9b49b3a8 --- /dev/null +++ b/src/jrd/optimizer/InnerJoin.cpp @@ -0,0 +1,609 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Arno Brinkman + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2004 Arno Brinkman + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Adriano dos Santos Fernandes + * Dmitry Yemanov + * + */ + +#include "firebird.h" + +#include "../jrd/jrd.h" +#include "../jrd/exe.h" +#include "../jrd/btr.h" +#include "../jrd/intl.h" +#include "../jrd/Collation.h" +#include "../jrd/ods.h" +#include "../jrd/RecordSourceNodes.h" +#include "../jrd/recsrc/RecordSource.h" +#include "../dsql/BoolNodes.h" +#include "../dsql/ExprNodes.h" +#include "../dsql/StmtNodes.h" +#include "../jrd/btr_proto.h" +#include "../jrd/cch_proto.h" +#include "../jrd/cmp_proto.h" +#include "../jrd/dpm_proto.h" +#include "../jrd/exe_proto.h" +#include "../jrd/ext_proto.h" +#include "../jrd/intl_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/mov_proto.h" +#include "../jrd/par_proto.h" + +#include "../jrd/optimizer/Optimizer.h" + +using namespace Firebird; +using namespace Jrd; + + +// +// Constructor +// + +InnerJoin::InnerJoin(thread_db* aTdbb, Optimizer* opt, + const StreamList& streams, + SortNode** sortClause, bool hasPlan) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), + optimizer(opt), + csb(opt->getCompilerScratch()), + sortPtr(sortClause), + plan(hasPlan), + innerStreams(getPool(), streams.getCount()), + joinedStreams(getPool()), + bestStreams(getPool()) +{ + joinedStreams.grow(streams.getCount()); + + for (const auto stream : streams) + innerStreams.add(FB_NEW_POOL(getPool()) StreamInfo(getPool(), stream)); + + calculateStreamInfo(); +} + + +// +// Calculate the needed information for all streams +// + +void InnerJoin::calculateStreamInfo() +{ + StreamList streams; + + // First get the base cost without any relation to any other inner join stream + +#ifdef OPT_DEBUG_RETRIEVAL + optimizer->printf("Base stream info:\n"); +#endif + + const auto sort = sortPtr ? *sortPtr : nullptr; + + for (auto innerStream : innerStreams) + { + streams.add(innerStream->stream); + csb->csb_rpt[innerStream->stream].activate(); + + Retrieval retrieval(tdbb, optimizer, innerStream->stream, false, false, sort, true); + const auto candidate = retrieval.getInversion(); + + innerStream->baseCost = candidate->cost; + innerStream->baseSelectivity = candidate->selectivity; + innerStream->baseIndexes = candidate->indexes; + innerStream->baseUnique = candidate->unique; + innerStream->baseNavigated = candidate->navigated; + + csb->csb_rpt[innerStream->stream].deactivate(); + } + + // Collect dependencies between every pair of streams + + for (const auto baseStream : streams) + { + csb->csb_rpt[baseStream].activate(); + + for (const auto innerStream : innerStreams) + { + const StreamType testStream = innerStream->stream; + + if (baseStream != testStream) + { + csb->csb_rpt[testStream].activate(); + getIndexedRelationships(innerStream); + csb->csb_rpt[testStream].deactivate(); + } + } + + csb->csb_rpt[baseStream].deactivate(); + } + + // Collect more complex inter-dependencies (involving three and more streams), if any + + if (streams.getCount() > 2) + { + StreamStateHolder stateHolder(csb, streams); + stateHolder.activate(); + + for (const auto innerStream : innerStreams) + getIndexedRelationships(innerStream); + } + + // Unless PLAN is enforced, sort the streams based on independency and cost + if (!plan && (innerStreams.getCount() > 1)) + { + StreamInfoList tempStreams; + + for (const auto innerStream : innerStreams) + { + FB_SIZE_T index = 0; + for (; index < tempStreams.getCount(); index++) + { + if (StreamInfo::cheaperThan(innerStream, tempStreams[index])) + break; + } + tempStreams.insert(index, innerStream); + } + + // Finally update the innerStreams with the sorted streams + innerStreams = tempStreams; + } +} + + +// +// Estimate the cost for the stream +// + +void InnerJoin::estimateCost(unsigned position, + const StreamInfo* stream, + double* cost, + double* resultingCardinality) const +{ + const auto sort = (position == 0 && sortPtr) ? *sortPtr : nullptr; + + // Create the optimizer retrieval generation class and calculate + // which indexes will be used and the total estimated selectivity will be returned + Retrieval retrieval(tdbb, optimizer, stream->stream, false, false, sort, true); + const auto candidate = retrieval.getInversion(); + + *cost = candidate->cost; + + // Calculate cardinality + const auto tail = &csb->csb_rpt[stream->stream]; + const double cardinality = tail->csb_cardinality * candidate->selectivity; + + *resultingCardinality = MAX(cardinality, MINIMUM_CARDINALITY); +} + + +// +// Find the best order out of the streams. First return a stream if it can't use +// an index based on a previous stream and it can't be used by another stream. +// Next loop through the remaining streams and find the best order. +// + +bool InnerJoin::findJoinOrder() +{ + bestStreams.clear(); + bestCount = 0; + remainingStreams = 0; + +#ifdef OPT_DEBUG + // Debug + printStartOrder(); +#endif + + int filters = 0, navigations = 0; + + for (const auto innerStream : innerStreams) + { + if (!innerStream->used) + { + remainingStreams++; + + const int currentFilter = innerStream->isFiltered() ? 1 : 0; + + if (navigations && currentFilter) + navigations = 0; + + filters += currentFilter; + + if (innerStream->baseNavigated && currentFilter == filters) + navigations++; + + if (innerStream->isIndependent()) + { + if (!bestCount || innerStream->baseCost < bestCost) + { + joinedStreams[0].bestStream = innerStream->stream; + bestCount = 1; + bestCost = innerStream->baseCost; + } + } + } + } + + if (bestCount == 0) + { + IndexedRelationships indexedRelationships; + + for (const auto innerStream : innerStreams) + { + if (!innerStream->used) + { + // If optimization for first rows has been requested and index navigations are + // possible, then consider only join orders starting with a navigational stream. + // Except cases when other streams have local predicates applied. + + const int currentFilter = innerStream->isFiltered() ? 1 : 0; + + if (!optimizer->favorFirstRows() || !navigations || + (innerStream->baseNavigated && currentFilter == filters)) + { + indexedRelationships.clear(); + findBestOrder(0, innerStream, indexedRelationships, 0.0, 1.0); + + if (plan) + { + // If a explicit PLAN was specified we should be ready; + break; + } + } + } + } + } + + // Mark streams as used + for (unsigned i = 0; i < bestCount; i++) + { + auto streamInfo = getStreamInfo(joinedStreams[i].bestStream); + streamInfo->used = true; + bestStreams.add(joinedStreams[i].bestStream); + } + +#ifdef OPT_DEBUG + // Debug + printBestOrder(); +#endif + + return bestStreams.hasData(); +} + + +// +// Make different combinations to find out the join order. +// For every position we start with the stream that has the best selectivity +// for that position. If we've have used up all our streams after that +// we assume we're done. +// + +void InnerJoin::findBestOrder(unsigned position, + StreamInfo* stream, + IndexedRelationships& processList, + double cost, + double cardinality) +{ + const auto tail = &csb->csb_rpt[stream->stream]; + + // Do some initializations + tail->activate(); + joinedStreams[position].number = stream->stream; + + // Save the various flag bits from the optimizer block to reset its + // state after each test + HalfStaticArray streamFlags(innerStreams.getCount()); + for (const auto innerStream : innerStreams) + streamFlags.add(innerStream->used); + + // Compute delta and total estimate cost to fetch this stream + double positionCost = 0, positionCardinality = 0, newCost = 0, newCardinality = 0; + + if (!plan) + { + estimateCost(position, stream, &positionCost, &positionCardinality); + newCost = cost + cardinality * positionCost; + newCardinality = positionCardinality * cardinality; + } + + position++; + + // If the partial order is either longer than any previous partial order, + // or the same length and cheap, save order as "best" + if (position > bestCount || (position == bestCount && newCost < bestCost)) + { + bestCount = position; + bestCost = newCost; + + const auto end = joinedStreams.begin() + position; + for (auto iter = joinedStreams.begin(); iter != end; ++iter) + { + auto& joinedStream = *iter; + joinedStream.bestStream = joinedStream.number; + } + } + +#ifdef OPT_DEBUG + // Debug information + printFoundOrder(position, positionCost, positionCardinality, newCost, newCardinality); +#endif + + // Mark this stream as "used" in the sense that it is already included + // in this particular proposed stream ordering + stream->used = true; + bool done = false; + + // If we've used up all the streams there's no reason to go any further + if (position == remainingStreams) + done = true; + + // If we know a combination with all streams used and the + // current cost is higher as the one from the best we're done + if (bestCount == remainingStreams && bestCost < newCost) + done = true; + + if (plan) + { + // If a explicit PLAN was specific pick the next relation. + // The order in innerStreams is expected to be exactly the order as + // specified in the explicit PLAN. + for (auto nextStream : innerStreams) + { + if (!nextStream->used) + { + findBestOrder(position, nextStream, processList, newCost, newCardinality); + break; + } + } + } + else if (!done) + { + // Add these relations to the processing list + for (auto& relationship : stream->indexedRelationships) + { + const auto relationStreamInfo = getStreamInfo(relationship.stream); + if (!relationStreamInfo->used) + { + bool found = false; + IndexRelationship* processRelationship = processList.begin(); + for (FB_SIZE_T index = 0; index < processList.getCount(); index++) + { + if (relationStreamInfo->stream == processRelationship[index].stream) + { + // If the cost of this relationship is cheaper then remove the + // old relationship and add this one + if (IndexRelationship::cheaperThan(relationship, processRelationship[index])) + { + processList.remove(index); + break; + } + + found = true; + break; + } + } + if (!found) + { + // Add relationship sorted on cost (cheapest as first) + processList.add(relationship); + } + } + } + + for (const auto& nextRelationship : processList) + { + auto relationStreamInfo = getStreamInfo(nextRelationship.stream); + if (!relationStreamInfo->used) + { + findBestOrder(position, relationStreamInfo, processList, newCost, newCardinality); + break; + } + } + } + + // Clean up from any changes made for compute the cost for this stream + tail->deactivate(); + for (FB_SIZE_T i = 0; i < streamFlags.getCount(); i++) + innerStreams[i]->used = streamFlags[i]; +} + + +// +// Form streams into rivers (combinations of streams) +// + +River* InnerJoin::formRiver() +{ + fb_assert(bestCount); + fb_assert(bestStreams.hasData()); + + if (bestStreams.getCount() != innerStreams.getCount()) + sortPtr = nullptr; + + HalfStaticArray rsbs; + + for (const auto stream : bestStreams) + { + const auto rsb = optimizer->generateRetrieval(stream, sortPtr, false, false); + rsbs.add(rsb); + sortPtr = nullptr; + } + + const auto rsb = (rsbs.getCount() == 1) ? rsbs[0] : + FB_NEW_POOL(getPool()) NestedLoopJoin(csb, rsbs.getCount(), rsbs.begin()); + + // Allocate a river block and move the best order into it + const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, nullptr, bestStreams); + river->deactivate(csb); + return river; +} + + +// +// Check if the testStream can use a index when the baseStream is active. If so +// then we create a indexRelationship and fill it with the needed information. +// The reference is added to the baseStream and the baseStream is added as previous +// expected stream to the testStream. +// + +void InnerJoin::getIndexedRelationships(StreamInfo* testStream) +{ +#ifdef OPT_DEBUG_RETRIEVAL + const auto name = optimizer->getStreamName(testStream->stream); + optimizer->printf("Dependencies for stream %u (%s):\n", + testStream->stream, name.c_str()); +#endif + + const auto tail = &csb->csb_rpt[testStream->stream]; + + Retrieval retrieval(tdbb, optimizer, testStream->stream, false, false, nullptr, true); + const auto candidate = retrieval.getInversion(); + + for (auto baseStream : innerStreams) + { + if (baseStream->stream != testStream->stream && + candidate->dependentFromStreams.exist(baseStream->stream)) + { + // If the base stream already depends on the testing stream, don't store it again + bool found = false; + for (const auto& relationship : baseStream->indexedRelationships) + { + if (relationship.stream == testStream->stream) + { + found = true; + break; + } + } + + if (found) + continue; + + // If we could use more conjunctions on the testing stream + // with the base stream active as without the base stream + // then the test stream has a indexed relationship with the base stream. + IndexRelationship indexRelationship; + indexRelationship.stream = testStream->stream; + indexRelationship.unique = candidate->unique; + indexRelationship.cost = candidate->cost; + indexRelationship.cardinality = candidate->unique ? + tail->csb_cardinality : tail->csb_cardinality * candidate->selectivity; + + // Relationships are kept sorted by cost and uniqueness in the array + baseStream->indexedRelationships.add(indexRelationship); + testStream->previousExpectedStreams++; + } + } +} + + +// +// Return stream information based on the stream number +// + +InnerJoin::StreamInfo* InnerJoin::getStreamInfo(StreamType stream) +{ + for (FB_SIZE_T i = 0; i < innerStreams.getCount(); i++) + { + if (innerStreams[i]->stream == stream) + return innerStreams[i]; + } + + // We should never come here + fb_assert(false); + return nullptr; +} + +#ifdef OPT_DEBUG +// Dump finally selected stream order +void InnerJoin::printBestOrder() const +{ + if (bestStreams.isEmpty()) + return; + + optimizer->printf(" best order, streams:"); + + const auto end = bestStreams.end(); + for (auto iter = bestStreams.begin(); iter != end; iter++) + { + const auto name = optimizer->getStreamName(*iter); + optimizer->printf(" %u (%s)", *iter, name.c_str()); + + if (iter != end - 1) + optimizer->printf(","); + } + + optimizer->printf("\n"); +} + +// Dump currently passed streams to a debug file +void InnerJoin::printFoundOrder(StreamType position, + double positionCost, + double positionCardinality, + double cost, + double cardinality) const +{ + for (auto i = position - 1; i > 0; i--) + optimizer->printf(" "); + + optimizer->printf(" #%2.2u, streams:", position); + + auto iter = joinedStreams.begin(); + const auto end = iter + position; + for (; iter < end; iter++) + { + const auto name = optimizer->getStreamName(iter->number); + optimizer->printf(" %u (%s)", iter->number, name.c_str()); + + if (iter != end - 1) + optimizer->printf(","); + } + + optimizer->printf("\n"); + + for (auto i = position - 1; i > 0; i--) + optimizer->printf(" "); + + optimizer->printf(" position cardinality (%10.2f), position cost (%10.2f),", positionCardinality, positionCost); + optimizer->printf(" cardinality (%10.2f), cost (%10.2f)", cardinality, cost); + + optimizer->printf("\n"); +} + +// Dump finally selected stream order +void InnerJoin::printStartOrder() const +{ + optimizer->printf("Start join order, streams:"); + + const auto end = innerStreams.end(); + for (auto iter = innerStreams.begin(); iter != end; iter++) + { + const auto innerStream = *iter; + if (!innerStream->used) + { + const auto name = optimizer->getStreamName(innerStream->stream); + optimizer->printf(" %u (%s) base cost (%1.2f)", + innerStream->stream, name.c_str(), innerStream->baseCost); + + if (iter != end - 1) + optimizer->printf(","); + } + } + + optimizer->printf("\n"); +} +#endif diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp new file mode 100644 index 0000000000..d34cc4aa9a --- /dev/null +++ b/src/jrd/optimizer/Optimizer.cpp @@ -0,0 +1,3228 @@ +/* + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * 2002.10.12: Nickolay Samofatov: Fixed problems with wrong results produced by + * outer joins + * 2001.07.28: John Bellardo: Added code to handle rse_skip nodes. + * 2001.07.17 Claudio Valderrama: Stop crash with indices and recursive calls + * of OPT_compile: indicator csb_indices set to zero after used memory is + * returned to the free pool. + * 2001.02.15: Claudio Valderrama: Don't obfuscate the plan output if a selectable + * stored procedure doesn't access tables, views or other procedures directly. + * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port + * 2002.10.30: Arno Brinkman: Changes made to gen_retrieval, OPT_compile and make_inversion. + * Procedure sort_indices added. The changes in gen_retrieval are that now + * an index with high field-count has priority to build an index from. + * Procedure make_inversion is changed so that it not pick every index + * that comes away, this was slow performance with bad selectivity indices + * which most are foreign_keys with a reference to a few records. + * 2002.11.01: Arno Brinkman: Added match_indices for better support of OR handling + * in INNER JOIN (gen_join) statements. + * 2002.12.15: Arno Brinkman: Added find_used_streams, so that inside opt_compile all the + * streams are marked active. This causes that more indices can be used for + * a retrieval. With this change BUG SF #219525 is solved too. + */ + +#include "firebird.h" +#include +#include +#include "../jrd/jrd.h" +#include "../jrd/align.h" +#include "../jrd/val.h" +#include "../jrd/req.h" +#include "../jrd/exe.h" +#include "../jrd/lls.h" +#include "../jrd/ods.h" +#include "../jrd/btr.h" +#include "../jrd/sort.h" +#include "../jrd/ini.h" +#include "../jrd/intl.h" +#include "../jrd/Collation.h" +#include "../common/gdsassert.h" +#include "../jrd/btr_proto.h" +#include "../jrd/cch_proto.h" +#include "../jrd/cmp_proto.h" +#include "../jrd/cvt2_proto.h" +#include "../jrd/dpm_proto.h" +#include "../common/dsc_proto.h" +#include "../jrd/err_proto.h" +#include "../jrd/ext_proto.h" +#include "../jrd/intl_proto.h" +#include "../jrd/lck_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/mov_proto.h" +#include "../jrd/par_proto.h" +#include "../yvalve/gds_proto.h" +#include "../jrd/DataTypeUtil.h" +#include "../jrd/KeywordsTable.h" +#include "../jrd/RecordSourceNodes.h" +#include "../jrd/VirtualTable.h" +#include "../jrd/Monitoring.h" +#include "../jrd/TimeZone.h" +#include "../jrd/UserManagement.h" +#include "../common/classes/array.h" +#include "../common/classes/objects_array.h" +#include "../common/os/os_utils.h" +#include "../jrd/recsrc/RecordSource.h" +#include "../jrd/recsrc/Cursor.h" +#include "../jrd/Mapping.h" +#include "../jrd/DbCreators.h" +#include "../dsql/BoolNodes.h" +#include "../dsql/ExprNodes.h" +#include "../dsql/StmtNodes.h" +#include "../jrd/ConfigTable.h" + +#include "../jrd/optimizer/Optimizer.h" + +using namespace Jrd; +using namespace Firebird; + +#ifdef OPT_DEBUG_RETRIEVAL +#define OPT_DEBUG +#endif + +#ifdef OPT_DEBUG_SYS_REQUESTS +#define OPT_DEBUG +#endif + +#ifdef OPT_DEBUG +#define OPTIMIZER_DEBUG_FILE "opt_debug.out" +#endif + + +namespace +{ + inline void SET_DEP_BIT(ULONG* array, const SLONG bit) + { + array[bit / BITS_PER_LONG] |= (1L << (bit % BITS_PER_LONG)); + } + + inline bool TEST_DEP_BIT(const ULONG* array, const ULONG bit) + { + return (array[bit / BITS_PER_LONG] & (1L << (bit % BITS_PER_LONG))) != 0; + } + + const int CACHE_PAGES_PER_STREAM = 15; + + // enumeration of sort datatypes + + static const UCHAR sort_dtypes[] = + { + 0, // dtype_unknown + SKD_text, // dtype_text + SKD_cstring, // dtype_cstring + SKD_varying, // dtype_varying + 0, + 0, + 0, // dtype_packed + 0, // dtype_byte + SKD_short, // dtype_short + SKD_long, // dtype_long + SKD_quad, // dtype_quad + SKD_float, // dtype_real + SKD_double, // dtype_double + SKD_double, // dtype_d_float + SKD_sql_date, // dtype_sql_date + SKD_sql_time, // dtype_sql_time + SKD_timestamp, // dtype_timestamp + SKD_quad, // dtype_blob + 0, // dtype_array + SKD_int64, // dtype_int64 + SKD_text, // dtype_dbkey - use text sort for backward compatibility + SKD_bytes, // dtype_boolean + SKD_dec64, // dtype_dec64 + SKD_dec128, // dtype_dec128 + SKD_int128, // dtype_int128 + SKD_sql_time_tz, // dtype_sql_time_tz + SKD_timestamp_tz // dtype_timestamp_tz + }; + + struct SortField + { + SortField() : stream(INVALID_STREAM), id(0), desc(nullptr) + {} + + SortField(StreamType _stream, ULONG _id, const dsc* _desc) + : stream(_stream), id(_id), desc(_desc) + {} + + StreamType stream; + ULONG id; + const dsc* desc; + }; + + class CrossJoin : public River + { + public: + CrossJoin(CompilerScratch* csb, RiverList& rivers) + : River(csb, nullptr, rivers) + { + // Save states of the underlying streams and restore them afterwards + + StreamStateHolder stateHolder(csb, m_streams); + + // Generate record source objects + + const FB_SIZE_T riverCount = rivers.getCount(); + + if (riverCount == 1) + { + River* const sub_river = rivers.pop(); + m_rsb = sub_river->getRecordSource(); + } + else + { + HalfStaticArray rsbs(riverCount); + + // Reorder input rivers according to their possible inter-dependencies + + while (rivers.hasData()) + { + const auto orgCount = rsbs.getCount(); + + for (auto& subRiver : rivers) + { + const auto subRsb = subRiver->getRecordSource(); + fb_assert(!rsbs.exist(subRsb)); + + subRiver->activate(csb); + + if (subRiver->isComputable(csb)) + { + rsbs.add(subRsb); + rivers.remove(&subRiver); + break; + } + + subRiver->deactivate(csb); + } + + if (rsbs.getCount() == orgCount) + break; + } + + if (rivers.hasData()) + { + // Ideally, we should never get here. But just in case it happened, handle it. + + for (auto& subRiver : rivers) + { + const auto subRsb = subRiver->getRecordSource(); + fb_assert(!rsbs.exist(subRsb)); + + const auto pos = &subRiver - rivers.begin(); + rsbs.insert(pos, subRsb); + } + + rivers.clear(); + } + + m_rsb = FB_NEW_POOL(csb->csb_pool) NestedLoopJoin(csb, rsbs.getCount(), rsbs.begin()); + } + } + }; + + inline void compose(MemoryPool& pool, BoolExprNode** node1, BoolExprNode* node2) + { + if (node2) + *node1 = (*node1) ? FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and, *node1, node2) : node2; + } + + void classMask(unsigned count, ValueExprNode** eq_class, ULONG* mask) + { + // Given an sort/merge join equivalence class (vector of node pointers + // of representative values for rivers), return a bit mask of rivers with values + + if (count > MAX_CONJUNCTS) + { + ERR_post(Arg::Gds(isc_optimizer_blk_exc)); + // Msg442: size of optimizer block exceeded + } + + for (unsigned i = 0; i < OPT_STREAM_BITS; i++) + mask[i] = 0; + + for (unsigned i = 0; i < count; i++, eq_class++) + { + if (*eq_class) + { + SET_DEP_BIT(mask, i); + DEV_BLKCHK(*eq_class, type_nod); + } + } + } + + + unsigned getRiverCount(unsigned count, const ValueExprNode* const* eq_class) + { + // Given an sort/merge join equivalence class (vector of node pointers + // of representative values for rivers), return the count of rivers with values + + unsigned cnt = 0; + + for (unsigned i = 0; i < count; i++) + { + if (*eq_class++) + cnt++; + } + + return cnt; + } + + bool fieldEqual(const ValueExprNode* node1, const ValueExprNode* node2) + { + if (!node1 || !node2) + return false; + + if (node1->getType() != node2->getType()) + return false; + + if (node1 == node2) + return true; + + const auto fieldNode1 = nodeAs(node1); + const auto fieldNode2 = nodeAs(node2); + + if (fieldNode1 && fieldNode2) + { + return fieldNode1->fieldStream == fieldNode2->fieldStream && + fieldNode1->fieldId == fieldNode2->fieldId; + } + + return false; + } + + bool fieldEqual(const BoolExprNode* node1, const BoolExprNode* node2) + { + if (!node1 || !node2) + return false; + + if (node1->getType() != node2->getType()) + return false; + + if (node1 == node2) + return true; + + const auto cmpNode = nodeAs(node1); + const auto cmpNode2 = nodeAs(node2); + + if (cmpNode && cmpNode2 && cmpNode->blrOp == cmpNode2->blrOp && + (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) + { + if (fieldEqual(cmpNode->arg1, cmpNode2->arg1) && + fieldEqual(cmpNode->arg2, cmpNode2->arg2)) + { + return true; + } + + if (fieldEqual(cmpNode->arg1, cmpNode2->arg2) && + fieldEqual(cmpNode->arg2, cmpNode2->arg1)) + { + return true; + } + } + + return false; + } + + + bool augmentStack(ValueExprNode* node, ValueExprNodeStack& stack) + { + for (ValueExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) + { + if (fieldEqual(node, temp.object())) + return false; + } + + stack.push(node); + return true; + } + + bool augmentStack(BoolExprNode* node, BoolExprNodeStack& stack) + { + for (BoolExprNodeStack::const_iterator temp(stack); temp.hasData(); ++temp) + { + if (fieldEqual(node, temp.object())) + return false; + } + + stack.push(node); + return true; + } + + bool searchStack(const ValueExprNode* node, const ValueExprNodeStack& stack) + { + for (ValueExprNodeStack::const_iterator iter(stack); iter.hasData(); ++iter) + { + if (fieldEqual(node, iter.object())) + return true; + } + + return false; + } + + double getCardinality(thread_db* tdbb, jrd_rel* relation, const Format* format) + { + // Return the estimated cardinality for the given relation + + double cardinality = DEFAULT_CARDINALITY; + + if (relation->rel_file) + cardinality = EXT_cardinality(tdbb, relation); + else if (!relation->isVirtual()) + { + MET_post_existence(tdbb, relation); + cardinality = DPM_cardinality(tdbb, relation, format); + MET_release_existence(tdbb, relation); + } + + return MAX(cardinality, MINIMUM_CARDINALITY); + } + + void markIndices(CompilerScratch::csb_repeat* tail, USHORT relationId) + { + // Mark indices that were not included in the user-specified access plan + + const auto plan = tail->csb_plan; + fb_assert(plan); + + if (plan->type != PlanNode::TYPE_RETRIEVE) + return; + + // Go through each of the indices and mark it unusable + // for indexed retrieval unless it was specifically mentioned + // in the plan; also mark indices for navigational access. + + // If there were none indices, this is a sequential retrieval. + + const auto relation = tail->csb_relation; + fb_assert(relation); + + if (!tail->csb_idx) + return; + + for (auto& idx : *tail->csb_idx) + { + if (!plan->accessType) + { + idx.idx_runtime_flags |= idx_plan_dont_use; + continue; + } + + bool first = true, found = false; + for (const auto& arg : plan->accessType->items) + { + if (relationId != arg.relationId) + { + // index %s cannot be used in the specified plan + ERR_post(Arg::Gds(isc_index_unused) << arg.indexName); + } + + if (idx.idx_id == arg.indexId) + { + if (plan->accessType->type == PlanNode::AccessType::TYPE_NAVIGATIONAL && first) + { + // dimitr: navigational access can use only one index, + // hence the extra check added (see the line above) + idx.idx_runtime_flags |= idx_plan_navigate; + } + else + { + // nod_indices + found = true; + break; + } + } + + first = false; + } + + if (!found) + idx.idx_runtime_flags |= idx_plan_dont_use; + } + } + + bool mapEqual(const ValueExprNode* field1, const ValueExprNode* field2, const MapNode* map) + { + // Test to see if two fields are equal, where the fields are in two different streams + // possibly mapped to each other. Order of the input fields is important. + const auto fieldNode1 = nodeAs(field1); + const auto fieldNode2 = nodeAs(field2); + + if (!fieldNode1 || !fieldNode2) + return false; + + // look through the mapping and see if we can find an equivalence. + auto sourcePtr = map->sourceList.begin(); + auto targetPtr = map->targetList.begin(); + + for (const auto sourceEnd = map->sourceList.end(); + sourcePtr != sourceEnd; + ++sourcePtr, ++targetPtr) + { + const auto mapFrom = nodeAs(*sourcePtr); + const auto mapTo = nodeAs(*targetPtr); + + if (!mapFrom || !mapTo) + continue; + + if (fieldNode1->fieldStream != mapFrom->fieldStream || + fieldNode1->fieldId != mapFrom->fieldId) + { + continue; + } + + if (fieldNode2->fieldStream != mapTo->fieldStream || + fieldNode2->fieldId != mapTo->fieldId) + { + continue; + } + + return true; + } + + return false; + } + + void setDirection(SortNode* fromClause, SortNode* toClause) + { + // Update the direction of a GROUP BY, DISTINCT, or ORDER BY + // clause to the same direction as another clause. Do the same + // for the nulls placement flag. + + const auto fromCount = fromClause->expressions.getCount(); + + fb_assert(fromCount <= toClause->expressions.getCount()); + fb_assert(fromCount == fromClause->direction.getCount() && + fromCount == fromClause->nullOrder.getCount()); + fb_assert(toClause->expressions.getCount() == toClause->direction.getCount() && + toClause->expressions.getCount() == toClause->nullOrder.getCount()); + + for (FB_SIZE_T i = 0; i < fromCount; ++i) + { + toClause->direction[i] = fromClause->direction[i]; + toClause->nullOrder[i] = fromClause->nullOrder[i]; + } + } + + void setPosition(const SortNode* from_clause, SortNode* to_clause, const MapNode* map) + { + // Update the fields in a GROUP BY, DISTINCT, or ORDER BY clause to the same position + // as another clause, possibly using a mapping between the streams. + + // Track the position in the from list with "to_swap", and find the corresponding + // field in the from list with "to_ptr", then swap the two fields. By the time + // we get to the end of the from list, all fields in the to list will be reordered. + + auto to_swap = to_clause->expressions.begin(); + + // We need to process no more than the number of nodes in the "from" clause + + const auto count = from_clause->expressions.getCount(); + fb_assert(count <= to_clause->expressions.getCount()); + + auto from_ptr = from_clause->expressions.begin(); + for (const auto from_end = from_ptr + count; from_ptr != from_end; ++from_ptr) + { + NestConst* to_ptr = to_clause->expressions.begin(); + for (const auto to_end = to_ptr + count; to_ptr != to_end; ++to_ptr) + { + if ((map && mapEqual(*to_ptr, *from_ptr, map)) || + (!map && fieldEqual(*to_ptr, *from_ptr))) + { + ValueExprNode* swap = *to_swap; + *to_swap = *to_ptr; + *to_ptr = swap; + } + } + + ++to_swap; + } + } + +} // namespace + + +// +// Constructor +// + +Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), csb(aCsb), rse(aRse), + compileStreams(getPool()), + bedStreams(getPool()), + keyStreams(getPool()), + subStreams(getPool()), + outerStreams(getPool()), + conjuncts(getPool()) +{ +} + + +// +// Destructor +// + +Optimizer::~Optimizer() +{ + // Release memory allocated for index descriptions + for (const auto compileStream : compileStreams) + { + delete csb->csb_rpt[compileStream].csb_idx; + csb->csb_rpt[compileStream].csb_idx = nullptr; + } + + if (debugFile) + fclose(debugFile); +} + + +// +// Compile and optimize a record selection expression into a set of record source blocks +// + +RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack) +{ + Optimizer subOpt(tdbb, csb, subRse); + const auto rsb = subOpt.compile(parentStack); + + if (parentStack && subOpt.isInnerJoin()) + { + // If any parent conjunct was utilized, update our copy of its flags. + // Currently used for inner joins only, although could also be applied + // to conjuncts utilized for outer streams of outer joins. + + for (auto subIter = subOpt.getParentConjuncts(); subIter.hasData(); ++subIter) + { + for (auto selfIter = getConjuncts(); selfIter.hasData(); ++selfIter) + { + if (*selfIter == *subIter) + { + selfIter |= (subIter & (CONJUNCT_USED | CONJUNCT_MATCHED)); + break; + } + } + } + } + + return rsb; +} + +RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) +{ + // If there is a boolean, there is some work to be done. First, + // decompose the boolean into conjunctions. Then get descriptions + // of all indices for all relations in the RseNode. This will give + // us the info necessary to allocate a optimizer block big + // enough to hold this crud. + + RecordSource* rsb = nullptr; + + checkSorts(); + SortNode* sort = rse->rse_sorted; + SortNode* project = rse->rse_projection; + SortNode* aggregate = rse->rse_aggregate; + + BoolExprNodeStack conjunctStack; + unsigned conjunctCount = 0; + + // put any additional booleans on the conjunct stack, and see if we + // can generate additional booleans by associativity--this will help + // to utilize indices that we might not have noticed + if (rse->rse_boolean) + conjunctCount = decompose(rse->rse_boolean, conjunctStack); + + conjunctCount += distributeEqualities(conjunctStack, conjunctCount); + + // AB: If we have limit our retrieval with FIRST / SKIP syntax then + // we may not deliver above conditions (from higher rse's) to this + // rse, because the results should be consistent. + if (rse->rse_skip || rse->rse_first) + parentStack = nullptr; + + // Set base-point before the parent/distributed nodes begin. + const unsigned baseCount = conjunctCount; + baseConjuncts = baseCount; + + // AB: Add parent conjunctions to conjunct_stack, keep in mind + // the outer-streams! For outer streams put missing (IS NULL) + // conjunctions in the missing_stack. + // + // opt_rpt[0..opt_base_conjuncts-1] = defined conjunctions to this stream + // opt_rpt[0..opt_base_parent_conjuncts-1] = defined conjunctions to this + // stream and allowed distributed conjunctions (with parent) + // opt_rpt[0..opt_base_missing_conjuncts-1] = defined conjunctions to this + // stream and allowed distributed conjunctions and allowed parent + // opt_rpt[0..opt_conjuncts_count-1] = all conjunctions + // + // allowed = booleans that can never evaluate to NULL/Unknown or turn + // NULL/Unknown into a True or False. + + unsigned parentCount = 0, distributedCount = 0; + BoolExprNodeStack missingStack; + + if (parentStack) + { + for (BoolExprNodeStack::iterator iter(*parentStack); + iter.hasData() && conjunctCount < MAX_CONJUNCTS; ++iter) + { + BoolExprNode* const node = iter.object(); + + if (!isInnerJoin() && node->possiblyUnknown()) + { + // parent missing conjunctions shouldn't be + // distributed to FULL OUTER JOIN streams at all + if (!isFullJoin()) + missingStack.push(node); + } + else + { + conjunctStack.push(node); + conjunctCount++; + parentCount++; + } + } + + // We've now merged parent, try again to make more conjunctions. + distributedCount = distributeEqualities(conjunctStack, conjunctCount); + conjunctCount += distributedCount; + } + + // The newly created conjunctions belong to the base conjunctions. + // After them are starting the parent conjunctions. + baseParentConjuncts = baseConjuncts + distributedCount; + + // Set base-point before the parent IS NULL nodes begin + baseMissingConjuncts = conjunctCount; + + // Check if size of optimizer block exceeded. + if (conjunctCount > MAX_CONJUNCTS) + { + ERR_post(Arg::Gds(isc_optimizer_blk_exc)); + // Msg442: size of optimizer block exceeded + } + + // Put conjunctions in opt structure. + // Note that it's a stack and we get the nodes in reversed order from the stack. + + conjuncts.grow(conjunctCount); + int nodeBase = -1, j = -1; + + for (unsigned i = conjunctCount; i > 0; i--, j--) + { + BoolExprNode* const node = conjunctStack.pop(); + + if (i == baseCount) + { + // The base conjunctions + j = baseCount - 1; + nodeBase = 0; + } + else if (i == conjunctCount - distributedCount) + { + // The parent conjunctions + j = parentCount - 1; + nodeBase = baseParentConjuncts; + } + else if (i == conjunctCount) + { + // The new conjunctions created by "distribution" from the stack + j = distributedCount - 1; + nodeBase = baseConjuncts; + } + + fb_assert(nodeBase >= 0 && j >= 0); + conjuncts[nodeBase + j].node = node; + } + + // Put the parent missing nodes on the stack + for (BoolExprNodeStack::iterator iter(missingStack); + iter.hasData() && conjunctCount < MAX_CONJUNCTS; ++iter) + { + BoolExprNode* const node = iter.object(); + + conjuncts.grow(conjunctCount + 1); + conjuncts[conjunctCount].node = node; + conjunctCount++; + } + + // Clear the csb_active flag of all streams in the RseNode + StreamList rseStreams; + rse->computeRseStreams(rseStreams); + + for (StreamList::iterator i = rseStreams.begin(); i != rseStreams.end(); ++i) + csb->csb_rpt[*i].deactivate(); + + // Go through the record selection expression generating + // record source blocks for all streams + + RiverList rivers; + + bool innerSubStream = false; + for (auto node : rse->rse_relations) + { + fb_assert(sort == rse->rse_sorted); + fb_assert(aggregate == rse->rse_aggregate); + + // Find the stream number and place it at the end of the bedStreams array + // (if this is really a stream and not another RseNode) + + node->computeRseStreams(bedStreams); + node->computeDbKeyStreams(keyStreams); + + // Compile the node + rsb = node->compile(tdbb, this, innerSubStream); + + // If an rsb has been generated, we have a non-relation; + // so it forms a river of its own since it is separately + // optimized from the streams in this rsb + + if (rsb) + { + StreamList localStreams; + rsb->findUsedStreams(localStreams); + + // AB: Save all outer-part streams + if (isInnerJoin() || (isLeftJoin() && !innerSubStream)) + { + subStreams.join(localStreams); + outerStreams.join(localStreams); + + // Apply local booleans, if any. Note that it's done + // only for inner joins and outer streams of left joins. + auto iter = getConjuncts(!isInnerJoin(), false); + rsb = applyLocalBoolean(rsb, localStreams, iter); + } + + const auto river = FB_NEW_POOL(getPool()) River(csb, rsb, node, localStreams); + river->deactivate(csb); + rivers.add(river); + } + else + { + // We have a relation, just add its stream + fb_assert(bedStreams.hasData()); + outerStreams.add(bedStreams.back()); + } + + innerSubStream = true; + } + + // This is an attempt to make sure we have a large enough cache to + // efficiently retrieve this query; make sure the cache has a minimum + // number of pages for each stream in the RseNode (the number is just a guess) + if (compileStreams.getCount() > 5) + CCH_expand(tdbb, (ULONG) (compileStreams.getCount() * CACHE_PAGES_PER_STREAM)); + + // At this point we are ready to start optimizing. + // We will use the opt block to hold information of + // a global nature, meaning that it needs to stick + // around for the rest of the optimization process. + + // Attempt to optimize aggregates via an index, if possible + if (aggregate && !sort) + sort = aggregate; + else + rse->rse_aggregate = aggregate = nullptr; + + // AB: Mark the previous used streams (sub-RseNode's) as active + for (const auto subStream : subStreams) + csb->csb_rpt[subStream].activate(); + + bool sortCanBeUsed = true; + SortNode* const orgSortNode = sort; + + // When DISTINCT and ORDER BY are done on different fields, + // and ORDER BY can be mapped to an index, then the records + // are returned in the wrong order because DISTINCT sort is + // performed after the navigational walk of the index. + // For that reason, we need to de-optimize this case so that + // ORDER BY does not use an index. + if (sort && project) + { + sort = nullptr; + sortCanBeUsed = false; + } + + // Outer joins are processed their own way + if (!isInnerJoin()) + rsb = generateOuterJoin(rivers, &sort); + else + { + // AB: If previous rsb's are already on the stack we can't use + // a navigational-retrieval for an ORDER BY because the next + // streams are JOINed to the previous ones + if (rivers.hasData()) + { + sort = nullptr; + sortCanBeUsed = false; + + // AB: We could already have multiple rivers at this + // point so try to do some hashing or sort/merging now. + while (generateEquiJoin(rivers)) + ; + } + + StreamList joinStreams(compileStreams); + + fb_assert(joinStreams.getCount() != 1 || csb->csb_rpt[joinStreams[0]].csb_relation); + + while (true) + { + // AB: Determine which streams have an index relationship + // with the currently active rivers. This is needed so that + // no merge is made between a new cross river and the + // currently active rivers. Where in the new cross river + // a stream depends (index) on the active rivers. + StreamList dependentStreams, freeStreams; + findDependentStreams(joinStreams, dependentStreams, freeStreams); + + // If we have dependent and free streams then we can't rely on + // the sort node to be used for index navigation + if (dependentStreams.hasData() && freeStreams.hasData()) + { + sort = nullptr; + sortCanBeUsed = false; + } + + if (dependentStreams.hasData()) + { + // Copy free streams + joinStreams.assign(freeStreams); + + // Make rivers from the dependent streams + generateInnerJoin(dependentStreams, rivers, &sort, rse->rse_plan); + + // Generate one river which holds a cross join rsb between + // all currently available rivers + + rivers.add(FB_NEW_POOL(getPool()) CrossJoin(csb, rivers)); + rivers.back()->activate(csb); + } + else + { + if (freeStreams.hasData()) + { + // Deactivate streams from rivers on stack, because + // the remaining streams don't have any indexed relationship with them + for (const auto river : rivers) + river->deactivate(csb); + } + + break; + } + } + + // attempt to form joins in decreasing order of desirability + generateInnerJoin(joinStreams, rivers, &sort, rse->rse_plan); + + // If there are multiple rivers, try some hashing or sort/merging + while (generateEquiJoin(rivers)) + ; + + rsb = CrossJoin(csb, rivers).getRecordSource(); + + // Pick up any residual boolean that may have fallen thru the cracks + rsb = generateResidualBoolean(rsb); + } + + // Assign the sort node back if it wasn't used by the index navigation + if (orgSortNode && !sortCanBeUsed) + sort = orgSortNode; + + // If the aggregate was not optimized via an index, get rid of the + // sort and flag the fact to the calling routine + if (aggregate && sort) + { + rse->rse_aggregate = nullptr; + sort = nullptr; + } + + // Check index usage in all the base streams to ensure + // that any user-specified access plan is followed + + checkIndices(); + + if (project || sort) + { + // Eliminate any duplicate dbkey streams + for (const auto stream: bedStreams) + { + FB_SIZE_T pos; + if (keyStreams.find(stream, pos)) + keyStreams.remove(pos); + } + + // Handle project clause, if present + if (project) + rsb = generateSort(bedStreams, &keyStreams, rsb, project, favorFirstRows(), true); + + // Handle sort clause if present + if (sort) + rsb = generateSort(bedStreams, &keyStreams, rsb, sort, favorFirstRows(), false); + } + + // Handle first and/or skip. The skip MUST (if present) + // appear in the rsb list AFTER the first. Since the gen_first and gen_skip + // functions add their nodes at the beginning of the rsb list we MUST call + // gen_skip before gen_first. + + if (rse->rse_skip) + rsb = FB_NEW_POOL(getPool()) SkipRowsStream(csb, rsb, rse->rse_skip); + + if (rse->rse_first) + rsb = FB_NEW_POOL(getPool()) FirstRowsStream(csb, rsb, rse->rse_first); + + if (rse->flags & RseNode::FLAG_SINGULAR) + rsb = FB_NEW_POOL(getPool()) SingularStream(csb, rsb); + + if (rse->flags & RseNode::FLAG_WRITELOCK) + { + for (const auto compileStream : compileStreams) + { + const auto tail = &csb->csb_rpt[compileStream]; + tail->csb_flags |= csb_update; + + fb_assert(tail->csb_relation); + + CMP_post_access(tdbb, csb, tail->csb_relation->rel_security_name, + tail->csb_view ? tail->csb_view->rel_id : 0, + SCL_update, obj_relations, tail->csb_relation->rel_name); + } + + rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb); + } + + if (rse->flags & RseNode::FLAG_SCROLLABLE) + rsb = FB_NEW_POOL(getPool()) BufferedStream(csb, rsb); + + return rsb; +} + + +// +// Prepare relation and its indices for optimization +// + +void Optimizer::compileRelation(StreamType stream) +{ + // We have found a base relation; record its stream number in the streams array + // as a candidate for merging into a river + + compileStreams.add(stream); + + // If we have any booleans or sort fields, we may be able to + // use an index to optimize them; retrieve the current format of + // all indices at this time so we can determine if it's possible + + const bool needIndices = conjuncts.hasData() || (rse->rse_sorted || rse->rse_aggregate); + + const auto tail = &csb->csb_rpt[stream]; + + const auto relation = tail->csb_relation; + fb_assert(relation); + + tail->csb_idx = nullptr; + + if (needIndices && !relation->rel_file && !relation->isVirtual()) + { + const auto relPages = relation->getPages(tdbb); + IndexDescList idxList; + BTR_all(tdbb, relation, idxList, relPages); + + if (idxList.hasData()) + tail->csb_idx = FB_NEW_POOL(getPool()) IndexDescList(getPool(), idxList); + + if (tail->csb_plan) + markIndices(tail, relation->rel_id); + } + + const auto format = CMP_format(tdbb, csb, stream); + tail->csb_cardinality = getCardinality(tdbb, relation, format); +} + + +// +// Generate a separate AggregateSort (Aggregate SortedStream Block) for each distinct operation. +// Note that this should be optimized to use indices if possible. +// + +void Optimizer::generateAggregateDistincts(MapNode* map) +{ + dsc descriptor; + dsc* desc = &descriptor; + + for (auto from : map->sourceList) + { + auto aggNode = nodeAs(from); + + if (aggNode && aggNode->distinct) + { + // Build the sort key definition. Turn cstrings into varying text. + aggNode->arg->getDesc(tdbb, csb, desc); + + if (desc->dsc_dtype == dtype_cstring) + { + desc->dsc_dtype = dtype_varying; + desc->dsc_length++; + } + + const auto asb = FB_NEW_POOL(getPool()) AggregateSort(getPool()); + + asb->intl = desc->isText() && desc->getTextType() != ttype_none && + desc->getTextType() != ttype_binary && desc->getTextType() != ttype_ascii; + + sort_key_def* sort_key = asb->keyItems.getBuffer(asb->intl ? 2 : 1); + sort_key->setSkdOffset(); + + if (asb->intl) + { + const USHORT key_length = ROUNDUP(INTL_key_length(tdbb, + INTL_TEXT_TO_INDEX(desc->getTextType()), desc->getStringLength()), sizeof(SINT64)); + + sort_key->setSkdLength(SKD_bytes, key_length); + sort_key->skd_flags = SKD_ascending; + sort_key->skd_vary_offset = 0; + + ++sort_key; + sort_key->setSkdOffset(&sort_key[-1]); + asb->length = sort_key->getSkdOffset(); + } + + fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); + sort_key->setSkdLength(sort_dtypes[desc->dsc_dtype], desc->dsc_length); + + if (!sort_key->skd_dtype) + ERR_post(Arg::Gds(isc_invalid_sort_datatype) << Arg::Str(DSC_dtype_tostring(desc->dsc_dtype))); + + if (desc->dsc_dtype == dtype_varying) + { + // allocate space to store varying length + sort_key->skd_vary_offset = sort_key->getSkdOffset() + ROUNDUP(desc->dsc_length, sizeof(SLONG)); + asb->length = sort_key->skd_vary_offset + sizeof(USHORT); + } + else + asb->length += sort_key->getSkdLength(); + + asb->length = ROUNDUP(asb->length, sizeof(SLONG)); + // dimitr: allocate an extra longword for the purely artificial counter, + // see AggNode::aggPass() for details; the length remains rounded properly + asb->length += sizeof(ULONG); + + sort_key->skd_flags = SKD_ascending; + asb->impure = csb->allocImpure(); + asb->desc = *desc; + + aggNode->asb = asb; + } + } +} + + +// +// Generate a record source block to handle either a sort or a project. +// The two case are virtual identical -- the only difference is that +// project eliminates duplicates. However, since duplicates are +// recognized and handled by sort, the JRD processing is identical. +// + +SortedStream* Optimizer::generateSort(const StreamList& streams, + const StreamList* dbkeyStreams, + RecordSource* rsb, + SortNode* sort, + bool refetchFlag, + bool projectFlag) +{ + /* We already know the number of keys, but we also need to compute the + total number of fields, keys and non-keys, to be pumped thru sort. Starting + with the number of keys, count the other field referenced. Since a field + is often a key, check for overlap to keep the length of the sort record + down. */ + + /* Along with the record number, the transaction id of the + * record will also be stored in the sort file. This will + * be used to detect update conflict in read committed + * transactions. */ + + ULONG items = sort->expressions.getCount() + + 3 * streams.getCount() + 2 * (dbkeyStreams ? dbkeyStreams->getCount() : 0); + const NestConst* const end_node = sort->expressions.end(); + + // Collect all fields involved into the sort + + HalfStaticArray fields; + ULONG totalLength = 0; + + for (const auto stream : streams) + { + UInt32Bitmap::Accessor accessor(csb->csb_rpt[stream].csb_fields); + + if (accessor.getFirst()) + { + do + { + const auto id = accessor.current(); + + const auto format = CMP_format(tdbb, csb, stream); + const auto desc = &format->fmt_desc[id]; + + if (id >= format->fmt_count || desc->isUnknown()) + IBERROR(157); // msg 157 cannot sort on a field that does not exist + + fields.push(SortField(stream, id, desc)); + totalLength += desc->dsc_length; + + // If the field has already been mentioned as a sort key, don't bother to repeat it. + // Unless this key is computed/volatile and thus cannot be restored after sorting. + + for (const auto expr : sort->expressions) + { + const auto fieldNode = nodeAs(expr); + + if (fieldNode && fieldNode->fieldStream == stream && fieldNode->fieldId == id) + { + if (!SortedStream::hasVolatileKey(desc)) + { + totalLength -= desc->dsc_length; + fields.pop(); + } + + break; + } + } + + } while (accessor.getNext()); + } + } + + auto fieldCount = fields.getCount(); + + // Unless refetching is requested explicitly (e.g. FIRST ROWS optimization mode), + // validate the sort record length against the configured threshold for inline storage + + if (!refetchFlag) + { + const auto dbb = tdbb->getDatabase(); + const auto threshold = dbb->dbb_config->getInlineSortThreshold(); + + refetchFlag = (totalLength > threshold); + } + + // Check for persistent fields to be excluded from the sort. + // If nothing is excluded, there's no point in the refetch mode. + + if (refetchFlag) + { + for (auto& item : fields) + { + const auto relation = csb->csb_rpt[item.stream].csb_relation; + + if (relation && + !relation->rel_file && + !relation->rel_view_rse && + !relation->isVirtual()) + { + item.desc = nullptr; + --fieldCount; + } + } + + refetchFlag = (fieldCount != fields.getCount()); + } + + items += fieldCount; + + // Now that we know the number of items, allocate a sort map block. + const auto map = FB_NEW_POOL(getPool()) SortedStream::SortMap(getPool()); + + if (projectFlag) + map->flags |= SortedStream::FLAG_PROJECT; + + if (refetchFlag) + map->flags |= SortedStream::FLAG_REFETCH; + + if (sort->unique) + map->flags |= SortedStream::FLAG_UNIQUE; + + sort_key_def* prev_key = nullptr; + + // Loop thru sort keys building sort keys. Actually, to handle null values + // correctly, two sort keys are made for each field, one for the null flag + // and one for field itself. + + dsc descriptor; + + SortedStream::SortMap::Item* map_item = map->items.getBuffer(items); + sort_key_def* sort_key = map->keyItems.getBuffer(2 * sort->expressions.getCount()); + const SortDirection* direction = sort->direction.begin(); + const NullsPlacement* nullOrder = sort->nullOrder.begin(); + + for (NestConst* node_ptr = sort->expressions.begin(); + node_ptr != end_node; + ++node_ptr, ++nullOrder, ++direction, ++map_item) + { + // Pick up sort key expression. + + NestConst node = *node_ptr; + dsc* desc = &descriptor; + node->getDesc(tdbb, csb, desc); + + // Allow for "key" forms of International text to grow + if (IS_INTL_DATA(desc)) + { + // Turn varying text and cstrings into text. + + if (desc->dsc_dtype == dtype_varying) + { + desc->dsc_dtype = dtype_text; + desc->dsc_length -= sizeof(USHORT); + } + else if (desc->dsc_dtype == dtype_cstring) + { + desc->dsc_dtype = dtype_text; + desc->dsc_length--; + } + + desc->dsc_length = INTL_key_length(tdbb, INTL_INDEX_TYPE(desc), desc->dsc_length); + } + + // Make key for null flag + sort_key->setSkdLength(SKD_text, 1); + sort_key->setSkdOffset(prev_key); + + // Handle nulls placement + sort_key->skd_flags = SKD_ascending; + + // Have SQL-compliant nulls ordering for ODS11+ + if ((*nullOrder == NULLS_DEFAULT && *direction != ORDER_DESC) || *nullOrder == NULLS_FIRST) + sort_key->skd_flags |= SKD_descending; + + prev_key = sort_key++; + + // Make key for sort key proper + fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); + sort_key->setSkdLength(sort_dtypes[desc->dsc_dtype], desc->dsc_length); + sort_key->setSkdOffset(&sort_key[-1], desc); + sort_key->skd_flags = SKD_ascending; + if (*direction == ORDER_DESC) + sort_key->skd_flags |= SKD_descending; + + if (!sort_key->skd_dtype) + ERR_post(Arg::Gds(isc_invalid_sort_datatype) << Arg::Str(DSC_dtype_tostring(desc->dsc_dtype))); + + if (sort_key->skd_dtype == SKD_varying || sort_key->skd_dtype == SKD_cstring) + { + if (desc->dsc_ttype() == ttype_binary) + sort_key->skd_flags |= SKD_binary; + } + + if (SortedStream::hasVolatileKey(desc) && !refetchFlag) + sort_key->skd_flags |= SKD_separate_data; + + map_item->reset(node, prev_key->getSkdOffset()); + map_item->desc = *desc; + map_item->desc.dsc_address = (UCHAR*)(IPTR) sort_key->getSkdOffset(); + + prev_key = sort_key++; + + if (const auto fieldNode = nodeAs(node)) + { + map_item->stream = fieldNode->fieldStream; + map_item->fieldId = fieldNode->fieldId; + } + } + + fb_assert(prev_key); + ULONG map_length = prev_key ? ROUNDUP(prev_key->getSkdOffset() + prev_key->getSkdLength(), sizeof(SLONG)) : 0; + map->keyLength = map_length; + ULONG flag_offset = map_length; + map_length += fieldCount; + + // Now go back and process all to fields involved with the sort + + for (const auto& item : fields) + { + if (!item.desc) + continue; + + if (item.desc->dsc_dtype >= dtype_aligned) + map_length = FB_ALIGN(map_length, type_alignments[item.desc->dsc_dtype]); + + map_item->reset(item.stream, (SSHORT) item.id, flag_offset++); + map_item->desc = *item.desc; + map_item->desc.dsc_address = (UCHAR*)(IPTR) map_length; + map_length += item.desc->dsc_length; + map_item++; + } + + // Make fields for record numbers and transaction ids for all streams + + map_length = ROUNDUP(map_length, sizeof(SINT64)); + for (const auto stream : streams) + { + map_item->reset(stream, SortedStream::ID_DBKEY); + map_item->desc.makeInt64(0, (SINT64*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; + map_item++; + + map_item->reset(stream, SortedStream::ID_TRANS); + map_item->desc.makeInt64(0, (SINT64*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; + map_item++; + } + + if (dbkeyStreams && dbkeyStreams->hasData()) + { + map_length = ROUNDUP(map_length, sizeof(SINT64)); + + for (const auto stream : *dbkeyStreams) + { + map_item->reset(stream, SortedStream::ID_DBKEY); + map_item->desc.makeInt64(0, (SINT64*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; + map_item++; + } + + for (const auto stream : *dbkeyStreams) + { + map_item->reset(stream, SortedStream::ID_DBKEY_VALID); + map_item->desc.makeText(1, CS_BINARY, (UCHAR*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; + map_item++; + } + } + + for (const auto stream : streams) + { + map_item->reset(stream, SortedStream::ID_DBKEY_VALID); + map_item->desc.makeText(1, CS_BINARY, (UCHAR*)(IPTR) map_length); + map_length += map_item->desc.dsc_length; + map_item++; + } + + fb_assert(map_item == map->items.end()); + fb_assert(sort_key == map->keyItems.end()); + + map_length = ROUNDUP(map_length, sizeof(SLONG)); + + // Make fields to store varying and cstring length + + for (auto& sortKey : map->keyItems) + { + fb_assert(sortKey.skd_dtype != 0); + + if (sortKey.skd_dtype == SKD_varying || sortKey.skd_dtype == SKD_cstring) + { + sortKey.skd_vary_offset = map_length; + map_length += sizeof(USHORT); + map->flags |= SortedStream::FLAG_KEY_VARY; + } + } + + if (map_length > MAX_SORT_RECORD) + { + ERR_post(Arg::Gds(isc_sort_rec_size_err) << Arg::Num(map_length)); + // Msg438: sort record size of %ld bytes is too big + } + + map->length = map_length; + + // That was most unpleasant. Never the less, it's done (except for the debugging). + // All that remains is to build the record source block for the sort. + return FB_NEW_POOL(getPool()) SortedStream(csb, rsb, map); +} + + +// +// Find conjuncts local to the given river and compose an appropriate filter +// + +RecordSource* Optimizer::applyLocalBoolean(RecordSource* rsb, + const StreamList& streams, + ConjunctIterator& iter) +{ + StreamStateHolder globalHolder(csb); + globalHolder.deactivate(); + + StreamStateHolder localHolder(csb, streams); + localHolder.activate(csb); + + BoolExprNode* boolean = nullptr; + double selectivity = MAXIMUM_SELECTIVITY; + + for (iter.rewind(); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) + { + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; + + if (!(iter & CONJUNCT_MATCHED)) + selectivity *= getSelectivity(*iter); + } + } + + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity) : rsb; +} + + +// +// Check to make sure that the user-specified indices were actually utilized by the optimizer +// + +void Optimizer::checkIndices() +{ + for (const auto compileStream : compileStreams) + { + const auto tail = &csb->csb_rpt[compileStream]; + + const auto plan = tail->csb_plan; + if (!plan) + continue; + + if (plan->type != PlanNode::TYPE_RETRIEVE) + continue; + + const auto relation = tail->csb_relation; + + // If there were no indices fetched at all but the user specified some, + // error out using the first index specified + + if (!tail->csb_idx && plan->accessType && !tdbb->getAttachment()->isGbak()) + { + // index %s cannot be used in the specified plan + ERR_post(Arg::Gds(isc_index_unused) << plan->accessType->items[0].indexName); + } + + if (!tail->csb_idx) + return; + + // Check to make sure that all indices are either used or marked not to be used, + // and that there are no unused navigational indices + MetaName index_name; + + for (const auto& idx : *tail->csb_idx) + { + if (!(idx.idx_runtime_flags & (idx_plan_dont_use | idx_used)) || + ((idx.idx_runtime_flags & idx_plan_navigate) && !(idx.idx_runtime_flags & idx_navigate))) + { + if (relation) + MET_lookup_index(tdbb, index_name, relation->rel_name, (USHORT) (idx.idx_id + 1)); + else + index_name = ""; + + // index %s cannot be used in the specified plan + ERR_post(Arg::Gds(isc_index_unused) << Arg::Str(index_name)); + } + } + } +} + + +// +// Try to optimize out unnecessary sorting +// + +void Optimizer::checkSorts() +{ + SortNode* sort = rse->rse_sorted; + const auto sortCount = sort ? sort->expressions.getCount() : 0; + + SortNode* project = rse->rse_projection; + const auto projectCount = project ? project->expressions.getCount() : 0; + + // Check if a GROUP BY exists using the same fields as the project or sort: + // if so, the projection can be eliminated; if no projection exists, then + // the sort can be eliminated + + RecordSourceNode* subRse; + AggregateSourceNode* aggregate; + SortNode* group; + + if ((project || sort) && + rse->rse_relations.getCount() == 1 && + (subRse = rse->rse_relations[0]) && + (aggregate = nodeAs(subRse)) && + (group = aggregate->group)) + { + const auto map = aggregate->map; + const auto groupCount = group->expressions.getCount(); + + // If all the fields of the project are the same as all the fields + // of the group by, get rid of the project + + if (project && projectCount == groupCount) + { + bool equal = true; + for (unsigned i = 0; i < groupCount; i++) + { + const auto groupNode = group->expressions[i]; + const auto projectNode = project->expressions[i]; + + if (!mapEqual(groupNode, projectNode, map)) + { + equal = false; + break; + } + } + + // We can now ignore the project, but in case the project is being done + // in descending order because of an order by, do the group by the same way. + if (equal) + { + setDirection(project, group); + project = rse->rse_projection = nullptr; + } + } + + // If there is no projection, then we can make a similar optimization + // for sort, except that sort may have fewer fields than group by + + if (!project && sort && sortCount <= groupCount) + { + bool equal = true; + for (unsigned i = 0; i < sortCount; i++) + { + const auto groupNode = group->expressions[i]; + const auto sortNode = sort->expressions[i]; + + if (!mapEqual(groupNode, sortNode, map)) + { + equal = false; + break; + } + } + + // If all the fields in the sort list match the first n fields in the + // project list, we can ignore the sort, but update the sort order + // (ascending/descending) to match that in the sort list + + if (equal) + { + setDirection(sort, group); + setPosition(sort, group, map); + sort = rse->rse_sorted = nullptr; + } + } + } + + // Examine the ORDER BY and DISTINCT clauses; if all the fields in the + // ORDER BY match the first n fields in the DISTINCT in any order, the + // ORDER BY can be removed, changing the fields in the DISTINCT to match + // the ordering of fields in the ORDER BY + + if (sort && project && sortCount <= projectCount) + { + bool equal = true; + for (unsigned i = 0; i < sortCount; i++) + { + const auto sortNode = sort->expressions[i]; + const auto projectNode = project->expressions[i]; + + if (!fieldEqual(sortNode, projectNode)) + { + equal = false; + break; + } + } + + // If all the fields in the sort list match the first n fields + // in the project list, we can ignore the sort, but update + // the project to match the sort + if (equal) + { + setDirection(sort, project); + setPosition(sort, project, nullptr); + sort = rse->rse_sorted = nullptr; + } + } + + // RP: optimize sort with OUTER JOIN + // if all the fields in the sort list are from one stream, check the stream is + // the most outer stream, if true update rse and ignore the sort + if (sort && !project) + { + StreamType sortStream = 0; + bool usableSort = true; + + for (unsigned i = 0; i < sortCount; i++) + { + const auto sortNode = sort->expressions[i]; + const auto sortField = nodeAs(sortNode); + + if (sortField) + { + // Get stream for this field at this position. + const StreamType currentStream = sortField->fieldStream; + + // If this is the first position node, save this stream + if (i == 0) + sortStream = currentStream; + else if (currentStream != sortStream) + { + // If the current stream is different then the previous stream + // then we can't use this sort for an indexed order retrieval + usableSort = false; + break; + } + } + else + { + // If this is not the first position node, reject this sort. + // Two expressions cannot be mapped to a single index. + if (i > 0) + { + usableSort = false; + break; + } + + // This position doesn't use a simple field, thus we should + // check the expression internals + SortedStreamList streams; + sortNode->collectStreams(streams); + + // We can use this sort only if there's a single stream + // referenced by the expression + if (streams.getCount() == 1) + sortStream = streams[0]; + else + { + usableSort = false; + break; + } + } + } + + if (usableSort) + { + RecordSourceNode* node = rse; + RseNode* newRse = nullptr; + + while (node) + { + if (nodeIs(node)) + { + newRse = static_cast(node); + + // AB: Don't distribute the sort when a FIRST/SKIP is supplied, + // because that will affect the behaviour from the deeper RSE. + // dimitr: the same rule applies to explicit/implicit user-defined sorts. + if (newRse != rse && + (newRse->rse_first || newRse->rse_skip || + newRse->rse_sorted || newRse->rse_projection)) + { + node = nullptr; + break; + } + + // Walk trough the relations of the RSE and see if a + // matching stream can be found. + if (newRse->rse_jointype == blr_inner) + { + if (newRse->rse_relations.getCount() == 1) + node = newRse->rse_relations[0]; + else + { + bool sortStreamFound = false; + for (const auto subRse : newRse->rse_relations) + { + if ((nodeIs(subRse) || nodeIs(subRse)) && + subRse->getStream() == sortStream && + newRse != rse) + { + // We have found the correct stream + sortStreamFound = true; + break; + } + } + + if (sortStreamFound) + { + // Set the sort to the found stream and clear the original sort + newRse->rse_sorted = sort; + sort = rse->rse_sorted = nullptr; + } + + node = nullptr; + } + } + else if (newRse->rse_jointype == blr_left) + node = newRse->rse_relations[0]; + else + node = nullptr; + } + else + { + if ((nodeIs(node) || nodeIs(node)) && + node->getStream() == sortStream && + newRse && newRse != rse) + { + // We have found the correct stream, thus apply the sort here + newRse->rse_sorted = sort; + sort = rse->rse_sorted = nullptr; + } + + node = nullptr; + } + } + } + } +} + + +// +// Decompose a boolean into a stack of conjuctions. +// + +unsigned Optimizer::decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack) +{ + if (const auto binaryNode = nodeAs(boolNode)) + { + if (binaryNode->blrOp == blr_and) + { + auto count = decompose(binaryNode->arg1, stack); + count += decompose(binaryNode->arg2, stack); + return count; + } + else if (binaryNode->blrOp == blr_or) + { + BoolExprNodeStack or_stack; + + if (decompose(binaryNode->arg1, or_stack) >= 2) + { + binaryNode->arg1 = or_stack.pop(); + + while (or_stack.hasData()) + { + const auto newBoolNode = + FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_and); + newBoolNode->arg1 = or_stack.pop(); + newBoolNode->arg2 = binaryNode->arg1; + + binaryNode->arg1 = newBoolNode; + } + } + + or_stack.clear(); + + if (decompose(binaryNode->arg2, or_stack) >= 2) + { + binaryNode->arg2 = or_stack.pop(); + + while (or_stack.hasData()) + { + const auto newBoolNode = + FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_and); + newBoolNode->arg1 = or_stack.pop(); + newBoolNode->arg2 = binaryNode->arg2; + + binaryNode->arg2 = newBoolNode; + } + } + } + } + else if (const auto cmpNode = nodeAs(boolNode)) + { + // turn a between into (a greater than or equal) AND (a less than or equal) + + if (cmpNode->blrOp == blr_between) + { + auto newCmpNode = FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_geq); + newCmpNode->arg1 = cmpNode->arg1; + newCmpNode->arg2 = cmpNode->arg2; + + stack.push(newCmpNode); + + newCmpNode = FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_leq); + newCmpNode->arg1 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg1); + newCmpNode->arg2 = cmpNode->arg3; + + stack.push(newCmpNode); + + return 2; + } + + // turn a LIKE/SIMILAR into a LIKE/SIMILAR and a STARTING WITH, if it starts + // with anything other than a pattern-matching character + + ValueExprNode* arg; + + if ((cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar) && + (arg = optimizeLikeSimilar(cmpNode))) + { + const auto newCmpNode = + FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_starting); + newCmpNode->arg1 = cmpNode->arg1; + newCmpNode->arg2 = arg; + + stack.push(newCmpNode); + stack.push(boolNode); + + return 2; + } + } + + stack.push(boolNode); + + return 1; +} + + +// +// Given a stack of conjunctions, generate some simple inferences. +// In general, find classes of equalities, then find operations based on members of those classes. +// If we find any, generate additional conjunctions. In short: +// +// if (a == b) and (a $ c) --> (b $ c) for any operation '$'. +// + +unsigned Optimizer::distributeEqualities(BoolExprNodeStack& orgStack, unsigned baseCount) +{ + // dimitr: Dumb protection against too many injected conjuncts (see CORE-5381). + // Don't produce more additional conjuncts than we originally had + // (i.e. this routine should never more than double the number of conjuncts). + // Ideally, we need two separate limits here: + // 1) number of injected conjuncts (affects required impure size) + // 2) number of input conjuncts (affects search time inside this routine) + + if (baseCount * 2 > MAX_CONJUNCTS) + return 0; + + ObjectsArray classes; + ObjectsArray::iterator eq_class; + + // Zip thru stack of booleans looking for field equalities + + for (BoolExprNodeStack::iterator iter(orgStack); iter.hasData(); ++iter) + { + const auto boolean = iter.object(); + + if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) + continue; + + const auto cmpNode = nodeAs(boolean); + + if (!cmpNode || cmpNode->blrOp != blr_eql) + continue; + + auto node1 = cmpNode->arg1; + if (!nodeIs(node1)) + continue; + + auto node2 = cmpNode->arg2; + if (!nodeIs(node2)) + continue; + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + if (searchStack(node1, *eq_class)) + { + augmentStack(node2, *eq_class); + break; + } + else if (searchStack(node2, *eq_class)) + { + eq_class->push(node1); + break; + } + } + + if (eq_class == classes.end()) + { + ValueExprNodeStack& s = classes.add(); + s.push(node1); + s.push(node2); + eq_class = classes.back(); + } + } + + if (classes.isEmpty()) + return 0; + + // Make another pass looking for any equality relationships that may have crept + // in between classes (this could result from the sequence (A = B, C = D, B = C) + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + for (ValueExprNodeStack::const_iterator iter(*eq_class); iter.hasData(); ++iter) + { + for (ObjectsArray::iterator eq_class2(eq_class); + ++eq_class2 != classes.end();) + { + if (searchStack(iter.object(), *eq_class2)) + { + while (eq_class2->hasData()) + augmentStack(eq_class2->pop(), *eq_class); + } + } + } + } + + unsigned count = 0; + + // Start by making a pass distributing field equalities + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + if (eq_class->hasMore(2)) + { + for (ValueExprNodeStack::iterator outer(*eq_class); outer.hasData(); ++outer) + { + for (ValueExprNodeStack::iterator inner(outer); (++inner).hasData(); ) + { + if (count < baseCount) + { + AutoPtr cmpNode(FB_NEW_POOL(getPool()) + ComparativeBoolNode(getPool(), blr_eql)); + cmpNode->arg1 = outer.object(); + cmpNode->arg2 = inner.object(); + + if (augmentStack(cmpNode, orgStack)) + { + count++; + cmpNode.release(); + } + } + } + } + } + } + + // Now make a second pass looking for non-field equalities + + for (BoolExprNodeStack::iterator iter(orgStack); iter.hasData(); ++iter) + { + const auto boolean = iter.object(); + const auto cmpNode = nodeAs(boolean); + ValueExprNode* node1; + ValueExprNode* node2; + + if (cmpNode && + (cmpNode->blrOp == blr_eql || + cmpNode->blrOp == blr_gtr || cmpNode->blrOp == blr_geq || + cmpNode->blrOp == blr_leq || cmpNode->blrOp == blr_lss || + cmpNode->blrOp == blr_matching || cmpNode->blrOp == blr_containing || + cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar)) + { + node1 = cmpNode->arg1; + node2 = cmpNode->arg2; + } + else + continue; + + bool reverse = false; + + if (!nodeIs(node1)) + { + ValueExprNode* swap_node = node1; + node1 = node2; + node2 = swap_node; + reverse = true; + } + + if (!nodeIs(node1)) + continue; + + if (!nodeIs(node2) && !nodeIs(node2) && !nodeIs(node2)) + continue; + + for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) + { + if (searchStack(node1, *eq_class)) + { + for (ValueExprNodeStack::iterator temp(*eq_class); temp.hasData(); ++temp) + { + if (!fieldEqual(node1, temp.object()) && count < baseCount) + { + ValueExprNode* arg1; + ValueExprNode* arg2; + + if (reverse) + { + arg1 = cmpNode->arg1; + arg2 = temp.object(); + } + else + { + arg1 = temp.object(); + arg2 = cmpNode->arg2; + } + + // From the conjuncts X(A,B) and A=C, infer the conjunct X(C,B) + AutoPtr newNode(makeInferenceNode(boolean, arg1, arg2)); + + if (augmentStack(newNode, orgStack)) + { + ++count; + newNode.release(); + } + } + } + + break; + } + } + } + + return count; +} + + +// +// Find the streams that can use an index with the currently active streams +// + +void Optimizer::findDependentStreams(const StreamList& streams, + StreamList& dependent_streams, + StreamList& free_streams) +{ +#ifdef OPT_DEBUG_RETRIEVAL + if (streams.hasData()) + printf("Detecting dependent streams:\n"); +#endif + + for (const auto stream : streams) + { + const auto tail = &csb->csb_rpt[stream]; + + // Set temporary active flag for this stream + tail->activate(); + + bool indexed_relationship = false; + + if (conjuncts.hasData()) + { + // Calculate the inversion for this stream. + // The returning candidate contains the streams that will be used for + // index retrieval. This meant that if some stream is used this stream + // depends on already active streams and can not be used in a separate + // SORT/MERGE. + + Retrieval retrieval(tdbb, this, stream, false, false, nullptr, true); + const auto candidate = retrieval.getInversion(); + + if (candidate->dependentFromStreams.hasData()) + indexed_relationship = true; + } + + if (indexed_relationship) + dependent_streams.add(stream); + else + free_streams.add(stream); + + // Reset active flag + tail->deactivate(); + } +} + + +// +// Form streams into rivers according to the user-specified plan +// + +void Optimizer::formRivers(const StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause) +{ + StreamList tempStreams; + + // This must be a join or a merge node, so go through + // the substreams and place them into the temp vector + // for formation into a river + + for (const auto planNode : planClause->subNodes) + { + if (planNode->type == PlanNode::TYPE_JOIN) + { + formRivers(streams, rivers, sortClause, planNode); + continue; + } + + // At this point we must have a retrieval node, so put + // the stream into the river + fb_assert(planNode->type == PlanNode::TYPE_RETRIEVE); + + const StreamType stream = planNode->relationNode->getStream(); + + // dimitr: the plan may contain more retrievals than the "streams" + // array (some streams could already be joined to the active + // rivers), so we populate the "temp" array only with the + // streams that appear in both the plan and the "streams" + // array. + + if (streams.exist(stream)) + tempStreams.add(stream); + } + + // Just because the user specified a join does not mean that + // we are able to form a river; thus form as many rivers out + // of the join are as necessary to exhaust the streams. + // AB: Only form rivers when any retrieval node is seen, for + // example a MERGE on two JOINs will come with no retrievals + // at this point. + + if (tempStreams.hasData()) + { + InnerJoin innerJoin(tdbb, this, tempStreams, + sortClause, (planClause != nullptr)); + + while (innerJoin.findJoinOrder()) + rivers.add(innerJoin.formRiver()); + } +} + + +// +// We've got a set of rivers that may or may not be amenable to +// a hash join or a sort/merge join, and it's time to find out. +// If there are, build an appropriate join RecordSource, +// push it on the rsb stack, and update rivers accordingly. +// If two or more rivers were successfully joined, return true. +// If the whole things is a moby no-op, return false. +// + +bool Optimizer::generateEquiJoin(RiverList& orgRivers) +{ + ULONG selected_rivers[OPT_STREAM_BITS], selected_rivers2[OPT_STREAM_BITS]; + ValueExprNode** eq_class; + + // Count the number of "rivers" involved in the operation, then allocate + // a scratch block large enough to hold values to compute equality + // classes. + + const unsigned orgCount = (unsigned) orgRivers.getCount(); + + if (orgCount < 2) + return false; + + HalfStaticArray scratch; + scratch.grow(baseConjuncts * orgCount); + ValueExprNode** classes = scratch.begin(); + + // Compute equivalence classes among streams. This involves finding groups + // of streams joined by field equalities. + + ValueExprNode** last_class = classes; + + auto iter = getBaseConjuncts(); + for (; iter.hasData(); ++iter) + { + if (iter & CONJUNCT_USED) + continue; + + NestConst node1; + NestConst node2; + + if (!getEquiJoinKeys(*iter, &node1, &node2, true)) + continue; + + for (unsigned i = 0; i < orgRivers.getCount(); i++) + { + const auto river1 = orgRivers[i]; + + if (!river1->isReferenced(node1)) + { + if (!river1->isReferenced(node2)) + continue; + + ValueExprNode* const temp = node1; + node1 = node2; + node2 = temp; + } + + for (unsigned j = i + 1; j < orgRivers.getCount(); j++) + { + const auto river2 = orgRivers[j]; + + if (river2->isReferenced(node2)) + { + for (eq_class = classes; eq_class < last_class; eq_class += orgCount) + { + if (node1->sameAs(classes[i], false) || + node2->sameAs(classes[j], false)) + { + break; + } + } + + eq_class[i] = node1; + eq_class[j] = node2; + + if (eq_class == last_class) + last_class += orgCount; + } + } + } + } + + // Pick both a set of classes and a set of rivers on which to join. + // Obviously, if the set of classes is empty, return false + // to indicate that nothing could be done. + + unsigned riverCount = 0; + HalfStaticArray selected_classes(orgCount); + + for (eq_class = classes; eq_class < last_class; eq_class += orgCount) + { + unsigned i = getRiverCount(orgCount, eq_class); + + if (i > riverCount) + { + riverCount = i; + selected_classes.shrink(0); + selected_classes.add(eq_class); + classMask(orgCount, eq_class, selected_rivers); + } + else + { + classMask(orgCount, eq_class, selected_rivers2); + + for (i = 0; i < OPT_STREAM_BITS; i++) + { + if ((selected_rivers[i] & selected_rivers2[i]) != selected_rivers[i]) + break; + } + + if (i == OPT_STREAM_BITS) + selected_classes.add(eq_class); + } + } + + if (!riverCount) + return false; + + // Prepare rivers for joining + + StreamList streams; + RiverList rivers; + HalfStaticArray keys; + unsigned position = 0, maxCardinalityPosition = 0, lowestPosition = MAX_ULONG; + double maxCardinality1 = 0, maxCardinality2 = 0; + + for (auto iter = orgRivers.begin(); iter < orgRivers.end(); position++) + { + if (!(TEST_DEP_BIT(selected_rivers, position))) + { + iter++; + continue; + } + + const auto river = *iter; + + // Get the lowest river position + + if (position < lowestPosition) + lowestPosition = position; + + // Find position of the river with maximum cardinality + + const auto rsb = river->getRecordSource(); + const auto cardinality = rsb->getCardinality(); + + if (cardinality > maxCardinality1) + { + maxCardinality2 = maxCardinality1; + maxCardinality1 = cardinality; + maxCardinalityPosition = rivers.getCount(); + } + else if (cardinality > maxCardinality2) + maxCardinality2 = cardinality; + + streams.join(river->getStreams()); + rivers.add(river); + orgRivers.remove(iter); + + // Collect keys to join on + + keys.add(FB_NEW_POOL(getPool()) NestValueArray(getPool())); + + for (const auto eq_class : selected_classes) + keys.back()->add(eq_class[position]); + } + + const bool hashOverflow = (maxCardinality2 > HashJoin::maxCapacity()); + + // If any of to-be-hashed rivers is too large to be hashed efficiently, + // then prefer a merge join instead of a hash join. + + const bool useMergeJoin = hashOverflow; + + // Build a join stream + + HalfStaticArray rsbs; + RecordSource* finalRsb = nullptr; + + if (useMergeJoin) + { + position = 0; + for (const auto river : rivers) + { + const auto sort = FB_NEW_POOL(getPool()) SortNode(getPool()); + + for (const auto key : *keys[position++]) + { + fb_assert(river->isReferenced(key)); + + sort->direction.add(ORDER_ASC); // ascending sort + sort->nullOrder.add(NULLS_DEFAULT); // default nulls placement + sort->expressions.add(key); + } + + const auto rsb = generateSort(river->getStreams(), nullptr, + river->getRecordSource(), sort, favorFirstRows(), false); + + rsbs.add(rsb); + } + + finalRsb = FB_NEW_POOL(getPool()) + MergeJoin(csb, rsbs.getCount(), (SortedStream**) rsbs.begin(), keys.begin()); + } + else + { + // Ensure that the largest river is placed at the first position. + // It's important for a hash join to be efficient. + + const auto maxCardinalityRiver = rivers[maxCardinalityPosition]; + rivers[maxCardinalityPosition] = rivers[0]; + rivers[0] = maxCardinalityRiver; + + const auto maxCardinalityKey = keys[maxCardinalityPosition]; + keys[maxCardinalityPosition] = keys[0]; + keys[0] = maxCardinalityKey; + + for (const auto river : rivers) + rsbs.add(river->getRecordSource()); + + finalRsb = FB_NEW_POOL(getPool()) + HashJoin(tdbb, csb, rsbs.getCount(), rsbs.begin(), keys.begin()); + } + + // Pick up any boolean that may apply + finalRsb = applyLocalBoolean(finalRsb, streams, iter); + + const auto finalRiver = FB_NEW_POOL(getPool()) River(csb, finalRsb, rivers); + orgRivers.insert(lowestPosition, finalRiver); + + return true; +} + + +// +// Find all indexed relationships between streams, +// then form streams into rivers (combinations of streams) +// + +void Optimizer::generateInnerJoin(StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause) +{ + if (streams.isEmpty()) + return; + + if (planClause && streams.getCount() > 1) + { + // this routine expects a join/merge + formRivers(streams, rivers, sortClause, planClause); + return; + } + + InnerJoin innerJoin(tdbb, this, streams, + sortClause, (planClause != nullptr)); + + while (innerJoin.findJoinOrder()) + { + const auto river = innerJoin.formRiver(); + rivers.add(river); + + // Remove already consumed streams from the source stream list + for (const auto stream : river->getStreams()) + { + FB_SIZE_T pos; + if (streams.find(stream, pos)) + streams.remove(pos); + else + fb_assert(false); + } + } +} + + +// +// Generate a top level outer join. The "outer" and "inner" sub-streams must be +// handled differently from each other. The inner is like other streams. +// The outer one isn't because conjuncts may not eliminate records from the stream. +// They only determine if a join with an inner stream record is to be attempted. +// + +RecordSource* Optimizer::generateOuterJoin(RiverList& rivers, + SortNode** sortClause) +{ + struct { + RecordSource* stream_rsb; + StreamType stream_num; + } stream_o, stream_i, *stream_ptr[2]; + + // Determine which stream should be outer and which is inner. + // In the case of a left join, the syntactically left stream is the + // outer, and the right stream is the inner. For all others, swap + // the sense of inner and outer, though for a full join it doesn't + // matter and we should probably try both orders to see which is + // more efficient. + if (rse->rse_jointype != blr_left) + { + stream_ptr[1] = &stream_o; + stream_ptr[0] = &stream_i; + } + else + { + stream_ptr[0] = &stream_o; + stream_ptr[1] = &stream_i; + } + + // Loop through the outer join sub-streams in + // reverse order because rivers may have been PUSHed + for (int i = 1; i >= 0; i--) + { + const auto node = rse->rse_relations[i]; + + if (nodeIs(node) || nodeIs(node)) + { + stream_ptr[i]->stream_rsb = nullptr; + stream_ptr[i]->stream_num = node->getStream(); + } + else + { + River* const river = rivers.pop(); + stream_ptr[i]->stream_rsb = river->getRecordSource(); + } + } + + if (!isFullJoin()) + { + // Generate rsbs for the sub-streams. + // For the left sub-stream we also will get a boolean back. + BoolExprNode* boolean = nullptr; + + if (!stream_o.stream_rsb) + { + stream_o.stream_rsb = + generateRetrieval(stream_o.stream_num, sortClause, true, false, &boolean); + } + + if (!stream_i.stream_rsb) + { + // AB: the sort clause for the inner stream of an OUTER JOIN + // should never be used for the index retrieval + stream_i.stream_rsb = + generateRetrieval(stream_i.stream_num, nullptr, false, true); + } + + // generate a parent boolean rsb for any remaining booleans that + // were not satisfied via an index lookup + stream_i.stream_rsb = generateResidualBoolean(stream_i.stream_rsb); + + // Allocate and fill in the rsb + return FB_NEW_POOL(getPool()) + NestedLoopJoin(csb, stream_o.stream_rsb, stream_i.stream_rsb, + boolean, OUTER_JOIN); + } + + bool hasOuterRsb = true, hasInnerRsb = true; + BoolExprNode* boolean = nullptr; + + if (!stream_o.stream_rsb) + { + hasOuterRsb = false; + stream_o.stream_rsb = + generateRetrieval(stream_o.stream_num, nullptr, true, false, &boolean); + } + + if (!stream_i.stream_rsb) + { + hasInnerRsb = false; + stream_i.stream_rsb = + generateRetrieval(stream_i.stream_num, nullptr, false, true); + } + + const auto innerRsb = generateResidualBoolean(stream_i.stream_rsb); + + const auto rsb1 = FB_NEW_POOL(getPool()) + NestedLoopJoin(csb, stream_o.stream_rsb, innerRsb, boolean, OUTER_JOIN); + + for (auto iter = getConjuncts(); iter.hasData(); ++iter) + { + if (iter & CONJUNCT_USED) + iter.reset(CMP_clone_node_opt(tdbb, csb, iter)); + } + + if (!hasInnerRsb) + csb->csb_rpt[stream_i.stream_num].deactivate(); + + if (!hasOuterRsb) + csb->csb_rpt[stream_o.stream_num].deactivate(); + + boolean = nullptr; + + if (!hasInnerRsb) + { + stream_i.stream_rsb = + generateRetrieval(stream_i.stream_num, nullptr, true, false, &boolean); + } + + if (!hasOuterRsb) + { + stream_o.stream_rsb = + generateRetrieval(stream_o.stream_num, nullptr, false, false); + } + + const auto outerRsb = generateResidualBoolean(stream_o.stream_rsb); + + const auto rsb2 = FB_NEW_POOL(getPool()) + NestedLoopJoin(csb, stream_i.stream_rsb, outerRsb, boolean, ANTI_JOIN); + + return FB_NEW_POOL(getPool()) FullOuterJoin(csb, rsb1, rsb2); +} + + +// +// Pick up any residual boolean remaining, meaning those that have not been used +// as part of some join. These booleans must still be applied to the result stream. +// + +RecordSource* Optimizer::generateResidualBoolean(RecordSource* rsb) +{ + BoolExprNode* boolean = nullptr; + double selectivity = MAXIMUM_SELECTIVITY; + + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED)) + { + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; + + if (!(iter & CONJUNCT_MATCHED)) + selectivity *= getSelectivity(*iter); + } + } + + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, selectivity) : rsb; +} + + +// +// Compile a record retrieval source +// + +RecordSource* Optimizer::generateRetrieval(StreamType stream, + SortNode** sortClause, + bool outerFlag, + bool innerFlag, + BoolExprNode** returnBoolean) +{ + const auto tail = &csb->csb_rpt[stream]; + const auto relation = tail->csb_relation; + fb_assert(relation); + + const string alias = makeAlias(stream); + tail->activate(); + + // Time to find inversions. For each index on the relation + // match all unused booleans against the index looking for upper + // and lower bounds that can be computed by the index. When + // all unused conjunctions are exhausted, see if there is enough + // information for an index retrieval. If so, build up an + // inversion component of the boolean. + + RecordSource* rsb = nullptr; + InversionNode* inversion = nullptr; + BoolExprNode* condition = nullptr; + Array dbkeyRanges; + double scanSelectivity = MAXIMUM_SELECTIVITY; + + if (relation->rel_file) + { + // External table + rsb = FB_NEW_POOL(getPool()) ExternalTableScan(csb, alias, stream, relation); + } + else if (relation->isVirtual()) + { + // Virtual table: monitoring or security + switch (relation->rel_id) + { + case rel_global_auth_mapping: + rsb = FB_NEW_POOL(getPool()) GlobalMappingScan(csb, alias, stream, relation); + break; + + case rel_sec_users: + case rel_sec_user_attributes: + rsb = FB_NEW_POOL(getPool()) UsersTableScan(csb, alias, stream, relation); + break; + + case rel_sec_db_creators: + rsb = FB_NEW_POOL(getPool()) DbCreatorsScan(csb, alias, stream, relation); + break; + + case rel_time_zones: + rsb = FB_NEW_POOL(getPool()) TimeZonesTableScan(csb, alias, stream, relation); + break; + + case rel_config: + rsb = FB_NEW_POOL(getPool()) ConfigTableScan(csb, alias, stream, relation); + break; + + case rel_keywords: + rsb = FB_NEW_POOL(getPool()) KeywordsTableScan(csb, alias, stream, relation); + break; + + default: + rsb = FB_NEW_POOL(getPool()) MonitoringTableScan(csb, alias, stream, relation); + break; + } + } + else + { + // Persistent table + Retrieval retrieval(tdbb, this, stream, outerFlag, innerFlag, + (sortClause ? *sortClause : nullptr), false); + const auto candidate = retrieval.getInversion(); + + if (candidate) + { + inversion = candidate->inversion; + condition = candidate->condition; + dbkeyRanges.assign(candidate->dbkeyRanges); + scanSelectivity = candidate->selectivity; + + // Just for safety sake, this condition must be already checked + // inside OptimizerRetrieval::matchOnIndexes() + + if (inversion && condition && + !condition->computable(csb, stream, false)) + { + fb_assert(false); + inversion = nullptr; + condition = nullptr; + dbkeyRanges.clear(); + } + } + + const auto navigation = retrieval.getNavigation(); + + if (navigation) + { + if (sortClause) + *sortClause = nullptr; + + navigation->setInversion(inversion, condition); + + rsb = navigation; + } + } + + if (outerFlag) + { + fb_assert(returnBoolean); + *returnBoolean = nullptr; + + // Now make another pass thru the outer conjuncts only, finding unused, + // computable booleans. When one is found, roll it into a final + // boolean and mark it used. + for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) + { + compose(getPool(), returnBoolean, iter); + iter |= CONJUNCT_USED; + } + } + } + + // Now make another pass thru the conjuncts finding unused, computable + // booleans. When one is found, roll it into a final boolean and mark + // it used. If a computable boolean didn't match against an index then + // mark the stream to denote unmatched booleans. + BoolExprNode* boolean = nullptr; + double filterSelectivity = MAXIMUM_SELECTIVITY; + + for (auto iter = getConjuncts(outerFlag, innerFlag); iter.hasData(); ++iter) + { + if (!(iter & CONJUNCT_USED) && + !(iter->nodFlags & ExprNode::FLAG_RESIDUAL) && + iter->computable(csb, INVALID_STREAM, false)) + { + // If inversion is available, utilize all conjuncts that refer to + // the stream being retrieved. Otherwise, utilize only conjuncts + // that are local to this stream. The remaining ones are left in piece + // as possible candidates for a merge/hash join. + + if ((inversion && iter->containsStream(stream)) || + (!inversion && iter->computable(csb, stream, true))) + { + compose(getPool(), &boolean, iter); + iter |= CONJUNCT_USED; + + if (!(iter & CONJUNCT_MATCHED)) + { + if (!outerFlag) + tail->csb_flags |= csb_unmatched; + + filterSelectivity *= getSelectivity(*iter); + } + } + } + } + + if (!rsb) + { + if (inversion && condition) + { + RecordSource* const rsb1 = + FB_NEW_POOL(getPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); + RecordSource* const rsb2 = + FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, + inversion, scanSelectivity); + + rsb = FB_NEW_POOL(getPool()) ConditionalStream(csb, rsb1, rsb2, condition); + } + else if (inversion) + { + rsb = FB_NEW_POOL(getPool()) BitmapTableScan(csb, alias, stream, relation, + inversion, scanSelectivity); + } + else + { + rsb = FB_NEW_POOL(getPool()) FullTableScan(csb, alias, stream, relation, dbkeyRanges); + + if (boolean) + csb->csb_rpt[stream].csb_flags |= csb_unmatched; + } + } + + return boolean ? FB_NEW_POOL(getPool()) FilteredStream(csb, rsb, boolean, filterSelectivity) : rsb; +} + + +// +// Check whether the given boolean can be involved in a equi-join relationship +// + +bool Optimizer::getEquiJoinKeys(BoolExprNode* boolean, + NestConst* node1, + NestConst* node2, + bool needCast) +{ + auto cmpNode = nodeAs(boolean); + if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv)) + return false; + + auto arg1 = cmpNode->arg1; + auto arg2 = cmpNode->arg2; + + if (!getEquiJoinKeys(arg1, arg2, needCast)) + return false; + + *node1 = arg1; + *node2 = arg2; + return true; +} + +bool Optimizer::getEquiJoinKeys(NestConst& node1, + NestConst& node2, + bool needCast) +{ + dsc result, desc1, desc2; + node1->getDesc(tdbb, csb, &desc1); + node2->getDesc(tdbb, csb, &desc2); + + // Ensure that arguments can be compared in the binary form + if (!CVT2_get_binary_comparable_desc(&result, &desc1, &desc2)) + return false; + + // Cast the arguments to the common data type, if required + if (needCast) + { + if (!DSC_EQUIV(&result, &desc1, true)) + { + const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); + cast->source = node1; + cast->castDesc = result; + cast->impureOffset = csb->allocImpure(); + node1 = cast; + } + + if (!DSC_EQUIV(&result, &desc2, true)) + { + const auto cast = FB_NEW_POOL(getPool()) CastNode(getPool()); + cast->source = node2; + cast->castDesc = result; + cast->impureOffset = csb->allocImpure(); + node2 = cast; + } + } + + return true; +} + + +// +// Compose a table name (including alias, if specified) for the given stream +// + +string Optimizer::getStreamName(StreamType stream) +{ + const auto tail = &csb->csb_rpt[stream]; + const auto relation = tail->csb_relation; + const auto procedure = tail->csb_procedure; + const auto alias = tail->csb_alias; + + string name; + + if (relation) + name = relation->rel_name.c_str(); + else if (procedure) + name = procedure->getName().toString(); + + if (alias && alias->hasData()) + { + if (name.hasData()) + name += " as "; + name += *alias; + } + + return name; +} + + +// +// Make an alias string suitable for printing as part of the plan. +// For views, this means multiple aliases to distinguish the base table. +// + +string Optimizer::makeAlias(StreamType stream) +{ + string alias; + + const CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; + + if (csb_tail->csb_view || csb_tail->csb_alias) + { + ObjectsArray alias_list; + + while (csb_tail) + { + if (csb_tail->csb_alias) + alias_list.push(*csb_tail->csb_alias); + else if (csb_tail->csb_relation) + alias_list.push(csb_tail->csb_relation->rel_name.c_str()); + + if (!csb_tail->csb_view) + break; + + csb_tail = &csb->csb_rpt[csb_tail->csb_view_stream]; + } + + while (alias_list.hasData()) + { + alias += alias_list.pop(); + + if (alias_list.hasData()) + alias += ' '; + } + } + else if (csb_tail->csb_relation) + alias = csb_tail->csb_relation->rel_name.c_str(); + else if (csb_tail->csb_procedure) + alias = csb_tail->csb_procedure->getName().toString(); + //// TODO: LocalTableSourceNode + else + fb_assert(false); + + return alias; +} + + +// +// From the predicate, boolean, and infer a new predicate using arg1 & arg2 as the first two +// parameters to the predicate. +// +// This is used when the engine knows A(boolean); + fb_assert(cmpNode); // see our caller + + // Clone the input predicate + const auto newCmpNode = + FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), cmpNode->blrOp); + + // We may safely copy invariantness flag because + // (1) we only distribute field equalities + // (2) invariantness of second argument of STARTING WITH or LIKE is solely + // determined by its dependency on any of the fields + // If provisions above change the line below will have to be modified + newCmpNode->nodFlags = cmpNode->nodFlags; + + // Share impure area for cached invariant value used to hold pre-compiled + // pattern for new LIKE and CONTAINING algorithms. + // Proper cloning of impure area for this node would require careful accounting + // of new invariant dependencies - we avoid such hassles via using single + // cached pattern value for all node clones. This is faster too. + if (newCmpNode->nodFlags & ExprNode::FLAG_INVARIANT) + newCmpNode->impureOffset = cmpNode->impureOffset; + + // But substitute new values for some of the predicate arguments + newCmpNode->arg1 = CMP_clone_node_opt(tdbb, csb, arg1); + newCmpNode->arg2 = CMP_clone_node_opt(tdbb, csb, arg2); + + // Arguments after the first two are just cloned (eg: LIKE ESCAPE clause) + if (cmpNode->arg3) + newCmpNode->arg3 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg3); + + return newCmpNode; +} + + +// +// Optimize a LIKE/SIMILAR expression, if possible, into a "STARTING WITH" AND a "LIKE/SIMILAR". +// This will allow us to use the index for the starting with, and the LIKE/SIMILAR can just tag +// along for the ride. But on the ride it does useful work, consider match LIKE/SIMILAR "ab%c". +// This is optimized by adding AND STARTING WITH "ab", but the LIKE/SIMILAR clause is still needed. +// + +ValueExprNode* Optimizer::optimizeLikeSimilar(ComparativeBoolNode* cmpNode) +{ + ValueExprNode* matchNode = cmpNode->arg1; + ValueExprNode* patternNode = cmpNode->arg2; + ValueExprNode* escapeNode = cmpNode->arg3; + + // if the pattern string or the escape string can't be + // evaluated at compile time, forget it + if (!nodeIs(patternNode) || (escapeNode && !nodeIs(escapeNode))) + return nullptr; + + dsc matchDesc; + matchNode->getDesc(tdbb, csb, &matchDesc); + + dsc* patternDesc = &nodeAs(patternNode)->litDesc; + dsc* escapeDesc = nullptr; + + if (escapeNode) + escapeDesc = &nodeAs(escapeNode)->litDesc; + + // if either is not a character expression, forget it + if ((matchDesc.dsc_dtype > dtype_any_text) || + (patternDesc->dsc_dtype > dtype_any_text) || + (escapeNode && escapeDesc->dsc_dtype > dtype_any_text)) + { + return nullptr; + } + + TextType* matchTextType = INTL_texttype_lookup(tdbb, INTL_TTYPE(&matchDesc)); + CharSet* matchCharset = matchTextType->getCharSet(); + TextType* patternTextType = INTL_texttype_lookup(tdbb, INTL_TTYPE(patternDesc)); + CharSet* patternCharset = patternTextType->getCharSet(); + + if (cmpNode->blrOp == blr_like) + { + UCHAR escape_canonic[sizeof(ULONG)]; + UCHAR first_ch[sizeof(ULONG)]; + ULONG first_len; + UCHAR* p; + USHORT p_count; + MoveBuffer escapeBuffer; + + // Get the escape character, if any + if (escapeNode) + { + // Ensure escape string is same character set as match string + p_count = MOV_make_string2(tdbb, escapeDesc, INTL_TTYPE(&matchDesc), &p, escapeBuffer); + + first_len = matchCharset->substring(p_count, p, sizeof(first_ch), first_ch, 0, 1); + matchTextType->canonical(first_len, p, sizeof(escape_canonic), escape_canonic); + } + + MoveBuffer patternBuffer; + p_count = MOV_make_string2(tdbb, patternDesc, INTL_TTYPE(&matchDesc), &p, patternBuffer); + + first_len = matchCharset->substring(p_count, p, sizeof(first_ch), first_ch, 0, 1); + + UCHAR first_canonic[sizeof(ULONG)]; + matchTextType->canonical(first_len, p, sizeof(first_canonic), first_canonic); + + const BYTE canWidth = matchTextType->getCanonicalWidth(); + + const UCHAR* matchOneChar = matchCharset->getSqlMatchOneLength() != 0 ? + matchTextType->getCanonicalChar(TextType::CHAR_SQL_MATCH_ONE) : nullptr; + const UCHAR* matchAnyChar = matchCharset->getSqlMatchAnyLength() != 0 ? + matchTextType->getCanonicalChar(TextType::CHAR_SQL_MATCH_ANY) : nullptr; + + // If the first character is a wildcard char, forget it. + if ((!escapeNode || memcmp(first_canonic, escape_canonic, canWidth) != 0) && + ((matchOneChar && memcmp(first_canonic, matchOneChar, canWidth) == 0) || + (matchAnyChar && memcmp(first_canonic, matchAnyChar, canWidth) == 0))) + { + return nullptr; + } + + // allocate a literal node to store the starting with string; + // assume it will be shorter than the pattern string + + const auto literal = FB_NEW_POOL(getPool()) LiteralNode(getPool()); + literal->litDesc = *patternDesc; + UCHAR* q = literal->litDesc.dsc_address = FB_NEW_POOL(getPool()) UCHAR[literal->litDesc.dsc_length]; + + // Set the string length to point till the first wildcard character. + + HalfStaticArray patternCanonical; + ULONG patternCanonicalLen = p_count / matchCharset->minBytesPerChar() * canWidth; + + patternCanonicalLen = matchTextType->canonical(p_count, p, + patternCanonicalLen, patternCanonical.getBuffer(patternCanonicalLen)); + + for (const UCHAR* patternPtr = patternCanonical.begin(); patternPtr < patternCanonical.end(); ) + { + // if there are escape characters, skip past them and don't treat the next char as a wildcard + const UCHAR* patternPtrStart = patternPtr; + patternPtr += canWidth; + + if (escapeNode && (memcmp(patternPtrStart, escape_canonic, canWidth) == 0)) + { + // Check for Escape character at end of string + if (!(patternPtr < patternCanonical.end())) + break; + + patternPtrStart = patternPtr; + patternPtr += canWidth; + } + else if ((matchOneChar && memcmp(patternPtrStart, matchOneChar, canWidth) == 0) || + (matchAnyChar && memcmp(patternPtrStart, matchAnyChar, canWidth) == 0)) + { + break; + } + + q += patternCharset->substring(patternDesc->dsc_length, + patternDesc->dsc_address, + literal->litDesc.dsc_length - (q - literal->litDesc.dsc_address), q, + (patternPtrStart - patternCanonical.begin()) / canWidth, 1); + } + + literal->litDesc.dsc_length = q - literal->litDesc.dsc_address; + + return literal; + } + else + { + fb_assert(cmpNode->blrOp == blr_similar); + + MoveBuffer escapeBuffer; + UCHAR* escapeStart = nullptr; + ULONG escapeLen = 0; + + // Get the escape character, if any + if (escapeNode) + { + // Ensure escape string is same character set as match string + escapeLen = MOV_make_string2(tdbb, escapeDesc, INTL_TTYPE(&matchDesc), &escapeStart, escapeBuffer); + } + + MoveBuffer patternBuffer; + UCHAR* patternStart; + ULONG patternLen = MOV_make_string2(tdbb, patternDesc, INTL_TTYPE(&matchDesc), &patternStart, patternBuffer); + const auto patternEnd = patternStart + patternLen; + const UCHAR* patternPtr = patternStart; + + MoveBuffer prefixBuffer; + ULONG charLen = 0; + + while (IntlUtil::readOneChar(matchCharset, &patternPtr, patternEnd, &charLen)) + { + if (escapeNode && charLen == escapeLen && memcmp(patternPtr, escapeStart, escapeLen) == 0) + { + if (!IntlUtil::readOneChar(matchCharset, &patternPtr, patternEnd, &charLen) || + !((charLen == escapeLen && memcmp(patternPtr, escapeStart, escapeLen) == 0) || + (charLen == 1 && SimilarToRegex::isSpecialChar(*patternPtr)))) + { + // Invalid escape. + return nullptr; + } + } + else if (charLen == 1 && SimilarToRegex::isSpecialChar(*patternPtr)) + break; + + prefixBuffer.push(patternPtr, charLen); + } + + if (prefixBuffer.isEmpty()) + return nullptr; + + // Allocate a literal node to store the starting with string. + // Use the match text type as the pattern string is converted to it. + + const auto literal = FB_NEW_POOL(getPool()) LiteralNode(getPool()); + literal->litDesc.makeText(prefixBuffer.getCount(), INTL_TTYPE(&matchDesc), + FB_NEW_POOL(getPool()) UCHAR[prefixBuffer.getCount()]); + memcpy(literal->litDesc.dsc_address, prefixBuffer.begin(), prefixBuffer.getCount()); + + return literal; + } +} + +void Optimizer::printf(const char* format, ...) +{ +#ifndef OPT_DEBUG_SYS_REQUESTS + if (csb->csb_g_flags & csb_internal) + return; +#endif + +#ifdef OPT_DEBUG + if (!debugFile) + debugFile = os_utils::fopen(OPTIMIZER_DEBUG_FILE, "a"); + + fb_assert(debugFile); + + va_list arglist; + va_start(arglist, format); + Firebird::string str; + str.vprintf(format, arglist); + va_end(arglist); + + fprintf(debugFile, "%s", str.c_str()); + fflush(debugFile); +#endif +} diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h new file mode 100644 index 0000000000..a7e44db850 --- /dev/null +++ b/src/jrd/optimizer/Optimizer.h @@ -0,0 +1,798 @@ +/* + * PROGRAM: Client/Server Common Code + * MODULE: Optimizer.h + * DESCRIPTION: Optimizer + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Arno Brinkman + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2004 Arno Brinkman + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef OPTIMIZER_H +#define OPTIMIZER_H + +//#define OPT_DEBUG +//#define OPT_DEBUG_RETRIEVAL +//#define OPT_DEBUG_SYS_REQUESTS + +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../dsql/BoolNodes.h" +#include "../dsql/ExprNodes.h" +#include "../jrd/RecordSourceNodes.h" +#include "../jrd/exe.h" + +namespace Jrd { + +// AB: 2005-11-05 +// Constants below needs some discussions and ideas +const double REDUCE_SELECTIVITY_FACTOR_BETWEEN = 0.0025; +const double REDUCE_SELECTIVITY_FACTOR_LESS = 0.05; +const double REDUCE_SELECTIVITY_FACTOR_GREATER = 0.05; +const double REDUCE_SELECTIVITY_FACTOR_STARTING = 0.01; + +const double REDUCE_SELECTIVITY_FACTOR_EQUALITY = 0.1; +const double REDUCE_SELECTIVITY_FACTOR_INEQUALITY = 0.3; + +const double MAXIMUM_SELECTIVITY = 1.0; +const double DEFAULT_SELECTIVITY = 0.1; + +const double MINIMUM_CARDINALITY = 1.0; +const double THRESHOLD_CARDINALITY = 5.0; +const double DEFAULT_CARDINALITY = 1000.0; + +// Default depth of an index tree (including one leaf page), +// also representing the minimal cost of the index scan. +// We assume that the root page would be always cached, +// so it's not included here. +const double DEFAULT_INDEX_COST = 3.0; + + +struct index_desc; +class jrd_rel; +class IndexTableScan; +class ComparativeBoolNode; +class InversionNode; +class PlanNode; +class SortNode; +class River; +class SortedStream; + + +// +// StreamStateHolder +// + +class StreamStateHolder +{ +public: + explicit StreamStateHolder(CompilerScratch* csb) + : m_csb(csb), m_streams(csb->csb_pool), m_flags(csb->csb_pool) + { + for (StreamType stream = 0; stream < csb->csb_n_stream; stream++) + m_streams.add(stream); + + init(); + } + + StreamStateHolder(CompilerScratch* csb, const StreamList& streams) + : m_csb(csb), m_streams(csb->csb_pool), m_flags(csb->csb_pool) + { + m_streams.assign(streams); + + init(); + } + + ~StreamStateHolder() + { + for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) + { + const StreamType stream = m_streams[i]; + + if (m_flags[i >> 3] & (1 << (i & 7))) + m_csb->csb_rpt[stream].activate(); + else + m_csb->csb_rpt[stream].deactivate(); + } + } + + void activate(bool subStream = false) + { + for (const auto stream : m_streams) + m_csb->csb_rpt[stream].activate(subStream); + } + + void deactivate() + { + for (const auto stream : m_streams) + m_csb->csb_rpt[stream].deactivate(); + } + +private: + void init() + { + m_flags.resize(FLAG_BYTES(m_streams.getCount())); + + for (FB_SIZE_T i = 0; i < m_streams.getCount(); i++) + { + const StreamType stream = m_streams[i]; + + if (m_csb->csb_rpt[stream].csb_flags & csb_active) + m_flags[i >> 3] |= (1 << (i & 7)); + } + } + + CompilerScratch* const m_csb; + StreamList m_streams; + Firebird::HalfStaticArray m_flags; +}; + + +// +// River +// + +typedef Firebird::HalfStaticArray RiverList; + +class River +{ +public: + River(CompilerScratch* csb, RecordSource* rsb, RecordSourceNode* node, const StreamList& streams) + : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool, streams) + { + if (node) + m_nodes.add(node); + } + + River(CompilerScratch* csb, RecordSource* rsb, RiverList& rivers) + : m_rsb(rsb), m_nodes(csb->csb_pool), m_streams(csb->csb_pool) + { + for (const auto subRiver : rivers) + { + m_nodes.join(subRiver->m_nodes); + m_streams.join(subRiver->m_streams); + } + } + + RecordSource* getRecordSource() const + { + return m_rsb; + } + + const StreamList& getStreams() const + { + return m_streams; + } + + void activate(CompilerScratch* csb) const + { + for (const auto stream : m_streams) + csb->csb_rpt[stream].activate(); + } + + void deactivate(CompilerScratch* csb) const + { + for (const auto stream : m_streams) + csb->csb_rpt[stream].deactivate(); + } + + bool isReferenced(const ExprNode* node) const + { + SortedStreamList nodeStreams; + node->collectStreams(nodeStreams); + + if (!nodeStreams.hasData()) + return false; + + for (const auto stream : nodeStreams) + { + if (!m_streams.exist(stream)) + return false; + } + + return true; + } + + bool isComputable(CompilerScratch* csb) const + { + for (const auto node : m_nodes) + { + if (!node->computable(csb, INVALID_STREAM, false)) + return false; + } + + return true; + } + +protected: + RecordSource* m_rsb; + Firebird::HalfStaticArray m_nodes; + StreamList m_streams; +}; + + +// +// Optimizer +// + +class Optimizer : public Firebird::PermanentStorage +{ +public: + struct Conjunct + { + // Conjunctions and their options + BoolExprNode* node; + unsigned flags; + }; + + static const unsigned CONJUNCT_USED = 1; // conjunct is used + static const unsigned CONJUNCT_MATCHED = 2; // conjunct matches an index segment + + typedef Firebird::HalfStaticArray ConjunctList; + + class ConjunctIterator + { + friend class Optimizer; + + public: + operator BoolExprNode*() const + { + return iter->node; + } + + BoolExprNode* operator->() const + { + return iter->node; + } + + BoolExprNode* operator*() const + { + return iter->node; + } + + unsigned operator&(unsigned flags) const + { + return (iter->flags & flags); + } + + void operator|=(unsigned flags) + { + iter->flags |= flags; + } + + void operator++() + { + iter++; + } + + bool hasData() const + { + return (iter < end); + } + + void rewind() + { + iter = begin; + } + + void reset(BoolExprNode* node) + { + iter->node = node; + iter->flags = 0; + } + + private: + Conjunct* const begin; + const Conjunct* const end; + Conjunct* iter; + + ConjunctIterator(Conjunct* _begin, const Conjunct* _end) + : begin(_begin), end(_end) + { + rewind(); + } + + explicit ConjunctIterator(const ConjunctIterator& other) + : begin(other.begin), end(other.end), iter(other.iter) + {} + }; + + ConjunctIterator getBaseConjuncts() + { + const auto begin = conjuncts.begin(); + const auto end = begin + baseConjuncts; + + return ConjunctIterator(begin, end); + } + + ConjunctIterator getParentConjuncts() + { + const auto begin = conjuncts.begin() + baseParentConjuncts; + const auto end = conjuncts.end(); + + return ConjunctIterator(begin, end); + } + + ConjunctIterator getConjuncts(bool outer = false, bool inner = false) + { + const auto begin = conjuncts.begin() + (outer ? baseParentConjuncts : 0); + const auto end = inner ? begin + baseMissingConjuncts : conjuncts.end(); + + return ConjunctIterator(begin, end); + } + + static Firebird::string getPlan(thread_db* tdbb, const Statement* statement, bool detailed) + { + return statement ? statement->getPlan(tdbb, detailed) : ""; + } + + static double getSelectivity(const BoolExprNode* node) + { + const auto cmpNode = nodeAs(node); + + return (cmpNode && cmpNode->blrOp == blr_eql) ? + REDUCE_SELECTIVITY_FACTOR_EQUALITY : + REDUCE_SELECTIVITY_FACTOR_INEQUALITY; + } + + static void adjustSelectivity(double& selectivity, double factor, double cardinality) + { + if (cardinality) + { + const auto minSelectivity = 1 / cardinality; + const auto diffSelectivity = selectivity > minSelectivity ? + selectivity - minSelectivity : 0; + selectivity = minSelectivity + diffSelectivity * factor; + } + } + + static RecordSource* compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse) + { + return Optimizer(tdbb, csb, rse).compile(nullptr); + } + + ~Optimizer(); + + RecordSource* compile(RseNode* subRse, BoolExprNodeStack* parentStack); + void compileRelation(StreamType stream); + void generateAggregateDistincts(MapNode* map); + RecordSource* generateRetrieval(StreamType stream, + SortNode** sortClause, + bool outerFlag, + bool innerFlag, + BoolExprNode** returnBoolean = nullptr); + SortedStream* generateSort(const StreamList& streams, + const StreamList* dbkeyStreams, + RecordSource* rsb, SortNode* sort, + bool refetchFlag, bool projectFlag); + + CompilerScratch* getCompilerScratch() const + { + return csb; + } + + bool isInnerJoin() const + { + return (rse->rse_jointype == blr_inner); + } + + bool isLeftJoin() const + { + return (rse->rse_jointype == blr_left); + } + + bool isFullJoin() const + { + return (rse->rse_jointype == blr_full); + } + + const StreamList& getOuterStreams() const + { + return outerStreams; + } + + bool favorFirstRows() const + { + return (rse->flags & RseNode::FLAG_OPT_FIRST_ROWS) != 0; + } + + bool getEquiJoinKeys(BoolExprNode* boolean, + NestConst* node1, + NestConst* node2, + bool needCast = false); + bool getEquiJoinKeys(NestConst& node1, + NestConst& node2, + bool needCast = false); + + Firebird::string getStreamName(StreamType stream); + Firebird::string makeAlias(StreamType stream); + void printf(const char* format, ...); + +private: + Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse); + + RecordSource* compile(BoolExprNodeStack* parentStack); + + RecordSource* applyLocalBoolean(RecordSource* rsb, + const StreamList& streams, + ConjunctIterator& iter); + void checkIndices(); + void checkSorts(); + unsigned decompose(BoolExprNode* boolNode, BoolExprNodeStack& stack); + unsigned distributeEqualities(BoolExprNodeStack& orgStack, unsigned baseCount); + void findDependentStreams(const StreamList& streams, + StreamList& dependent_streams, + StreamList& free_streams); + void formRivers(const StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause); + bool generateEquiJoin(RiverList& org_rivers); + void generateInnerJoin(StreamList& streams, + RiverList& rivers, + SortNode** sortClause, + const PlanNode* planClause); + RecordSource* generateOuterJoin(RiverList& rivers, + SortNode** sortClause); + RecordSource* generateResidualBoolean(RecordSource* rsb); + BoolExprNode* makeInferenceNode(BoolExprNode* boolean, + ValueExprNode* arg1, + ValueExprNode* arg2); + ValueExprNode* optimizeLikeSimilar(ComparativeBoolNode* cmpNode); + + thread_db* const tdbb; + CompilerScratch* const csb; + RseNode* const rse; + + FILE* debugFile = nullptr; + unsigned baseConjuncts = 0; // number of conjuncts in our rse, next conjuncts are distributed parent + unsigned baseParentConjuncts = 0; // number of conjuncts in our rse + distributed with parent, next are parent + unsigned baseMissingConjuncts = 0; // number of conjuncts in our and parent rse, but without missing + + StreamList compileStreams, bedStreams, keyStreams, subStreams, outerStreams; + ConjunctList conjuncts; +}; + + +// +// IndexScratch +// + +enum segmentScanType { + segmentScanNone, + segmentScanGreater, + segmentScanLess, + segmentScanBetween, + segmentScanEqual, + segmentScanEquivalent, + segmentScanMissing, + segmentScanStarting +}; + +typedef Firebird::HalfStaticArray MatchedBooleanList; + +struct IndexScratchSegment +{ + explicit IndexScratchSegment(MemoryPool& p) + : matches(p) + {} + + explicit IndexScratchSegment(MemoryPool& p, const IndexScratchSegment& other) + : lowerValue(other.lowerValue), + upperValue(other.upperValue), + excludeLower(other.excludeLower), + excludeUpper(other.excludeUpper), + scope(other.scope), + scanType(other.scanType), + matches(p, other.matches) + {} + + ValueExprNode* lowerValue = nullptr; // lower bound on index value + ValueExprNode* upperValue = nullptr; // upper bound on index value + bool excludeLower = false; // exclude lower bound value from scan + bool excludeUpper = false; // exclude upper bound value from scan + unsigned scope = 0; // highest scope level + segmentScanType scanType = segmentScanNone; // scan type + + MatchedBooleanList matches; // matched booleans +}; + +struct IndexScratch +{ + IndexScratch(MemoryPool& p, index_desc* idx, double cardinality); + IndexScratch(MemoryPool& p, const IndexScratch& other); + + index_desc* index = nullptr; // index descriptor + double cardinality = 0; // estimated cardinality of the whole index + double selectivity = MAXIMUM_SELECTIVITY; // calculated selectivity for this index + bool candidate = false; // used when deciding which indices to use + bool scopeCandidate = false; // used when making inversion based on scope + unsigned lowerCount = 0; + unsigned upperCount = 0; + unsigned nonFullMatchedSegments = 0; + bool usePartialKey = false; // Use INTL_KEY_PARTIAL + bool useMultiStartingKeys = false; // Use INTL_KEY_MULTI_STARTING + + Firebird::ObjectsArray segments; +}; + +typedef Firebird::ObjectsArray IndexScratchList; + +// +// InversionCandidate +// + +struct InversionCandidate +{ + explicit InversionCandidate(MemoryPool& p) + : matches(p), dbkeyRanges(p), dependentFromStreams(p) + {} + + double selectivity = MAXIMUM_SELECTIVITY; + double cost = 0; + unsigned nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; + unsigned matchedSegments = 0; + unsigned indexes = 0; + unsigned dependencies = 0; + BoolExprNode* boolean = nullptr; + BoolExprNode* condition = nullptr; + InversionNode* inversion = nullptr; + IndexScratch* scratch = nullptr; + bool used = false; + bool unique = false; + bool navigated = false; + + MatchedBooleanList matches; + Firebird::Array dbkeyRanges; + SortedStreamList dependentFromStreams; +}; + +typedef Firebird::HalfStaticArray InversionCandidateList; + + +// +// Retrieval +// + +class Retrieval : private Firebird::PermanentStorage +{ +public: + Retrieval(thread_db* tdbb, Optimizer* opt, StreamType streamNumber, + bool outer, bool inner, SortNode* sortNode, bool costOnly); + + ~Retrieval() + { + for (auto candidate : inversionCandidates) + delete candidate; + } + + InversionCandidate* getInversion(); + IndexTableScan* getNavigation(); + +protected: + void analyzeNavigation(const InversionCandidateList& inversions); + bool betterInversion(const InversionCandidate* inv1, const InversionCandidate* inv2, + bool ignoreUnmatched) const; + InversionNode* composeInversion(InversionNode* node1, InversionNode* node2, + InversionNode::Type node_type) const; + const Firebird::string& getAlias(); + void getInversionCandidates(InversionCandidateList& inversions, + IndexScratchList& indexScratches, unsigned scope) const; + InversionNode* makeIndexScanNode(IndexScratch* indexScratch) const; + InversionCandidate* makeInversion(InversionCandidateList& inversions) const; + bool matchBoolean(IndexScratch* indexScratch, BoolExprNode* boolean, unsigned scope) const; + InversionCandidate* matchDbKey(BoolExprNode* boolean) const; + InversionCandidate* matchOnIndexes(IndexScratchList& indexScratches, + BoolExprNode* boolean, unsigned scope) const; + ValueExprNode* findDbKey(ValueExprNode* dbkey, SLONG* position) const; + bool validateStarts(IndexScratch* indexScratch, ComparativeBoolNode* cmpNode, + unsigned segment) const; + +#ifdef OPT_DEBUG_RETRIEVAL + void printCandidate(const InversionCandidate* candidate) const; + void printCandidates(const InversionCandidateList& inversions) const; + void printFinalCandidate(const InversionCandidate* candidate) const; +#endif + +private: + thread_db* const tdbb; + Optimizer* const optimizer; + CompilerScratch* const csb; + const StreamType stream; + const bool innerFlag; + const bool outerFlag; + SortNode* const sort; + jrd_rel* relation; + const bool createIndexScanNodes; + const bool setConjunctionsMatched; + Firebird::string alias; + IndexScratchList indexScratches; + InversionCandidateList inversionCandidates; + Firebird::AutoPtr finalCandidate; + Firebird::AutoPtr navigationCandidate; +}; + + +// +// InnerJoin +// + +class InnerJoin : private Firebird::PermanentStorage +{ + struct IndexRelationship + { + static bool cheaperThan(const IndexRelationship& item1, const IndexRelationship& item2) + { + if (item1.cost == 0) + return true; + + if (item2.cost == 0) + return false; + + const double compare = item1.cost / item2.cost; + if (compare >= 0.98 && compare <= 1.02) + { + // cost is nearly the same, now check uniqueness and cardinality + + if (item1.unique == item2.unique) + { + if (item1.cardinality < item2.cardinality) + return true; + } + else if (item1.unique) + return true; + else if (item2.unique) + return false; + } + else if (item1.cost < item2.cost) + return true; + + return false; + } + + // Needed for SortedArray + bool operator>(const IndexRelationship& other) const + { + return !cheaperThan(*this, other); + } + + StreamType stream = 0; + bool unique = false; + double cost = 0; + double cardinality = 0; + }; + + typedef Firebird::SortedArray IndexedRelationships; + + class StreamInfo + { + public: + StreamInfo(MemoryPool& p, StreamType streamNumber) + : stream(streamNumber), indexedRelationships(p) + {} + + bool isIndependent() const + { + // Return true if this stream can't be used by other streams + // and it can't use index retrieval based on other streams + + return (indexedRelationships.isEmpty() && !previousExpectedStreams); + } + + bool isFiltered() const + { + return (baseIndexes || baseSelectivity < MAXIMUM_SELECTIVITY); + } + + static bool cheaperThan(const StreamInfo* item1, const StreamInfo* item2) + { + // First those streams which cannot be used by other streams + // or cannot depend on a stream + if (item1->isIndependent() && !item2->isIndependent()) + return true; + + // Next those with the lowest previous expected streams + const int compare = item1->previousExpectedStreams - + item2->previousExpectedStreams; + + if (compare < 0) + return true; + + // Next those with the cheapest base cost + if (item1->baseCost < item2->baseCost) + return true; + + return false; + } + + const StreamType stream; + + bool baseUnique = false; + double baseCost = 0; + double baseSelectivity = 0; + unsigned baseIndexes = 0; + bool baseNavigated = false; + bool used = false; + unsigned previousExpectedStreams = 0; + + IndexedRelationships indexedRelationships; + }; + + typedef Firebird::HalfStaticArray StreamInfoList; + + struct JoinedStreamInfo + { + // Streams and their options + StreamType bestStream; // stream in best join order seen so far + StreamType number; // stream in position of join order + }; + + typedef Firebird::HalfStaticArray JoinedStreamList; + +public: + InnerJoin(thread_db* tdbb, Optimizer* opt, + const StreamList& streams, + SortNode** sortClause, bool hasPlan); + + ~InnerJoin() + { + for (auto innerStream : innerStreams) + delete innerStream; + } + + bool findJoinOrder(); + River* formRiver(); + +protected: + void calculateStreamInfo(); + void estimateCost(unsigned position, const StreamInfo* stream, double* cost, double* resultingCardinality) const; + void findBestOrder(unsigned position, StreamInfo* stream, + IndexedRelationships& processList, double cost, double cardinality); + void getIndexedRelationships(StreamInfo* testStream); + StreamInfo* getStreamInfo(StreamType stream); +#ifdef OPT_DEBUG + void printBestOrder() const; + void printFoundOrder(StreamType position, double positionCost, + double positionCardinality, double cost, double cardinality) const; + void printProcessList(const IndexedRelationships& processList, StreamType stream) const; + void printStartOrder() const; +#endif + +private: + thread_db* const tdbb; + Optimizer* const optimizer; + CompilerScratch* const csb; + SortNode** sortPtr; + const bool plan; + + unsigned remainingStreams = 0; + unsigned bestCount = 0; // longest length of indexable streams + double bestCost = 0; // cost of best join order + + StreamInfoList innerStreams; + JoinedStreamList joinedStreams; + StreamList bestStreams; +}; + +} // namespace Jrd + +#endif // OPTIMIZER_H diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp new file mode 100644 index 0000000000..1755e8f1c4 --- /dev/null +++ b/src/jrd/optimizer/Retrieval.cpp @@ -0,0 +1,2235 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Arno Brinkman + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2004 Arno Brinkman + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Adriano dos Santos Fernandes + * Dmitry Yemanov + * + */ + +#include "firebird.h" + +#include "../jrd/jrd.h" +#include "../jrd/exe.h" +#include "../jrd/btr.h" +#include "../jrd/intl.h" +#include "../jrd/Collation.h" +#include "../jrd/ods.h" +#include "../jrd/RecordSourceNodes.h" +#include "../jrd/recsrc/RecordSource.h" +#include "../dsql/BoolNodes.h" +#include "../dsql/ExprNodes.h" +#include "../dsql/StmtNodes.h" +#include "../jrd/btr_proto.h" +#include "../jrd/cch_proto.h" +#include "../jrd/cmp_proto.h" +#include "../jrd/dpm_proto.h" +#include "../jrd/exe_proto.h" +#include "../jrd/ext_proto.h" +#include "../jrd/intl_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/mov_proto.h" +#include "../jrd/par_proto.h" + +#include "../jrd/optimizer/Optimizer.h" + +using namespace Firebird; +using namespace Jrd; + + +namespace +{ + // Check the index for being an expression one and + // matching both the given stream and the given expression tree + bool checkExpressionIndex(CompilerScratch* csb, const index_desc* idx, + ValueExprNode* node, StreamType stream) + { + fb_assert(idx); + + if (idx->idx_expression) + { + // The desired expression can be hidden inside a derived expression node, + // so try to recover it (see CORE-4118). + while (!idx->idx_expression->sameAs(node, true)) + { + const auto derivedExpr = nodeAs(node); + const auto cast = nodeAs(node); + + if (derivedExpr) + node = derivedExpr->arg; + else if (cast && cast->artificial) + node = cast->source; + else + return false; + } + + SortedStreamList exprStreams, nodeStreams; + idx->idx_expression->collectStreams(exprStreams); + node->collectStreams(nodeStreams); + + if (exprStreams.getCount() == 1 && exprStreams[0] == 0 && + nodeStreams.getCount() == 1 && nodeStreams[0] == stream) + { + return true; + } + } + + return false; + } + + ValueExprNode* injectCast(CompilerScratch* csb, + ValueExprNode* value, CastNode*& cast, + const dsc& desc) + { + // If the indexed column is of type int64, then we need to inject + // an extra cast to deliver the scale value to the BTR level + + if (value && desc.dsc_dtype == dtype_int64) + { + if (!cast) + { + cast = FB_NEW_POOL(csb->csb_pool) CastNode(csb->csb_pool); + cast->source = value; + cast->castDesc = desc; + cast->impureOffset = csb->allocImpure(); + } + + value = cast; + } + + return value; + } + + ValueExprNode* invertBoolValue(CompilerScratch* csb, ValueExprNode* value) + { + // Having a condition ( != ), + // invert it by making ( == ( == FALSE)), + // so that an index lookup could be possible + + static const UCHAR falseValue = '\0'; + LiteralNode* const falseLiteral = + FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); + falseLiteral->litDesc.makeBoolean(const_cast(&falseValue)); + + ComparativeBoolNode* const cmpNode = + FB_NEW_POOL(csb->csb_pool) ComparativeBoolNode(csb->csb_pool, blr_eql); + cmpNode->arg1 = value; + cmpNode->arg2 = falseLiteral; + + // Recreate the boolean expression as a value + BoolAsValueNode* const newValue = + FB_NEW_POOL(csb->csb_pool) BoolAsValueNode(csb->csb_pool); + newValue->boolean = cmpNode; + + newValue->impureOffset = csb->allocImpure(); + + return newValue; + } + +} // namespace + + +IndexScratch::IndexScratch(MemoryPool& p, index_desc* idx, double card) + : index(idx), cardinality(card), segments(p) +{ + segments.resize(index->idx_count); +} + +IndexScratch::IndexScratch(MemoryPool& p, const IndexScratch& other) + : index(other.index), + cardinality(other.cardinality), + selectivity(other.selectivity), + candidate(other.candidate), + scopeCandidate(other.scopeCandidate), + lowerCount(other.lowerCount), + upperCount(other.upperCount), + nonFullMatchedSegments(other.nonFullMatchedSegments), + usePartialKey(other.usePartialKey), + useMultiStartingKeys(other.useMultiStartingKeys), + segments(p, other.segments) +{} + + +Retrieval::Retrieval(thread_db* aTdbb, Optimizer* opt, StreamType streamNumber, + bool outer, bool inner, SortNode* sortNode, bool costOnly) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), + optimizer(opt), + csb(opt->getCompilerScratch()), + stream(streamNumber), + innerFlag(inner), + outerFlag(outer), + sort(sortNode), + createIndexScanNodes(!costOnly), + setConjunctionsMatched(!costOnly), + alias(getPool()), + indexScratches(getPool()), + inversionCandidates(getPool()) +{ + const Database* const dbb = tdbb->getDatabase(); + + const auto tail = &csb->csb_rpt[stream]; + + relation = tail->csb_relation; + fb_assert(relation); + + if (!tail->csb_idx) + return; + + for (auto& index : *tail->csb_idx) + { + const auto length = ROUNDUP(BTR_key_length(tdbb, relation, &index), sizeof(SLONG)); + + // AB: Calculate the cardinality which should reflect the total number + // of index pages for this index. + // We assume that the average index-key can be compressed by a factor 0.5 + // In the future the average key-length should be stored and retrieved + // from a system table (RDB$INDICES for example). + // Multiplying the selectivity with this cardinality gives the estimated + // number of index pages that are read for the index retrieval. + // Compound indexes are generally less compressed. + const double factor = (index.idx_count == 1) ? 0.5 : 0.7; + + double cardinality = tail->csb_cardinality; + cardinality *= (2 + length * factor); + cardinality /= (dbb->dbb_page_size - BTR_SIZE); + cardinality = MAX(cardinality, MINIMUM_CARDINALITY); + + indexScratches.add(IndexScratch(getPool(), &index, cardinality)); + } +} + + +// +// Melt two inversions together by the type given in node_type +// + +InversionNode* Retrieval::composeInversion(InversionNode* node1, + InversionNode* node2, + InversionNode::Type node_type) const +{ + if (!node2) + return node1; + + if (!node1) + return node2; + + if (node_type == InversionNode::TYPE_OR) + { + if (node1->type == InversionNode::TYPE_INDEX && + node2->type == InversionNode::TYPE_INDEX && + node1->retrieval->irb_index == node2->retrieval->irb_index) + { + node_type = InversionNode::TYPE_IN; + } + else if (node1->type == InversionNode::TYPE_IN && + node2->type == InversionNode::TYPE_INDEX && + node1->node2->retrieval->irb_index == node2->retrieval->irb_index) + { + node_type = InversionNode::TYPE_IN; + } + } + + return FB_NEW_POOL(getPool()) InversionNode(node_type, node1, node2); +} + +const string& Retrieval::getAlias() +{ + if (alias.isEmpty()) + alias = optimizer->makeAlias(this->stream); + + return alias; +} + +InversionCandidate* Retrieval::getInversion() +{ + if (finalCandidate) + return finalCandidate; + + auto iter = optimizer->getConjuncts(outerFlag, innerFlag); + + InversionCandidate* invCandidate = nullptr; + + if (relation && !relation->rel_file && !relation->isVirtual()) + { + InversionCandidateList inversions; + + // First, handle "AND" comparisons (all nodes except OR) + for (iter.rewind(); iter.hasData(); ++iter) + { + const auto booleanNode = nodeAs(*iter); + + if (!(iter & Optimizer::CONJUNCT_USED) && + (!booleanNode || booleanNode->blrOp != blr_or)) + { + invCandidate = matchOnIndexes(indexScratches, iter, 1); + + if (invCandidate) + inversions.add(invCandidate); + } + } + + getInversionCandidates(inversions, indexScratches, 1); + + // Second, handle "OR" comparisons + for (iter.rewind(); iter.hasData(); ++iter) + { + const auto booleanNode = nodeAs(*iter); + + if (!(iter & Optimizer::CONJUNCT_USED) && + (booleanNode && booleanNode->blrOp == blr_or)) + { + invCandidate = matchOnIndexes(indexScratches, iter, 1); + + if (invCandidate) + { + invCandidate->boolean = iter; + inversions.add(invCandidate); + } + } + } + + if (sort) + analyzeNavigation(inversions); + +#ifdef OPT_DEBUG_RETRIEVAL + // Debug + printCandidates(inversions); +#endif + + invCandidate = makeInversion(inversions); + + // Clean up intermediate inversion candidates + for (const auto candidate : inversions) + { + if (candidate != navigationCandidate) + delete candidate; + } + } + + const auto cardinality = csb->csb_rpt[stream].csb_cardinality; + + if (!invCandidate) + { + // No index will be used, thus create a dummy inversion candidate + // representing the natural table access. All the necessary properties + // (selectivity: 1.0, cost: 0, unique: false) are set up by the constructor. + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + } + + if (invCandidate->unique) + { + // Set up the unique retrieval cost to be fixed and not dependent on + // possibly outdated statistics. It includes N index scans plus one data page fetch. + invCandidate->cost = DEFAULT_INDEX_COST * invCandidate->indexes + 1; + } + else + { + // Add the records retrieval cost to the priorly calculated index scan cost + invCandidate->cost += cardinality * invCandidate->selectivity; + } + + // Adjust the effective selectivity by treating computable but unmatched conjunctions + // as filters. But consider only those local to our stream. + // While being here, also mark matched conjuncts, if requested. + double selectivity = MAXIMUM_SELECTIVITY; + for (iter.rewind(); iter.hasData(); ++iter) + { + if (!(iter & Optimizer::CONJUNCT_USED)) + { + const auto matched = invCandidate->matches.exist(iter); + + if (setConjunctionsMatched && matched) + iter |= Optimizer::CONJUNCT_MATCHED; + else if (!setConjunctionsMatched && !matched && + iter->computable(csb, stream, true)) + { + selectivity *= Optimizer::getSelectivity(*iter); + } + } + } + + Optimizer::adjustSelectivity(invCandidate->selectivity, selectivity, cardinality); + + // Add the streams where this stream is depending on + for (auto match : invCandidate->matches) + { + match->findDependentFromStreams(csb, stream, + &invCandidate->dependentFromStreams); + } + +#ifdef OPT_DEBUG_RETRIEVAL + // Debug + printFinalCandidate(invCandidate); +#endif + + finalCandidate = invCandidate; + + return invCandidate; +} + +IndexTableScan* Retrieval::getNavigation() +{ + if (!navigationCandidate) + return nullptr; + + IndexScratch* const scratch = navigationCandidate->scratch; + + // Looks like we can do a navigational walk. Flag that + // we have used this index for navigation, and allocate + // a navigational rsb for it. + scratch->index->idx_runtime_flags |= idx_navigate; + + const USHORT key_length = + ROUNDUP(BTR_key_length(tdbb, relation, scratch->index), sizeof(SLONG)); + + InversionNode* const index_node = makeIndexScanNode(scratch); + + return FB_NEW_POOL(getPool()) + IndexTableScan(csb, getAlias(), stream, relation, index_node, key_length, + navigationCandidate->selectivity); +} + +void Retrieval::analyzeNavigation(const InversionCandidateList& inversions) +{ + fb_assert(sort); + + HalfStaticArray tempCandidates; + InversionCandidate* bestCandidate = nullptr; + + for (auto& indexScratch : indexScratches) + { + auto idx = indexScratch.index; + + // if the number of fields in the sort is greater than the number of + // fields in the index, the index will not be used to optimize the + // sort--note that in the case where the first field is unique, this + // could be optimized, since the sort will be performed correctly by + // navigating on a unique index on the first field--deej + if (sort->expressions.getCount() > idx->idx_count) + continue; + + // if the user-specified access plan for this request didn't + // mention this index, forget it + if ((idx->idx_runtime_flags & idx_plan_dont_use) && + !(idx->idx_runtime_flags & idx_plan_navigate)) + { + continue; + } + + // only a single-column ORDER BY clause can be mapped to + // an expression index + if (idx->idx_flags & idx_expressn) + { + if (sort->expressions.getCount() != 1) + continue; + } + + // check to see if the fields in the sort match the fields in the index + // in the exact same order + + unsigned equalSegments = 0; + for (unsigned i = 0; i < MIN(indexScratch.lowerCount, indexScratch.upperCount); i++) + { + const auto& segment = indexScratch.segments[i]; + + if (segment.scanType == segmentScanEqual || + segment.scanType == segmentScanEquivalent || + segment.scanType == segmentScanMissing) + { + equalSegments++; + } + } + + bool usableIndex = true; + const index_desc::idx_repeat* idx_tail = idx->idx_rpt; + const index_desc::idx_repeat* const idx_end = idx_tail + idx->idx_count; + NestConst* ptr = sort->expressions.begin(); + const SortDirection* direction = sort->direction.begin(); + const NullsPlacement* nullOrder = sort->nullOrder.begin(); + + for (const NestConst* const end = sort->expressions.end(); + ptr != end; + ++ptr, ++direction, ++nullOrder, ++idx_tail) + { + ValueExprNode* const orgNode = *ptr; + FieldNode* fieldNode; + bool nodeMatched = false; + + // Collect nodes equivalent to the given sort node + + HalfStaticArray nodes; + nodes.add(orgNode); + + for (auto iter = optimizer->getConjuncts(); iter.hasData(); ++iter) + { + const auto cmpNode = nodeAs(*iter); + + if (cmpNode && (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) + { + ValueExprNode* const node1 = cmpNode->arg1; + ValueExprNode* const node2 = cmpNode->arg2; + + if (node1->sameAs(orgNode, false)) + nodes.add(node2); + + if (node2->sameAs(orgNode, false)) + nodes.add(node1); + } + } + + // Check whether any of the equivalent nodes is suitable for index navigation + + for (const auto node : nodes) + { + if (idx->idx_flags & idx_expressn) + { + if (!checkExpressionIndex(csb, idx, node, stream)) + continue; + } + else if (!(fieldNode = nodeAs(node)) || fieldNode->fieldStream != stream) + { + continue; + } + else + { + for (; idx_tail < idx_end && fieldNode->fieldId != idx_tail->idx_field; idx_tail++) + { + const unsigned segmentNumber = idx_tail - idx->idx_rpt; + + if (segmentNumber >= equalSegments) + break; + } + + if (idx_tail >= idx_end || fieldNode->fieldId != idx_tail->idx_field) + continue; + } + + if ((*direction == ORDER_DESC && !(idx->idx_flags & idx_descending)) || + (*direction == ORDER_ASC && (idx->idx_flags & idx_descending)) || + ((*nullOrder == NULLS_FIRST && *direction == ORDER_DESC) || + (*nullOrder == NULLS_LAST && *direction == ORDER_ASC))) + { + continue; + } + + dsc desc; + node->getDesc(tdbb, csb, &desc); + + // ASF: "desc.dsc_ttype() > ttype_last_internal" is to avoid recursion + // when looking for charsets/collations + + if (DTYPE_IS_TEXT(desc.dsc_dtype) && desc.dsc_ttype() > ttype_last_internal) + { + const TextType* const tt = INTL_texttype_lookup(tdbb, desc.dsc_ttype()); + + if (idx->idx_flags & idx_unique) + { + if (tt->getFlags() & TEXTTYPE_UNSORTED_UNIQUE) + continue; + } + else + { + // ASF: We currently can't use non-unique index for GROUP BY and DISTINCT with + // multi-level and insensitive collation. In NAV, keys are verified with memcmp + // but there we don't know length of each level. + if (sort->unique && (tt->getFlags() & TEXTTYPE_SEPARATE_UNIQUE)) + continue; + } + } + + nodeMatched = true; + break; + } + + if (!nodeMatched) + { + usableIndex = false; + break; + } + } + + if (!usableIndex) + continue; + + // Lookup the inversion candidate matching our navigational index + + InversionCandidate* candidate = nullptr; + + for (const auto inversion : inversions) + { + if (inversion->scratch == &indexScratch) + { + candidate = inversion; + break; + } + } + + // Check whether the navigational index has any matches shared with other inversion + // candidates. If so, compare inversions and decide whether navigation is acceptable. + // However, if the user-specified access plan mentions this index, + // then don't consider any (possibly better) alternatives. + // Another exception is when the FIRST ROWS optimization strategy is applied. + + if (candidate && !optimizer->favorFirstRows() && + !(idx->idx_runtime_flags & idx_plan_navigate)) + { + for (const auto otherCandidate : inversions) + { + if (otherCandidate != candidate) + { + for (const auto otherMatch : otherCandidate->matches) + { + if (candidate->matches.exist(otherMatch) && + betterInversion(otherCandidate, candidate, true)) + { + usableIndex = false; + break; + } + } + } + + if (!usableIndex) + break; + } + } + + if (!usableIndex) + continue; + + // Looks like we can do a navigational walk. Remember this candidate + // and compare it against other possible candidates. + + if (!candidate) + { + // If no inversion candidate is found, create a fake one representing full index scan + + candidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + candidate->cost = indexScratch.cardinality; + candidate->indexes = 1; + candidate->scratch = &indexScratch; + candidate->nonFullMatchedSegments = indexScratch.segments.getCount(); + tempCandidates.add(candidate); + } + + if (!bestCandidate || + betterInversion(candidate, bestCandidate, false)) + { + bestCandidate = candidate; + } + } + + // Clean up intermediate inversion candidates + for (const auto candidate : tempCandidates) + { + if (candidate != bestCandidate) + delete candidate; + } + + navigationCandidate = bestCandidate; +} + +bool Retrieval::betterInversion(const InversionCandidate* inv1, + const InversionCandidate* inv2, + bool ignoreUnmatched) const +{ + // Return true if inversion1 is *better* than inversion2. + // It's mostly about the retrieval cost, but other aspects are also taken into account. + + if (inv1->unique && !inv2->unique) + { + // A unique full equal match is better than anything else. + return true; + } + + if (inv1->unique == inv2->unique) + { + if (inv1->dependencies > inv2->dependencies) + { + // Index used for a relationship must be always prefered to + // the filtering ones, otherwise the nested loop join has + // no chances to be better than a sort merge. + // An alternative (simplified) condition might be: + // currentInv->dependencies > 0 + // && bestCandidate->dependencies == 0 + // but so far I tend to think that the current one is better. + return true; + } + + if (inv1->dependencies == inv2->dependencies) + { + const double cardinality = csb->csb_rpt[stream].csb_cardinality; + + const double cost1 = inv1->cost + (inv1->selectivity * cardinality); + const double cost2 = inv2->cost + (inv2->selectivity * cardinality); + + // Do we have very similar costs? + double diffCost = 0; + if (!cost1 && !cost2) + { + // Two zero costs should be handled as being the same + // (other comparison criteria should be applied, see below). + diffCost = 1; + } + else if (cost1) + { + // Calculate the difference + diffCost = cost2 / cost1; + } + + if ((diffCost >= 0.98) && (diffCost <= 1.02)) + { + // If the "same" costs then compare with the nr of unmatched segments, + // how many indexes and matched segments. First compare number of indexes. + int compareSelectivity = (inv1->indexes - inv2->indexes); + + if (compareSelectivity == 0) + { + // For the same number of indexes compare number of matched segments. + // Note the inverted condition: the more matched segments the better. + compareSelectivity = (inv2->matchedSegments - inv1->matchedSegments); + + if (compareSelectivity == 0 && !ignoreUnmatched) + { + // For the same number of matched segments + // compare ones that aren't full matched + compareSelectivity = + (inv1->nonFullMatchedSegments - inv2->nonFullMatchedSegments); + } + } + + if (compareSelectivity < 0) + return true; + } + else if (cost1 < cost2) + return true; + } + } + + return false; +} + +void Retrieval::getInversionCandidates(InversionCandidateList& inversions, + IndexScratchList& fromIndexScratches, + unsigned scope) const +{ + const double cardinality = csb->csb_rpt[stream].csb_cardinality; + + // Walk through indexes to calculate selectivity / candidate + MatchedBooleanList matches; + + for (auto& scratch : fromIndexScratches) + { + scratch.scopeCandidate = false; + scratch.lowerCount = 0; + scratch.upperCount = 0; + scratch.nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1; + scratch.usePartialKey = false; + scratch.useMultiStartingKeys = false; + + if (scratch.candidate) + { + matches.clear(); + scratch.selectivity = MAXIMUM_SELECTIVITY; + + bool unique = false; + const auto idx = scratch.index; + + for (unsigned j = 0; j < scratch.segments.getCount(); j++) + { + const auto& segment = scratch.segments[j]; + + if (segment.scope == scope) + scratch.scopeCandidate = true; + + const USHORT iType = idx->idx_rpt[j].idx_itype; + + if (iType >= idx_first_intl_string) + { + auto textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(iType)); + + if (segment.scanType != segmentScanMissing && !(idx->idx_flags & idx_unique)) + { + if (textType->getFlags() & TEXTTYPE_SEPARATE_UNIQUE) + { + // ASF: Order is more precise than equivalence class. + // We can't use the next segments, and we'll need to use + // INTL_KEY_PARTIAL to construct the last segment's key. + scratch.usePartialKey = true; + } + } + + if (segment.scanType == segmentScanStarting) + { + if (textType->getFlags() & TEXTTYPE_MULTI_STARTING_KEY) + scratch.useMultiStartingKeys = true; // use INTL_KEY_MULTI_STARTING + + scratch.usePartialKey = true; + } + } + + // Check if this is the last usable segment + if (!scratch.usePartialKey && + (segment.scanType == segmentScanEqual || + segment.scanType == segmentScanEquivalent || + segment.scanType == segmentScanMissing)) + { + // This is a perfect usable segment thus update root selectivity + scratch.lowerCount++; + scratch.upperCount++; + scratch.selectivity = idx->idx_rpt[j].idx_selectivity; + scratch.nonFullMatchedSegments = idx->idx_count - (j + 1); + // Add matches for this segment to the main matches list + matches.join(segment.matches); + + // An equality scan for any unique index cannot retrieve more + // than one row. The same is true for an equivalence scan for + // any primary index. + const bool single_match = + (segment.scanType == segmentScanEqual && + (idx->idx_flags & idx_unique)) || + (segment.scanType == segmentScanEquivalent && + (idx->idx_flags & idx_primary)); + + // dimitr: IS NULL scan against primary key is guaranteed + // to return zero rows. Do we need yet another + // special case here? + + if (single_match && ((j + 1) == idx->idx_count)) + { + // We have found a full equal matching index and it's unique, + // so we can stop looking further, because this is the best + // one we can get. + unique = true; + break; + } + + // dimitr: number of nulls is not reflected by our selectivity, + // so IS NOT DISTINCT and IS NULL scans may retrieve + // much bigger bitmap than expected here. I think + // appropriate reduce selectivity factors are required + // to be applied here. + } + else + { + // This is our last segment that we can use, + // estimate the selectivity + double selectivity = scratch.selectivity; + double factor = 1; + + switch (segment.scanType) + { + case segmentScanBetween: + scratch.lowerCount++; + scratch.upperCount++; + selectivity = idx->idx_rpt[j].idx_selectivity; + factor = REDUCE_SELECTIVITY_FACTOR_BETWEEN; + break; + + case segmentScanLess: + scratch.upperCount++; + selectivity = idx->idx_rpt[j].idx_selectivity; + factor = REDUCE_SELECTIVITY_FACTOR_LESS; + break; + + case segmentScanGreater: + scratch.lowerCount++; + selectivity = idx->idx_rpt[j].idx_selectivity; + factor = REDUCE_SELECTIVITY_FACTOR_GREATER; + break; + + case segmentScanStarting: + case segmentScanEqual: + case segmentScanEquivalent: + scratch.lowerCount++; + scratch.upperCount++; + selectivity = idx->idx_rpt[j].idx_selectivity; + factor = REDUCE_SELECTIVITY_FACTOR_STARTING; + break; + + default: + fb_assert(segment.scanType == segmentScanNone); + break; + } + + // Adjust the compound selectivity using the reduce factor. + // It should be better than the previous segment but worse + // than a full match. + const double diffSelectivity = scratch.selectivity - selectivity; + selectivity += (diffSelectivity * factor); + fb_assert(selectivity <= scratch.selectivity); + scratch.selectivity = selectivity; + + if (segment.scanType != segmentScanNone) + { + matches.join(segment.matches); + scratch.nonFullMatchedSegments = idx->idx_count - j; + } + break; + } + } + + if (scratch.scopeCandidate) + { + // When selectivity is zero the statement is prepared on an + // empty table or the statistics aren't updated. + // For an unique index, estimate the selectivity via the stream cardinality. + // For a non-unique one, assume 1/10 of the maximum selectivity, so that + // at least some indexes could be chosen by the optimizer. + double selectivity = scratch.selectivity; + + if (selectivity <= 0) + selectivity = unique ? 1 / cardinality : DEFAULT_SELECTIVITY; + + const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + invCandidate->unique = unique; + invCandidate->selectivity = selectivity; + // Calculate the cost (only index pages) for this index. + invCandidate->cost = DEFAULT_INDEX_COST + scratch.selectivity * scratch.cardinality; + invCandidate->nonFullMatchedSegments = scratch.nonFullMatchedSegments; + invCandidate->matchedSegments = MAX(scratch.lowerCount, scratch.upperCount); + invCandidate->indexes = 1; + invCandidate->scratch = &scratch; + invCandidate->matches.join(matches); + + for (auto match : invCandidate->matches) + { + match->findDependentFromStreams(csb, stream, + &invCandidate->dependentFromStreams); + } + + invCandidate->dependencies = invCandidate->dependentFromStreams.getCount(); + inversions.add(invCandidate); + } + } + } +} + + +// +// Search a dbkey (possibly a concatenated one) for a dbkey for specified stream +// + +ValueExprNode* Retrieval::findDbKey(ValueExprNode* dbkey, SLONG* position) const +{ + const auto keyNode = nodeAs(dbkey); + + if (keyNode && keyNode->blrOp == blr_dbkey) + { + if (keyNode->recStream == stream) + return dbkey; + + ++*position; + return nullptr; + } + + const auto concatNode = nodeAs(dbkey); + + if (concatNode) + { + ValueExprNode* dbkey_temp = findDbKey(concatNode->arg1, position); + + if (dbkey_temp) + return dbkey_temp; + + dbkey_temp = findDbKey(concatNode->arg2, position); + + if (dbkey_temp) + return dbkey_temp; + } + + return nullptr; +} + + +// +// Build node for index scan +// + +InversionNode* Retrieval::makeIndexScanNode(IndexScratch* indexScratch) const +{ + if (!createIndexScanNodes) + return nullptr; + + index_desc* const idx = indexScratch->index; + + // Check whether this is during a compile or during a SET INDEX operation + if (csb) + CMP_post_resource(&csb->csb_resources, relation, Resource::rsc_index, idx->idx_id); + else + { + CMP_post_resource(&tdbb->getRequest()->getStatement()->resources, relation, + Resource::rsc_index, idx->idx_id); + } + + // For external requests, determine index name (to be reported in plans) + MetaName indexName; + if (!(csb->csb_g_flags & csb_internal)) + MET_lookup_index(tdbb, indexName, relation->rel_name, idx->idx_id + 1); + + const auto retrieval = + FB_NEW_POOL(getPool()) IndexRetrieval(getPool(), relation, idx, indexName); + + // Pick up lower bound segment values + ValueExprNode** lower = retrieval->irb_value; + ValueExprNode** upper = retrieval->irb_value + idx->idx_count; + retrieval->irb_lower_count = indexScratch->lowerCount; + retrieval->irb_upper_count = indexScratch->upperCount; + + if (idx->idx_flags & idx_descending) + { + // switch upper/lower information + upper = retrieval->irb_value; + lower = retrieval->irb_value + idx->idx_count; + retrieval->irb_lower_count = indexScratch->upperCount; + retrieval->irb_upper_count = indexScratch->lowerCount; + retrieval->irb_generic |= irb_descending; + } + + bool ignoreNullsOnScan = true; + const auto& segments = indexScratch->segments; + + const auto count = MAX(indexScratch->lowerCount, indexScratch->upperCount); + for (unsigned i = 0; i < count; i++) + { + if (segments[i].scanType == segmentScanMissing) + { + *lower++ = *upper++ = NullNode::instance(); + ignoreNullsOnScan = false; + } + else + { + if (i < indexScratch->lowerCount) + *lower++ = segments[i].lowerValue; + + if (i < indexScratch->upperCount) + *upper++ = segments[i].upperValue; + + if (segments[i].scanType == segmentScanEquivalent) + ignoreNullsOnScan = false; + } + } + + if (count) + { + if (segments[count - 1].scanType == segmentScanStarting) + retrieval->irb_generic |= irb_starting; + + if (segments[count - 1].excludeLower) + retrieval->irb_generic |= irb_exclude_lower; + + if (segments[count - 1].excludeUpper) + retrieval->irb_generic |= irb_exclude_upper; + } + + if (indexScratch->usePartialKey) + retrieval->irb_generic |= irb_starting; // Flag the need to use INTL_KEY_PARTIAL in btr. + + if (indexScratch->useMultiStartingKeys) + { + // Flag the need to use INTL_KEY_MULTI_STARTING in btr. + retrieval->irb_generic |= irb_multi_starting | irb_starting; + } + + // This index is never used for IS NULL, thus we can ignore NULLs + // already at index scan. But this rule doesn't apply to nod_equiv + // which requires NULLs to be found in the index. + // A second exception is when this index is used for navigation. + if (ignoreNullsOnScan && !(idx->idx_runtime_flags & idx_navigate)) + retrieval->irb_generic |= irb_ignore_null_value_key; + + // Check to see if this is really an equality retrieval + if (retrieval->irb_lower_count == retrieval->irb_upper_count) + { + retrieval->irb_generic |= irb_equality; + + for (unsigned i = 0; i < retrieval->irb_lower_count; i++) + { + if (segments[i].lowerValue != segments[i].upperValue) + { + retrieval->irb_generic &= ~irb_equality; + break; + } + } + } + + // If we are matching less than the full index, this is a partial match + if (idx->idx_flags & idx_descending) + { + if (retrieval->irb_lower_count < idx->idx_count) + retrieval->irb_generic |= irb_partial; + } + else + { + if (retrieval->irb_upper_count < idx->idx_count) + retrieval->irb_generic |= irb_partial; + } + + // mark the index as utilized for the purposes of this compile + idx->idx_runtime_flags |= idx_used; + + const ULONG impure = csb ? csb->allocImpure() : 0; + return FB_NEW_POOL(getPool()) InversionNode(retrieval, impure); +} + +// +// Select best available inversion candidates and compose them to 1 inversion +// + +InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) const +{ + if (inversions.isEmpty() && !navigationCandidate) + return nullptr; + + const double streamCardinality = csb->csb_rpt[stream].csb_cardinality; + + // Prepared statements could be optimized against an almost empty table + // and then cached (such as in the restore process), thus causing slowdown + // when the table grows. In this case, let's consider all available indices. + const bool smallTable = (streamCardinality <= THRESHOLD_CARDINALITY); + + // These flags work around our smart index selection algorithm. Any explicit + // (i.e. user specified) plan requires all existing indices to be considered + // for a retrieval. Internal (system) requests used by the engine itself are + // often optimized using zero or non-actual statistics, so they are processed + // using somewhat relaxed rules. + const bool customPlan = csb->csb_rpt[stream].csb_plan; + const bool sysRequest = (csb->csb_g_flags & csb_internal); + + double totalSelectivity = MAXIMUM_SELECTIVITY; // worst selectivity + double totalIndexCost = 0; + + // The upper limit to use an index based retrieval is five indexes + almost all datapages + const double maximumCost = (DEFAULT_INDEX_COST * 5) + (streamCardinality * 0.95); + const double minimumSelectivity = 1 / streamCardinality; + + double previousTotalCost = maximumCost; + + // Force to always choose at least one index + bool firstCandidate = true; + + InversionCandidate* invCandidate = nullptr; + + 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)) + { + inversion->used = true; + } + } + + MatchedBooleanList matches; + + if (navigationCandidate) + { + matches.join(navigationCandidate->matches); + + // Reset the selectivity/cost prerequisites to account the navigational candidate + totalSelectivity = navigationCandidate->selectivity; + totalIndexCost = navigationCandidate->cost; + previousTotalCost = totalIndexCost + totalSelectivity * streamCardinality; + + if (navigationCandidate->matchedSegments) + firstCandidate = false; + } + + for (FB_SIZE_T i = 0; i < inversions.getCount(); i++) + { + // Initialize vars before walking through candidates + InversionCandidate* bestCandidate = nullptr; + bool restartLoop = false; + + for (const auto currentInv : inversions) + { + if (!currentInv->used) + { + // If this is a unique full equal matched inversion we're done, so + // we can make the inversion and return it. + if (currentInv->unique && currentInv->dependencies && !currentInv->condition) + { + if (!invCandidate) + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + + if (!currentInv->inversion && currentInv->scratch) + invCandidate->inversion = makeIndexScanNode(currentInv->scratch); + else + invCandidate->inversion = currentInv->inversion; + + invCandidate->dbkeyRanges.assign(currentInv->dbkeyRanges); + invCandidate->unique = currentInv->unique; + invCandidate->selectivity = currentInv->selectivity; + invCandidate->cost = currentInv->cost; + invCandidate->indexes = currentInv->indexes; + 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 (currentInv->boolean) + { + if (!matches.exist(currentInv->boolean)) + matches.add(currentInv->boolean); + } + + invCandidate->matches.join(matches); + if (customPlan) + continue; + + return invCandidate; + } + + // Look if a match is already used by previous matches. + bool anyMatchAlreadyUsed = false, matchUsedByNavigation = false; + for (const auto currentMatch : currentInv->matches) + { + if (matches.exist(currentMatch)) + { + anyMatchAlreadyUsed = true; + + if (navigationCandidate && + navigationCandidate->matches.exist(currentMatch)) + { + matchUsedByNavigation = true; + } + + break; + } + } + + 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); + } + + // Restart loop, because other indexes could also be excluded now. + restartLoop = true; + break; + } + + if (!bestCandidate) + { + // The first candidate + bestCandidate = currentInv; + } + else + { + // Prefer unconditional inversions + if (currentInv->condition) + { + currentInv->used = true; + restartLoop = true; + break; + } + + if (bestCandidate->condition) + { + bestCandidate = currentInv; + restartLoop = true; + break; + } + + if (betterInversion(currentInv, bestCandidate, false)) + bestCandidate = currentInv; + } + } + } + + if (restartLoop) + continue; + + // If we have a candidate which is interesting build the inversion + // else we're done. + if (bestCandidate) + { + // AB: Here we test if our new candidate is interesting enough to be added for + // index retrieval. + + // AB: For now i'll use the calculation that's often used for and-ing selectivities (S1 * S2). + // I think this calculation is not right for many cases. + // For example two "good" selectivities will result in a very good selectivity, but + // mostly a filter is made by adding criteria's where every criteria is an extra filter + // compared to the previous one. Thus with the second criteria in _most_ cases still + // records are returned. (Think also on the segment-selectivity in compound indexes) + // Assume a table with 100000 records and two selectivities of 0.001 (100 records) which + // are both AND-ed (with S1 * S2 => 0.001 * 0.001 = 0.000001 => 0.1 record). + // + // A better formula could be where the result is between "Sbest" and "Sbest * factor" + // The reducing factor should be between 0 and 1 (Sbest = best selectivity) + // + // Example: + /* + double newTotalSelectivity = 0; + double bestSel = bestCandidate->selectivity; + double worstSel = totalSelectivity; + if (bestCandidate->selectivity > totalSelectivity) + { + worstSel = bestCandidate->selectivity; + bestSel = totalSelectivity; + } + + if (bestSel >= MAXIMUM_SELECTIVITY) { + newTotalSelectivity = MAXIMUM_SELECTIVITY; + } + else if (bestSel == 0) { + newTotalSelectivity = 0; + } + else { + newTotalSelectivity = bestSel - ((1 - worstSel) * (bestSel - (bestSel * 0.01))); + } + */ + + const double newTotalSelectivity = bestCandidate->selectivity * totalSelectivity; + const double newTotalDataCost = newTotalSelectivity * streamCardinality; + const double newTotalIndexCost = totalIndexCost + bestCandidate->cost; + const double totalCost = newTotalDataCost + newTotalIndexCost; + + // Test if the new totalCost will be higher than the previous totalCost + // and if the current selectivity (without the bestCandidate) is already good enough. + if (customPlan || sysRequest || smallTable || firstCandidate || + (totalCost < previousTotalCost && totalSelectivity > minimumSelectivity)) + { + // Exclude index from next pass + bestCandidate->used = true; + + firstCandidate = false; + + previousTotalCost = totalCost; + totalIndexCost = newTotalIndexCost; + totalSelectivity = newTotalSelectivity; + + if (!invCandidate) + { + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + if (!bestCandidate->inversion && bestCandidate->scratch) { + invCandidate->inversion = makeIndexScanNode(bestCandidate->scratch); + } + else { + invCandidate->inversion = bestCandidate->inversion; + } + invCandidate->dbkeyRanges.assign(bestCandidate->dbkeyRanges); + invCandidate->unique = bestCandidate->unique; + invCandidate->selectivity = bestCandidate->selectivity; + invCandidate->cost = bestCandidate->cost; + invCandidate->indexes = bestCandidate->indexes; + invCandidate->nonFullMatchedSegments = 0; + invCandidate->matchedSegments = bestCandidate->matchedSegments; + invCandidate->dependencies = bestCandidate->dependencies; + invCandidate->condition = bestCandidate->condition; + + for (FB_SIZE_T j = 0; j < bestCandidate->matches.getCount(); j++) + { + if (!matches.exist(bestCandidate->matches[j])) + matches.add(bestCandidate->matches[j]); + } + if (bestCandidate->boolean) + { + if (!matches.exist(bestCandidate->boolean)) + matches.add(bestCandidate->boolean); + } + } + 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); + } + + invCandidate->dbkeyRanges.join(bestCandidate->dbkeyRanges); + invCandidate->unique = (invCandidate->unique || bestCandidate->unique); + invCandidate->selectivity = totalSelectivity; + invCandidate->cost += bestCandidate->cost; + invCandidate->indexes += bestCandidate->indexes; + invCandidate->nonFullMatchedSegments = 0; + invCandidate->matchedSegments = + MAX(bestCandidate->matchedSegments, invCandidate->matchedSegments); + invCandidate->dependencies += bestCandidate->dependencies; + + for (const auto bestMatch : bestCandidate->matches) + { + if (!matches.exist(bestMatch)) + matches.add(bestMatch); + } + + if (bestCandidate->boolean) + { + if (!matches.exist(bestCandidate->boolean)) + matches.add(bestCandidate->boolean); + } + } + + if (invCandidate->unique) + { + // Single unique full equal match is enough + if (!customPlan) + break; + } + } + else + { + // We're done + break; + } + } + else { + break; + } + } + + // If we have no index used for filtering, but there's a navigational walk, + // set up the inversion candidate appropriately. + + if (navigationCandidate) + { + if (!invCandidate) + invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + + invCandidate->unique = navigationCandidate->unique; + invCandidate->selectivity *= navigationCandidate->selectivity; + invCandidate->cost += navigationCandidate->cost; + ++invCandidate->indexes; + invCandidate->navigated = true; + } + + if (invCandidate) + invCandidate->matches.join(matches); + + return invCandidate; +} + +bool Retrieval::matchBoolean(IndexScratch* indexScratch, + BoolExprNode* boolean, + unsigned scope) const +{ + if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) + return false; + + const auto cmpNode = nodeAs(boolean); + const auto missingNode = nodeAs(boolean); + const auto notNode = nodeAs(boolean); + const auto rseNode = nodeAs(boolean); + bool forward = true; + ValueExprNode* value = nullptr; + ValueExprNode* match = nullptr; + + if (cmpNode) + { + match = cmpNode->arg1; + value = cmpNode->arg2; + } + else if (missingNode) + match = missingNode->arg; + else if (notNode || rseNode) + return false; + else + { + fb_assert(false); + return false; + } + + ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ? + cmpNode->arg3 : nullptr; + + const auto idx = indexScratch->index; + + if (idx->idx_flags & idx_expressn) + { + // see if one side or the other is matchable to the index expression + + fb_assert(idx->idx_expression); + + if (!checkExpressionIndex(csb, idx, match, stream) || + (value && !value->computable(csb, stream, false))) + { + if ((!cmpNode || cmpNode->blrOp != blr_starting) && value && + checkExpressionIndex(csb, idx, value, stream) && + match->computable(csb, stream, false)) + { + ValueExprNode* temp = match; + match = value; + value = temp; + forward = false; + } + else + return false; + } + } + else + { + // If left side is not a field, swap sides. + // If left side is still not a field, give up + + FieldNode* fieldNode; + + if (!(fieldNode = nodeAs(match)) || + fieldNode->fieldStream != stream || + (value && !value->computable(csb, stream, false))) + { + ValueExprNode* temp = match; + match = value; + value = temp; + + if ((!match || !(fieldNode = nodeAs(match))) || + fieldNode->fieldStream != stream || + !value->computable(csb, stream, false)) + { + return false; + } + + forward = false; + } + } + + // check datatypes to ensure that the index scan is guaranteed + // to deliver correct results + + bool excludeBound = cmpNode && (cmpNode->blrOp == blr_gtr || cmpNode->blrOp == blr_lss); + + dsc matchDesc, valueDesc; + + if (value) + { + match->getDesc(tdbb, csb, &matchDesc); + value->getDesc(tdbb, csb, &valueDesc); + + if (!BTR_types_comparable(matchDesc, valueDesc)) + return false; + + if (matchDesc.dsc_dtype == dtype_sql_date && + valueDesc.dsc_dtype == dtype_timestamp) + { + // for "DATE TIMESTAMP" we need to include the boundary value + excludeBound = false; + } + } + + // match the field to an index, if possible, and save the value to be matched + // as either the lower or upper bound for retrieval, or both + + const auto fieldNode = nodeAs(match); + + if (!(idx->idx_flags & idx_expressn)) + fb_assert(fieldNode); + + const bool isDesc = (idx->idx_flags & idx_descending); + + // Needed for int64 matches, see injectCast() function + CastNode *cast = nullptr, *cast2 = nullptr; + + fb_assert(indexScratch->segments.getCount() == idx->idx_count); + + for (unsigned i = 0; i < idx->idx_count; i++) + { + if (!(idx->idx_flags & idx_expressn) && + fieldNode->fieldId != idx->idx_rpt[i].idx_field) + { + continue; + } + + const auto segment = &indexScratch->segments[i]; + + if (cmpNode) + { + switch (cmpNode->blrOp) + { + case blr_between: + if (!forward || !value2->computable(csb, stream, false)) + return false; + segment->matches.add(boolean); + // AB: If we have already an exact match don't + // override it with worser matches. + if (!((segment->scanType == segmentScanEqual) || + (segment->scanType == segmentScanEquivalent))) + { + segment->lowerValue = injectCast(csb, value, cast, matchDesc); + segment->upperValue = injectCast(csb, value2, cast2, matchDesc); + segment->scanType = segmentScanBetween; + segment->excludeLower = false; + segment->excludeUpper = false; + } + break; + + case blr_equiv: + segment->matches.add(boolean); + // AB: If we have already an exact match don't + // override it with worser matches. + if (!(segment->scanType == segmentScanEqual)) + { + segment->lowerValue = segment->upperValue = + injectCast(csb, value, cast, matchDesc); + segment->scanType = segmentScanEquivalent; + segment->excludeLower = false; + segment->excludeUpper = false; + } + break; + + case blr_eql: + segment->matches.add(boolean); + segment->lowerValue = segment->upperValue = + injectCast(csb, value, cast, matchDesc); + segment->scanType = segmentScanEqual; + segment->excludeLower = false; + segment->excludeUpper = false; + break; + + // ASF: Make "NOT boolean" work with indices. + case blr_neq: + { + if (valueDesc.dsc_dtype != dtype_boolean) + return false; + + // Invert the value and use it with "segmentScanEqual" for the index lookup + segment->matches.add(boolean); + segment->lowerValue = segment->upperValue = + invertBoolValue(csb, value); + segment->scanType = segmentScanEqual; + segment->excludeLower = false; + segment->excludeUpper = false; + break; + } + + case blr_gtr: + case blr_geq: + segment->matches.add(boolean); + if (!((segment->scanType == segmentScanEqual) || + (segment->scanType == segmentScanEquivalent) || + (segment->scanType == segmentScanBetween))) + { + if (forward != isDesc) // (forward && !isDesc || !forward && isDesc) + segment->excludeLower = excludeBound; + else + segment->excludeUpper = excludeBound; + + if (forward) + { + segment->lowerValue = injectCast(csb, value, cast, matchDesc); + if (segment->scanType == segmentScanLess) + segment->scanType = segmentScanBetween; + else + segment->scanType = segmentScanGreater; + } + else + { + segment->upperValue = injectCast(csb, value, cast, matchDesc); + if (segment->scanType == segmentScanGreater) + segment->scanType = segmentScanBetween; + else + segment->scanType = segmentScanLess; + } + } + break; + + case blr_lss: + case blr_leq: + segment->matches.add(boolean); + if (!((segment->scanType == segmentScanEqual) || + (segment->scanType == segmentScanEquivalent) || + (segment->scanType == segmentScanBetween))) + { + if (forward != isDesc) + segment->excludeUpper = excludeBound; + else + segment->excludeLower = excludeBound; + + if (forward) + { + segment->upperValue = injectCast(csb, value, cast, matchDesc); + if (segment->scanType == segmentScanGreater) + segment->scanType = segmentScanBetween; + else + segment->scanType = segmentScanLess; + } + else + { + segment->lowerValue = injectCast(csb, value, cast, matchDesc); + if (segment->scanType == segmentScanLess) + segment->scanType = segmentScanBetween; + else + segment->scanType = segmentScanGreater; + } + } + break; + + case blr_starting: + // Check if validate for using index + if (!forward || !validateStarts(indexScratch, cmpNode, i)) + return false; + segment->matches.add(boolean); + if (!((segment->scanType == segmentScanEqual) || + (segment->scanType == segmentScanEquivalent))) + { + segment->lowerValue = segment->upperValue = + injectCast(csb, value, cast, matchDesc); + segment->scanType = segmentScanStarting; + segment->excludeLower = false; + segment->excludeUpper = false; + } + break; + + default: + return false; + } + } + else if (missingNode) + { + segment->matches.add(boolean); + if (!((segment->scanType == segmentScanEqual) || + (segment->scanType == segmentScanEquivalent))) + { + segment->lowerValue = segment->upperValue = nullptr; + segment->scanType = segmentScanMissing; + segment->excludeLower = false; + segment->excludeUpper = false; + } + } + else + { + fb_assert(false); + return false; + } + + // A match could be made + if (segment->scope < scope) + segment->scope = scope; + + if (i == 0) + { + // If this is the first segment, then this index is a candidate. + indexScratch->candidate = true; + } + + return true; + } + + return false; +} + +// +// Check whether a boolean is a DB_KEY based comparison +// + +InversionCandidate* Retrieval::matchDbKey(BoolExprNode* boolean) const +{ + // If this isn't an equality, it isn't even interesting + + const auto cmpNode = nodeAs(boolean); + + if (!cmpNode) + return nullptr; + + switch (cmpNode->blrOp) + { + case blr_equiv: + case blr_eql: + case blr_gtr: + case blr_geq: + case blr_lss: + case blr_leq: + case blr_between: + break; + + default: + return nullptr; + } + + // Find the side of the equality that is potentially a dbkey. + // If neither, make the obvious deduction. + + SLONG n = 0; + int dbkeyArg = 1; + auto dbkey = findDbKey(cmpNode->arg1, &n); + + if (!dbkey) + { + n = 0; + dbkeyArg = 2; + dbkey = findDbKey(cmpNode->arg2, &n); + } + + if (!dbkey && (cmpNode->blrOp == blr_between)) + { + n = 0; + dbkeyArg = 3; + dbkey = findDbKey(cmpNode->arg3, &n); + } + + if (!dbkey) + return nullptr; + + // Make sure we have the correct stream + + const auto keyNode = nodeAs(dbkey); + + if (!keyNode || keyNode->blrOp != blr_dbkey || keyNode->recStream != stream) + return nullptr; + + // If this is a dbkey for the appropriate stream, it's invertable + + const double cardinality = csb->csb_rpt[stream].csb_cardinality; + + bool unique = false; + double selectivity = 0; + + ValueExprNode* lower = nullptr; + ValueExprNode* upper = nullptr; + + switch (cmpNode->blrOp) + { + case blr_eql: + case blr_equiv: + unique = true; + selectivity = 1 / cardinality; + lower = upper = (dbkeyArg == 1) ? cmpNode->arg2 : cmpNode->arg1; + break; + + case blr_gtr: + case blr_geq: + selectivity = REDUCE_SELECTIVITY_FACTOR_GREATER; + if (dbkeyArg == 1) + lower = cmpNode->arg2; // dbkey > arg2 + else + upper = cmpNode->arg1; // arg1 < dbkey + break; + + case blr_lss: + case blr_leq: + selectivity = REDUCE_SELECTIVITY_FACTOR_LESS; + if (dbkeyArg == 1) + upper = cmpNode->arg2; // dbkey < arg2 + else + lower = cmpNode->arg1; // arg1 < dbkey + break; + + case blr_between: + if (dbkeyArg == 1) // dbkey between arg2 and arg3 + { + selectivity = REDUCE_SELECTIVITY_FACTOR_BETWEEN; + lower = cmpNode->arg2; + upper = cmpNode->arg3; + } + else if (dbkeyArg == 2) // arg1 between dbkey and arg3, or dbkey <= arg1 and arg1 <= arg3 + { + selectivity = REDUCE_SELECTIVITY_FACTOR_LESS; + upper = cmpNode->arg1; + } + else if (dbkeyArg == 3) // arg1 between arg2 and dbkey, or arg2 <= arg1 and arg1 <= dbkey + { + selectivity = REDUCE_SELECTIVITY_FACTOR_GREATER; + lower = cmpNode->arg1; + } + break; + + default: + return nullptr; + } + + if (lower && !lower->computable(csb, stream, false)) + return nullptr; + + if (upper && !upper->computable(csb, stream, false)) + return nullptr; + + const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + invCandidate->unique = unique; + invCandidate->selectivity = selectivity; + invCandidate->cost = 0; + invCandidate->matches.add(boolean); + boolean->findDependentFromStreams(csb, stream, &invCandidate->dependentFromStreams); + invCandidate->dependencies = invCandidate->dependentFromStreams.getCount(); + + if (createIndexScanNodes) + { + if (unique) + { + fb_assert(lower == upper); + + const auto inversion = FB_NEW_POOL(getPool()) InversionNode(lower, n); + inversion->impure = csb->allocImpure(); + invCandidate->inversion = inversion; + } + else + { + fb_assert(n == 0); + fb_assert(lower || upper); + + const auto dbkeyRange = FB_NEW_POOL(getPool()) DbKeyRangeNode(lower, upper); + invCandidate->dbkeyRanges.add(dbkeyRange); + } + } + + return invCandidate; +} + +// +// Try to match boolean on every index. +// If the boolean is an "OR" node then a inversion candidate could be returned. +// + +InversionCandidate* Retrieval::matchOnIndexes(IndexScratchList& inputIndexScratches, + BoolExprNode* boolean, + unsigned scope) const +{ + const auto binaryNode = nodeAs(boolean); + + // Handle the "OR" case up front + if (binaryNode && binaryNode->blrOp == blr_or) + { + InversionCandidateList inversions; + + // Make list for index matches + + // Copy information from caller + IndexScratchList indexOrScratches(inputIndexScratches); + + // We use a scope variable to see on how + // deep we are in a nested or conjunction. + scope++; + + auto invCandidate1 = matchOnIndexes(indexOrScratches, binaryNode->arg1, scope); + + if (invCandidate1) + inversions.add(invCandidate1); + + auto childBoolNode = nodeAs(binaryNode->arg1); + + // Get usable inversions based on indexOrScratches and scope + if (!childBoolNode || childBoolNode->blrOp != blr_or) + getInversionCandidates(inversions, indexOrScratches, scope); + + invCandidate1 = makeInversion(inversions); + + // Copy information from caller + indexOrScratches = inputIndexScratches; + + // Clear inversion list + inversions.clear(); + + auto invCandidate2 = matchOnIndexes(indexOrScratches, binaryNode->arg2, scope); + + if (invCandidate2) + inversions.add(invCandidate2); + + childBoolNode = nodeAs(binaryNode->arg2); + + // Make inversion based on indexOrScratches and scope + if (!childBoolNode || childBoolNode->blrOp != blr_or) + getInversionCandidates(inversions, indexOrScratches, scope); + + invCandidate2 = makeInversion(inversions); + + if (invCandidate1 && invCandidate2 && + (invCandidate1->indexes || invCandidate1->unique) && + (invCandidate2->indexes || invCandidate2->unique)) + { + const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); + invCandidate->inversion = composeInversion(invCandidate1->inversion, + invCandidate2->inversion, InversionNode::TYPE_OR); + invCandidate->selectivity = invCandidate1->selectivity + invCandidate2->selectivity - + invCandidate1->selectivity * invCandidate2->selectivity; + invCandidate->cost = invCandidate1->cost + invCandidate2->cost; + invCandidate->indexes = invCandidate1->indexes + invCandidate2->indexes; + invCandidate->nonFullMatchedSegments = 0; + invCandidate->matchedSegments = + MIN(invCandidate1->matchedSegments, invCandidate2->matchedSegments); + invCandidate->dependencies = invCandidate1->dependencies + invCandidate2->dependencies; + + if (invCandidate1->condition && invCandidate2->condition) + { + const auto newNode = FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_or); + newNode->arg1 = invCandidate1->condition; + newNode->arg2 = invCandidate2->condition; + invCandidate->condition = newNode; + } + else if (invCandidate1->condition) + { + invCandidate->condition = invCandidate1->condition; + } + else if (invCandidate2->condition) + { + invCandidate->condition = invCandidate2->condition; + } + + // Add matches conjunctions that exists in both left and right inversion + if (invCandidate1->matches.hasData() && invCandidate2->matches.hasData()) + { + SortedArray matches; + + for (const auto match : invCandidate1->matches) + matches.add(match); + + for (const auto match : invCandidate2->matches) + matches.add(match); + } + + return invCandidate; + } + + if (invCandidate1) + { + BoolExprNode* condition = binaryNode->arg2; + + if (condition->computable(csb, stream, false)) + { + if (invCandidate1->condition) + { + const auto newNode = FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_or); + newNode->arg1 = invCandidate1->condition; + newNode->arg2 = condition; + condition = newNode; + } + + invCandidate1->condition = condition; + return invCandidate1; + } + } + + if (invCandidate2) + { + BoolExprNode* condition = binaryNode->arg1; + + if (condition->computable(csb, stream, false)) + { + if (invCandidate2->condition) + { + const auto newNode = FB_NEW_POOL(getPool()) BinaryBoolNode(getPool(), blr_or); + newNode->arg1 = invCandidate2->condition; + newNode->arg2 = condition; + condition = newNode; + } + + invCandidate2->condition = condition; + return invCandidate2; + } + } + + return nullptr; + } + + if (binaryNode && binaryNode->blrOp == blr_and) + { + // Recursively call this procedure for every boolean + // and finally get candidate inversions. + // Normally we come here from within a OR conjunction. + InversionCandidateList inversions; + + InversionCandidate* invCandidate = matchOnIndexes( + inputIndexScratches, binaryNode->arg1, scope); + + if (invCandidate) + inversions.add(invCandidate); + + invCandidate = matchOnIndexes(inputIndexScratches, binaryNode->arg2, scope); + + if (invCandidate) + inversions.add(invCandidate); + + return makeInversion(inversions); + } + + // Check for DB_KEY comparison + InversionCandidate* const invCandidate = matchDbKey(boolean); + + // Walk through indexes + for (auto& indexScratch : inputIndexScratches) + { + // Try to match the boolean against a index. + if (!(indexScratch.index->idx_runtime_flags & idx_plan_dont_use) || + (indexScratch.index->idx_runtime_flags & idx_plan_navigate)) + { + matchBoolean(&indexScratch, boolean, scope); + } + } + + return invCandidate; +} + + +// +// Check if the boolean is valid for using it against the given index segment +// + +bool Retrieval::validateStarts(IndexScratch* indexScratch, + ComparativeBoolNode* cmpNode, + unsigned segment) const +{ + fb_assert(cmpNode && cmpNode->blrOp == blr_starting); + if (!cmpNode || cmpNode->blrOp != blr_starting) + return false; + + ValueExprNode* field = cmpNode->arg1; + ValueExprNode* value = cmpNode->arg2; + + const auto idx = indexScratch->index; + + if (idx->idx_flags & idx_expressn) + { + // AB: What if the expression contains a number/float etc.. and + // we use starting with against it? Is that allowed? + fb_assert(idx->idx_expression); + + if (!(checkExpressionIndex(csb, idx, field, stream) || + (value && !value->computable(csb, stream, false)))) + { + // AB: Can we swap de left and right sides by a starting with? + // X STARTING WITH 'a' that is never the same as 'a' STARTING WITH X + if (value && + checkExpressionIndex(csb, idx, value, stream) && + field->computable(csb, stream, false)) + { + field = value; + value = cmpNode->arg1; + } + else + return false; + } + } + else + { + const auto fieldNode = nodeAs(field); + + if (!fieldNode) + { + // dimitr: any idea how we can use an index in this case? + // The code below produced wrong results. + // AB: I don't think that it would be effective, because + // this must include many matches (think about empty string) + return false; + /* + if (!nodeIs(value)) + return nullptr; + field = value; + value = cmpNode->arg1; + */ + } + + // Every string starts with an empty string so don't bother using an index in that case. + if (const auto literal = nodeAs(value)) + { + if ((literal->litDesc.dsc_dtype == dtype_text && literal->litDesc.dsc_length == 0) || + (literal->litDesc.dsc_dtype == dtype_varying && + literal->litDesc.dsc_length == sizeof(USHORT))) + { + return false; + } + } + + // AB: Check if the index-segment is usable for using starts. + // Thus it should be of type string, etc... + if (fieldNode->fieldStream != stream || + fieldNode->fieldId != idx->idx_rpt[segment].idx_field || + !(idx->idx_rpt[segment].idx_itype == idx_string || + idx->idx_rpt[segment].idx_itype == idx_byte_array || + idx->idx_rpt[segment].idx_itype == idx_metadata || + idx->idx_rpt[segment].idx_itype >= idx_first_intl_string) || + !value->computable(csb, stream, false)) + { + return false; + } + } + + return true; +} + + +#ifdef OPT_DEBUG_RETRIEVAL +void Retrieval::printCandidate(const InversionCandidate* candidate) const +{ + optimizer->printf(" cost (%1.2f), selectivity (%1.10f), indexes (%d), matched (%d, %d)", + candidate->cost, candidate->selectivity, candidate->indexes, candidate->matchedSegments, + candidate->nonFullMatchedSegments); + + if (candidate->unique) + optimizer->printf(", unique"); + + if (candidate->dependentFromStreams.hasData()) + { + optimizer->printf(", dependent from streams:"); + + const auto end = candidate->dependentFromStreams.end(); + for (auto iter = candidate->dependentFromStreams.begin(); iter != end; iter++) + { + const auto name = optimizer->getStreamName(*iter); + + if (name.hasData()) + optimizer->printf(" %u (%s)", *iter, name.c_str()); + else + optimizer->printf(" %u", *iter); + + if (iter != end - 1) + optimizer->printf(","); + } + } + + optimizer->printf("\n"); +} + +void Retrieval::printCandidates(const InversionCandidateList& inversions) const +{ + if (inversions.getCount() < 2) + return; + + const auto name = optimizer->getStreamName(stream); + optimizer->printf(" retrieval candidates for stream %u (%s):\n", + stream, name.c_str()); + + for (const auto candidate : inversions) + printCandidate(candidate); +} + +void Retrieval::printFinalCandidate(const InversionCandidate* candidate) const +{ + if (!candidate) + return; + + const auto name = optimizer->getStreamName(stream); + optimizer->printf(" final candidate for stream %u (%s):\n", + stream, name.c_str()); + + printCandidate(candidate); +} +#endif diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 65513746ea..890496eec0 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -167,7 +167,7 @@ namespace // Parse blr, returning a compiler scratch block with the results. // Caller must do pool handling. DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length, - CompilerScratch* view_csb, CompilerScratch** csb_ptr, JrdStatement** statementPtr, + CompilerScratch* view_csb, CompilerScratch** csb_ptr, Statement** statementPtr, const bool trigger, USHORT flags) { #ifdef CMP_DEBUG @@ -188,7 +188,7 @@ DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr PAR_syntax_error(csb, "end_of_command"); if (statementPtr) - *statementPtr = JrdStatement::makeStatement(tdbb, csb, false); + *statementPtr = Statement::makeStatement(tdbb, csb, false); return csb->csb_node; } @@ -197,7 +197,7 @@ DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr // Finish parse of memory nodes, returning a compiler scratch block with the results. // Caller must do pool handling. void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node, - CompilerScratch* view_csb, CompilerScratch** csb_ptr, JrdStatement** statementPtr, + CompilerScratch* view_csb, CompilerScratch** csb_ptr, Statement** statementPtr, const bool trigger, USHORT flags) { BlrParseWrapper csb(*tdbb->getDefaultPool(), relation, view_csb, csb_ptr, trigger, flags); @@ -206,7 +206,7 @@ void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node, csb->csb_node = node; if (statementPtr) - *statementPtr = JrdStatement::makeStatement(tdbb, csb, false); + *statementPtr = Statement::makeStatement(tdbb, csb, false); } @@ -1141,6 +1141,7 @@ void PAR_procedure_parms(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedu MemoryPool& pool = *tdbb->getDefaultPool(); // We have a few parameters. Get on with creating the message block + // Outer messages map may start with 2, but they are always in the routine start. USHORT n = ++csb->csb_msg_number; if (n < 2) csb->csb_msg_number = n = 2; diff --git a/src/jrd/par_proto.h b/src/jrd/par_proto.h index 02f0012e95..77ea5dd2a0 100644 --- a/src/jrd/par_proto.h +++ b/src/jrd/par_proto.h @@ -27,8 +27,8 @@ namespace Jrd { class CompilerScratch; class jrd_rel; - class jrd_req; - class JrdStatement; + class Request; + class Statement; class thread_db; class ItemInfo; class BoolExprNode; @@ -46,9 +46,9 @@ struct dsc; Jrd::ValueListNode* PAR_args(Jrd::thread_db*, Jrd::CompilerScratch*, USHORT, USHORT); Jrd::ValueListNode* PAR_args(Jrd::thread_db*, Jrd::CompilerScratch*); Jrd::DmlNode* PAR_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, ULONG blr_length, - Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::JrdStatement**, const bool, USHORT); + Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::Statement**, const bool, USHORT); void PAR_preparsed_node(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::DmlNode*, - Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::JrdStatement**, const bool, USHORT); + Jrd::CompilerScratch*, Jrd::CompilerScratch**, Jrd::Statement**, const bool, USHORT); Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR* blr, ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, USHORT); StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*); diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index d2592e03d0..14c94ec1ec 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -27,6 +27,7 @@ #include "../jrd/mov_proto.h" #include "../jrd/vio_proto.h" #include "../jrd/Attachment.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -48,13 +49,23 @@ BaseAggWinStream::BaseAggWinStream(thread_db* tdbb, Compiler m_oneRowWhenEmpty(oneRowWhenEmpty) { fb_assert(m_next); + m_impure = csb->allocImpure(); + + if (group) + { + m_cardinality = next->getCardinality(); + for (auto count = group->getCount(); count; count--) + m_cardinality *= REDUCE_SELECTIVITY_FACTOR_EQUALITY; + } + else + m_cardinality = MINIMUM_CARDINALITY; } template void BaseAggWinStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = getImpure(request); impure->irsb_flags = irsb_open; @@ -77,7 +88,7 @@ void BaseAggWinStream::open(thread_db* tdbb) const template void BaseAggWinStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -111,7 +122,7 @@ void BaseAggWinStream::markRecursive() } template -void BaseAggWinStream::invalidateRecords(jrd_req* request) const +void BaseAggWinStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -130,7 +141,7 @@ void BaseAggWinStream::findUsedStreams(StreamList& streams, template bool BaseAggWinStream::evaluateGroup(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); JRD_reschedule(tdbb); @@ -194,7 +205,7 @@ bool BaseAggWinStream::evaluateGroup(thread_db* tdbb) const // Initialize the aggregate record template -void BaseAggWinStream::aggInit(thread_db* tdbb, jrd_req* request, +void BaseAggWinStream::aggInit(thread_db* tdbb, Request* request, const MapNode* map) const { const NestConst* const sourceEnd = map->sourceList.end(); @@ -215,7 +226,7 @@ void BaseAggWinStream::aggInit(thread_db* tdbb, jrd_req* req // Go through and compute all the aggregates on this record template -bool BaseAggWinStream::aggPass(thread_db* tdbb, jrd_req* request, +bool BaseAggWinStream::aggPass(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const { bool ret = true; @@ -245,7 +256,7 @@ bool BaseAggWinStream::aggPass(thread_db* tdbb, jrd_req* req } template -void BaseAggWinStream::aggExecute(thread_db* tdbb, jrd_req* request, +void BaseAggWinStream::aggExecute(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const { const NestConst* const sourceEnd = sourceList.end(); @@ -277,7 +288,7 @@ void BaseAggWinStream::aggExecute(thread_db* tdbb, jrd_req* // Finalize a sort for distinct aggregate template -void BaseAggWinStream::aggFinish(thread_db* tdbb, jrd_req* request, +void BaseAggWinStream::aggFinish(thread_db* tdbb, Request* request, const MapNode* map) const { const NestConst* const sourceEnd = map->sourceList.end(); @@ -295,7 +306,7 @@ void BaseAggWinStream::aggFinish(thread_db* tdbb, jrd_req* r // Look for change in the values of a group/order. template -int BaseAggWinStream::lookForChange(thread_db* tdbb, jrd_req* request, +int BaseAggWinStream::lookForChange(thread_db* tdbb, Request* request, const NestValueArray* group, const SortNode* sort, impure_value* values) const { if (!group) @@ -340,7 +351,7 @@ int BaseAggWinStream::lookForChange(thread_db* tdbb, jrd_req } template -bool BaseAggWinStream::getNextRecord(thread_db* tdbb, jrd_req* request) const +bool BaseAggWinStream::getNextRecord(thread_db* tdbb, Request* request) const { Impure* const impure = getImpure(request); @@ -368,7 +379,10 @@ AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, Stream void AggregatedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Aggregate"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -377,7 +391,7 @@ bool AggregatedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = getImpure(request); diff --git a/src/jrd/recsrc/BitmapTableScan.cpp b/src/jrd/recsrc/BitmapTableScan.cpp index 6b3485eda7..2542910cd6 100644 --- a/src/jrd/recsrc/BitmapTableScan.cpp +++ b/src/jrd/recsrc/BitmapTableScan.cpp @@ -37,18 +37,19 @@ using namespace Jrd; BitmapTableScan::BitmapTableScan(CompilerScratch* csb, const string& alias, StreamType stream, jrd_rel* relation, - InversionNode* inversion) + InversionNode* inversion, double selectivity) : RecordStream(csb, stream), m_alias(csb->csb_pool, alias), m_relation(relation), m_inversion(inversion) { fb_assert(m_inversion); m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality * selectivity; } void BitmapTableScan::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -62,7 +63,7 @@ void BitmapTableScan::open(thread_db* tdbb) const void BitmapTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -84,7 +85,7 @@ bool BitmapTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -129,6 +130,7 @@ void BitmapTableScan::print(thread_db* tdbb, string& plan, plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; + printOptInfo(plan); printInversion(tdbb, m_inversion, plan, true, level); } else diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 5bfad5b7ff..8f8e5355f5 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -45,6 +45,7 @@ BufferedStream::BufferedStream(CompilerScratch* csb, RecordSource* next) fb_assert(m_next); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); StreamList streams; m_next->findUsedStreams(streams); @@ -114,7 +115,7 @@ BufferedStream::BufferedStream(CompilerScratch* csb, RecordSource* next) void BufferedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_mustread; @@ -130,7 +131,7 @@ void BufferedStream::open(thread_db* tdbb) const void BufferedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -151,7 +152,7 @@ bool BufferedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -319,6 +320,7 @@ void BufferedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigne extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length); plan += printIndent(++level) + "Record Buffer" + extras; + printOptInfo(plan); } m_next->print(tdbb, plan, detailed, level); @@ -334,7 +336,7 @@ void BufferedStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void BufferedStream::invalidateRecords(jrd_req* request) const +void BufferedStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -346,7 +348,7 @@ void BufferedStream::nullRecords(thread_db* tdbb) const void BufferedStream::locate(thread_db* tdbb, FB_UINT64 position) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); // If we haven't fetched and cached the underlying stream completely, do it now @@ -362,7 +364,7 @@ void BufferedStream::locate(thread_db* tdbb, FB_UINT64 position) const FB_UINT64 BufferedStream::getCount(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); // If we haven't fetched and cached the underlying stream completely, do it now diff --git a/src/jrd/recsrc/ConditionalStream.cpp b/src/jrd/recsrc/ConditionalStream.cpp index 91d3e85403..d0c5f98b41 100644 --- a/src/jrd/recsrc/ConditionalStream.cpp +++ b/src/jrd/recsrc/ConditionalStream.cpp @@ -46,11 +46,12 @@ ConditionalStream::ConditionalStream(CompilerScratch* csb, fb_assert(m_first && m_second && m_boolean); m_impure = csb->allocImpure(); + m_cardinality = (first->getCardinality() + second->getCardinality()) / 2; } void ConditionalStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -61,7 +62,7 @@ void ConditionalStream::open(thread_db* tdbb) const void ConditionalStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -79,7 +80,7 @@ bool ConditionalStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -90,7 +91,7 @@ bool ConditionalStream::getRecord(thread_db* tdbb) const bool ConditionalStream::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -101,7 +102,7 @@ bool ConditionalStream::refetchRecord(thread_db* tdbb) const bool ConditionalStream::lockRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -115,6 +116,8 @@ void ConditionalStream::print(thread_db* tdbb, string& plan, bool detailed, unsi if (detailed) { plan += printIndent(++level) + "Condition"; + printOptInfo(plan); + m_first->print(tdbb, plan, true, level); m_second->print(tdbb, plan, true, level); } @@ -146,7 +149,7 @@ void ConditionalStream::findUsedStreams(StreamList& streams, bool expandAll) con m_second->findUsedStreams(streams, expandAll); } -void ConditionalStream::invalidateRecords(jrd_req* request) const +void ConditionalStream::invalidateRecords(Request* request) const { m_first->invalidateRecords(request); m_second->invalidateRecords(request); diff --git a/src/jrd/recsrc/Cursor.cpp b/src/jrd/recsrc/Cursor.cpp index 941e228bf6..b65ed23896 100644 --- a/src/jrd/recsrc/Cursor.cpp +++ b/src/jrd/recsrc/Cursor.cpp @@ -35,7 +35,7 @@ namespace { bool validate(thread_db* tdbb) { - const jrd_req* const request = tdbb->getRequest(); + const Request* const request = tdbb->getRequest(); if (request->req_flags & req_abort) return false; @@ -47,7 +47,7 @@ namespace } // Initialize dependent invariants - void initializeInvariants(jrd_req* request, const VarInvariantArray* invariants) + void initializeInvariants(Request* request, const VarInvariantArray* invariants) { if (invariants) { @@ -105,7 +105,7 @@ Cursor::Cursor(CompilerScratch* csb, const RecordSource* rsb, void Cursor::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* impure = request->getImpure(m_impure); impure->irsb_active = true; @@ -117,7 +117,7 @@ void Cursor::open(thread_db* tdbb) const void Cursor::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_active) @@ -135,7 +135,7 @@ bool Cursor::fetchNext(thread_db* tdbb) const if (!validate(tdbb)) return false; - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->irsb_active) @@ -207,7 +207,7 @@ bool Cursor::fetchAbsolute(thread_db* tdbb, SINT64 offset) const if (!validate(tdbb)) return false; - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->irsb_active) @@ -268,7 +268,7 @@ bool Cursor::fetchRelative(thread_db* tdbb, SINT64 offset) const if (!validate(tdbb)) return false; - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!impure->irsb_active) @@ -335,7 +335,7 @@ bool Cursor::fetchRelative(thread_db* tdbb, SINT64 offset) const } // Check if the cursor is in a good state for access a field. -void Cursor::checkState(jrd_req* request) const +void Cursor::checkState(Request* request) const { const Impure* const impure = request->getImpure(m_impure); diff --git a/src/jrd/recsrc/Cursor.h b/src/jrd/recsrc/Cursor.h index 280dd7e9f2..0e96439bee 100644 --- a/src/jrd/recsrc/Cursor.h +++ b/src/jrd/recsrc/Cursor.h @@ -75,7 +75,7 @@ namespace Jrd bool fetchAbsolute(thread_db* tdbb, SINT64 offset) const; bool fetchRelative(thread_db* tdbb, SINT64 offset) const; - void checkState(jrd_req* request) const; + void checkState(Request* request) const; const RecordSource* getAccessPath() const { diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 6b080c7289..be63ba8401 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -23,7 +23,6 @@ #include "firebird.h" #include "../jrd/jrd.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/ext_proto.h" #include "../jrd/met_proto.h" @@ -43,12 +42,13 @@ ExternalTableScan::ExternalTableScan(CompilerScratch* csb, const string& alias, : RecordStream(csb, stream), m_relation(relation), m_alias(csb->csb_pool, alias) { m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality; } void ExternalTableScan::open(thread_db* tdbb) const { Database* const dbb = tdbb->getDatabase(); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -66,7 +66,7 @@ void ExternalTableScan::open(thread_db* tdbb) const void ExternalTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -80,7 +80,7 @@ bool ExternalTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -123,6 +123,7 @@ void ExternalTableScan::print(thread_db* tdbb, string& plan, { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/FilteredStream.cpp b/src/jrd/recsrc/FilteredStream.cpp index 5ae71e8803..a5a4ed25cf 100644 --- a/src/jrd/recsrc/FilteredStream.cpp +++ b/src/jrd/recsrc/FilteredStream.cpp @@ -25,6 +25,7 @@ #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/evl_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -35,18 +36,23 @@ using namespace Jrd; // Data access: predicate driven filter // ------------------------------------ -FilteredStream::FilteredStream(CompilerScratch* csb, RecordSource* next, BoolExprNode* boolean) +FilteredStream::FilteredStream(CompilerScratch* csb, RecordSource* next, + BoolExprNode* boolean, double selectivity) : m_next(next), m_boolean(boolean), m_anyBoolean(NULL), m_ansiAny(false), m_ansiAll(false), m_ansiNot(false) { fb_assert(m_next && m_boolean); m_impure = csb->allocImpure(); + + const auto cardinality = next->getCardinality(); + Optimizer::adjustSelectivity(selectivity, MAXIMUM_SELECTIVITY, cardinality); + m_cardinality = cardinality * selectivity; } void FilteredStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -56,7 +62,7 @@ void FilteredStream::open(thread_db* tdbb) const void FilteredStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -74,7 +80,7 @@ bool FilteredStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -91,7 +97,7 @@ bool FilteredStream::getRecord(thread_db* tdbb) const bool FilteredStream::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); return m_next->refetchRecord(tdbb) && m_boolean->execute(tdbb, request); @@ -105,7 +111,10 @@ bool FilteredStream::lockRecord(thread_db* tdbb) const void FilteredStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Filter"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -120,7 +129,7 @@ void FilteredStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void FilteredStream::invalidateRecords(jrd_req* request) const +void FilteredStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -132,7 +141,7 @@ void FilteredStream::nullRecords(thread_db* tdbb) const bool FilteredStream::evaluateBoolean(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); // For ANY and ALL clauses (ALL is handled as a negated ANY), // we must first detect them, and then make sure that the returned diff --git a/src/jrd/recsrc/FirstRowsStream.cpp b/src/jrd/recsrc/FirstRowsStream.cpp index f5deb5328c..8fa3fecaf7 100644 --- a/src/jrd/recsrc/FirstRowsStream.cpp +++ b/src/jrd/recsrc/FirstRowsStream.cpp @@ -26,6 +26,7 @@ #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -42,11 +43,16 @@ FirstRowsStream::FirstRowsStream(CompilerScratch* csb, RecordSource* next, Value fb_assert(m_next && m_value); m_impure = csb->allocImpure(); + + const auto valueConst = nodeAs(value); + const auto valueDesc = valueConst ? &valueConst->litDesc : nullptr; + m_cardinality = (valueDesc && valueDesc->dsc_dtype == dtype_long) ? + valueConst->getSlong() : DEFAULT_CARDINALITY; } void FirstRowsStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = 0; @@ -67,7 +73,7 @@ void FirstRowsStream::open(thread_db* tdbb) const void FirstRowsStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -85,7 +91,7 @@ bool FirstRowsStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -115,7 +121,10 @@ bool FirstRowsStream::lockRecord(thread_db* tdbb) const void FirstRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "First N Records"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -130,7 +139,7 @@ void FirstRowsStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void FirstRowsStream::invalidateRecords(jrd_req* request) const +void FirstRowsStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index 73b77f8f05..d7bcbe9e55 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -43,11 +43,12 @@ FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSou fb_assert(m_arg1 && m_arg2); m_impure = csb->allocImpure(); + m_cardinality = arg1->getCardinality() + arg2->getCardinality(); } void FullOuterJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_first; @@ -57,7 +58,7 @@ void FullOuterJoin::open(thread_db* tdbb) const void FullOuterJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -78,7 +79,7 @@ bool FullOuterJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -141,7 +142,7 @@ void FullOuterJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_arg2->findUsedStreams(streams, expandAll); } -void FullOuterJoin::invalidateRecords(jrd_req* request) const +void FullOuterJoin::invalidateRecords(Request* request) const { m_arg1->invalidateRecords(request); m_arg2->invalidateRecords(request); diff --git a/src/jrd/recsrc/FullTableScan.cpp b/src/jrd/recsrc/FullTableScan.cpp index d8096fea15..563fc07d54 100644 --- a/src/jrd/recsrc/FullTableScan.cpp +++ b/src/jrd/recsrc/FullTableScan.cpp @@ -45,13 +45,14 @@ FullTableScan::FullTableScan(CompilerScratch* csb, const string& alias, m_dbkeyRanges(csb->csb_pool, dbkeyRanges) { m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality; } void FullTableScan::open(thread_db* tdbb) const { Database* const dbb = tdbb->getDatabase(); Attachment* const attachment = tdbb->getAttachment(); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -112,7 +113,7 @@ void FullTableScan::open(thread_db* tdbb) const void FullTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -135,7 +136,7 @@ bool FullTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -185,6 +186,7 @@ void FullTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index 5dab9e12f4..5fcee2fa25 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -30,6 +30,7 @@ #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/intl_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -44,6 +45,15 @@ using namespace Jrd; static const ULONG HASH_SIZE = 1009; static const ULONG BUCKET_PREALLOCATE_SIZE = 32; // 256 bytes per slot +unsigned HashJoin::maxCapacity() +{ + // Binary search across 1000 collisions is computationally similar to + // linear searc across 10 collisions. We use this number as a rough + // estimation of whether the lookup performance is likely to be acceptable. + return HASH_SIZE * 1000; +} + + class HashJoin::HashTable : public PermanentStorage { class CollisionList @@ -214,6 +224,7 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, fb_assert(count >= 2); m_impure = csb->allocImpure(); + m_cardinality = args[0]->getCardinality(); m_leader.source = args[0]; m_leader.keys = keys[0]; @@ -248,6 +259,10 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, RecordSource* const sub_rsb = args[i]; fb_assert(sub_rsb); + m_cardinality *= sub_rsb->getCardinality(); + for (auto keyCount = keys[i]->getCount(); keyCount; keyCount--) + m_cardinality *= REDUCE_SELECTIVITY_FACTOR_EQUALITY; + SubStream sub; sub.buffer = FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, sub_rsb); sub.keys = keys[i]; @@ -283,7 +298,7 @@ HashJoin::HashJoin(thread_db* tdbb, CompilerScratch* csb, FB_SIZE_T count, void HashJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_mustread; @@ -324,7 +339,7 @@ void HashJoin::open(thread_db* tdbb) const void HashJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); invalidateRecords(request); @@ -350,7 +365,7 @@ bool HashJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -431,6 +446,7 @@ void HashJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned leve if (detailed) { plan += printIndent(++level) + "Hash Join (inner)"; + printOptInfo(plan); m_leader.source->print(tdbb, plan, true, level); @@ -470,7 +486,7 @@ void HashJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_args[i].source->findUsedStreams(streams, expandAll); } -void HashJoin::invalidateRecords(jrd_req* request) const +void HashJoin::invalidateRecords(Request* request) const { m_leader.source->invalidateRecords(request); @@ -487,7 +503,7 @@ void HashJoin::nullRecords(thread_db* tdbb) const } ULONG HashJoin::computeHash(thread_db* tdbb, - jrd_req* request, + Request* request, const SubStream& sub, UCHAR* keyBuffer) const { diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index f281cc0024..0befcca78d 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -22,7 +22,6 @@ #include "../jrd/exe.h" #include "../jrd/btr.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/btr_proto.h" #include "../jrd/cch_proto.h" #include "../jrd/cmp_proto.h" @@ -42,7 +41,8 @@ using namespace Jrd; IndexTableScan::IndexTableScan(CompilerScratch* csb, const string& alias, StreamType stream, jrd_rel* relation, - InversionNode* index, USHORT length) + InversionNode* index, USHORT length, + double selectivity) : RecordStream(csb, stream), m_alias(csb->csb_pool, alias), m_relation(relation), m_index(index), m_inversion(NULL), m_condition(NULL), m_length(length), m_offset(0) @@ -58,11 +58,12 @@ IndexTableScan::IndexTableScan(CompilerScratch* csb, const string& alias, size += sizeof(index_desc); m_impure = csb->allocImpure(FB_ALIGNMENT, static_cast(size)); + m_cardinality = csb->csb_rpt[stream].csb_cardinality * selectivity; } void IndexTableScan::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_first | irsb_open; @@ -71,11 +72,17 @@ void IndexTableScan::open(thread_db* tdbb) const RLCK_reserve_relation(tdbb, request->req_transaction, m_relation, false); rpb->rpb_number.setValue(BOF_NUMBER); + + fb_assert(!impure->irsb_nav_lower); + impure->irsb_nav_current_lower = impure->irsb_nav_lower = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key; + + fb_assert(!impure->irsb_nav_upper); + impure->irsb_nav_current_upper = impure->irsb_nav_upper = FB_NEW_POOL(*tdbb->getDefaultPool()) temporary_key; } void IndexTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -100,14 +107,28 @@ void IndexTableScan::close(thread_db* tdbb) const if (impure->irsb_nav_btr_gc_lock) { #ifdef DEBUG_LCK_LIST - if (!impure->irsb_nav_page) - gds__log("DEBUG_LCK_LIST: irsb_nav_btr_gc_lock && !irsb_nav_page"); + if (!impure->irsb_nav_page && impure->irsb_nav_btr_gc_lock->isActive()) + gds__log("DEBUG_LCK_LIST: irsb_nav_btr_gc_lock->isActive() && !irsb_nav_page"); #endif - impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + if (impure->irsb_nav_btr_gc_lock->isActive()) + impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + delete impure->irsb_nav_btr_gc_lock; impure->irsb_nav_btr_gc_lock = NULL; } impure->irsb_nav_page = 0; + + if (impure->irsb_nav_lower) + { + delete impure->irsb_nav_lower; + impure->irsb_nav_current_lower = impure->irsb_nav_lower = NULL; + } + + if (impure->irsb_nav_upper) + { + delete impure->irsb_nav_upper; + impure->irsb_nav_current_upper = impure->irsb_nav_upper = NULL; + } } #ifdef DEBUG_LCK_LIST // paranoid check @@ -115,7 +136,9 @@ void IndexTableScan::close(thread_db* tdbb) const { gds__log("DEBUG_LCK_LIST: irsb_nav_btr_gc_lock && !(irsb_flags & irsb_open)"); - impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + if (impure->irsb_nav_btr_gc_lock->isActive()) + impure->irsb_nav_btr_gc_lock->enablePageGC(tdbb); + delete impure->irsb_nav_btr_gc_lock; impure->irsb_nav_btr_gc_lock = NULL; impure->irsb_nav_page = 0; @@ -127,7 +150,7 @@ bool IndexTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -143,120 +166,137 @@ bool IndexTableScan::getRecord(thread_db* tdbb) const setPage(tdbb, impure, NULL); } - index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); - - // find the last fetched position from the index - const USHORT pageSpaceID = m_relation->getPages(tdbb)->rel_pg_space_id; - win window(pageSpaceID, impure->irsb_nav_page); - - UCHAR* nextPointer = getPosition(tdbb, impure, &window); - if (!nextPointer) + // If this is the first time, start at the beginning + if (!impure->irsb_nav_page) { - rpb->rpb_number.setValid(false); - return false; - } - - temporary_key key; - memcpy(key.key_data, impure->irsb_nav_data, impure->irsb_nav_length); - - const IndexRetrieval* const retrieval = m_index->retrieval; - const USHORT flags = retrieval->irb_generic & (irb_descending | irb_partial | irb_starting); - - // set the upper (or lower) limit for navigational retrieval - temporary_key upper; - if (retrieval->irb_upper_count) - { - upper.key_length = impure->irsb_nav_upper_length; - memcpy(upper.key_data, impure->irsb_nav_data + m_length, upper.key_length); - } - - // Find the next interesting node. If necessary, skip to the next page. - RecordNumber number; - IndexNode node; - while (true) - { - Ods::btree_page* page = (Ods::btree_page*) window.win_buffer; - - UCHAR* pointer = nextPointer; - if (pointer) - { - node.readNode(pointer, true); - number = node.recordNumber; - } - - if (node.isEndLevel) - break; - - if (node.isEndBucket) - { - page = (Ods::btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); - nextPointer = page->btr_nodes + page->btr_jump_size; - continue; - } - - // Build the current key value from the prefix and current node data. - memcpy(key.key_data + node.prefix, node.data, node.length); - key.key_length = node.length + node.prefix; - - // Make sure we haven't hit the upper (or lower) limit. - if (retrieval->irb_upper_count && - compareKeys(idx, key.key_data, key.key_length, &upper, flags) > 0) - { - break; - } - - // skip this record if: - // 1) there is an inversion tree for this index and this record - // is not in the bitmap for the inversion, or - // 2) the record has already been visited - - if ((!(impure->irsb_flags & irsb_mustread) && - (!impure->irsb_nav_bitmap || - !RecordBitmap::test(*impure->irsb_nav_bitmap, number.getValue()))) || - RecordBitmap::test(impure->irsb_nav_records_visited, number.getValue())) - { - nextPointer = node.readNode(pointer, true); - continue; - } - - // reset the current navigational position in the index - rpb->rpb_number = number; - setPosition(tdbb, impure, rpb, &window, pointer, key); - - CCH_RELEASE(tdbb, &window); - - if (VIO_get(tdbb, rpb, request->req_transaction, request->req_pool)) - { - temporary_key value; - - const idx_e result = BTR_key(tdbb, m_relation, rpb->rpb_record, idx, &value, false); - - if (result != idx_e_ok) - { - IndexErrorContext context(m_relation, idx); - context.raise(tdbb, result, rpb->rpb_record); - } - - if (!compareKeys(idx, key.key_data, key.key_length, &value, 0)) - { - // mark in the navigational bitmap that we have visited this record - RBM_SET(tdbb->getDefaultPool(), &impure->irsb_nav_records_visited, - rpb->rpb_number.getValue()); - - rpb->rpb_number.setValid(true); - return true; - } - } - - nextPointer = getPosition(tdbb, impure, &window); - if (!nextPointer) + // initialize for a retrieval + if (!setupBitmaps(tdbb, impure)) { rpb->rpb_number.setValid(false); return false; } } - CCH_RELEASE(tdbb, &window); + index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); + + // find the last fetched position from the index + const USHORT pageSpaceID = m_relation->getPages(tdbb)->rel_pg_space_id; + win window(pageSpaceID, impure->irsb_nav_page); + + const IndexRetrieval* const retrieval = m_index->retrieval; + const USHORT flags = retrieval->irb_generic & (irb_descending | irb_partial | irb_starting); + + do + { + UCHAR* nextPointer = getPosition(tdbb, impure, &window); + if (!nextPointer) + { + rpb->rpb_number.setValid(false); + return false; + } + + temporary_key key; + memcpy(key.key_data, impure->irsb_nav_data, impure->irsb_nav_length); + + // set the upper (or lower) limit for navigational retrieval + temporary_key upper; + if (retrieval->irb_upper_count) + { + upper.key_length = impure->irsb_nav_upper_length; + memcpy(upper.key_data, impure->irsb_nav_data + m_length, upper.key_length); + } + + // Find the next interesting node. If necessary, skip to the next page. + RecordNumber number; + IndexNode node; + while (true) + { + Ods::btree_page* page = (Ods::btree_page*) window.win_buffer; + + UCHAR* pointer = nextPointer; + if (pointer) + { + node.readNode(pointer, true); + number = node.recordNumber; + } + + if (node.isEndLevel) + break; + + if (node.isEndBucket) + { + page = (Ods::btree_page*) CCH_HANDOFF(tdbb, &window, page->btr_sibling, LCK_read, pag_index); + nextPointer = page->btr_nodes + page->btr_jump_size; + continue; + } + + // Build the current key value from the prefix and current node data. + memcpy(key.key_data + node.prefix, node.data, node.length); + key.key_length = node.length + node.prefix; + + // Make sure we haven't hit the upper (or lower) limit. + if (retrieval->irb_upper_count && + compareKeys(idx, key.key_data, key.key_length, &upper, flags) > 0) + { + break; + } + + // skip this record if: + // 1) there is an inversion tree for this index and this record + // is not in the bitmap for the inversion, or + // 2) the record has already been visited + + if ((!(impure->irsb_flags & irsb_mustread) && + (!impure->irsb_nav_bitmap || + !RecordBitmap::test(*impure->irsb_nav_bitmap, number.getValue()))) || + RecordBitmap::test(impure->irsb_nav_records_visited, number.getValue())) + { + nextPointer = node.readNode(pointer, true); + continue; + } + + // reset the current navigational position in the index + rpb->rpb_number = number; + setPosition(tdbb, impure, rpb, &window, pointer, key); + + CCH_RELEASE(tdbb, &window); + + if (VIO_get(tdbb, rpb, request->req_transaction, request->req_pool)) + { + temporary_key value; + + const idx_e result = BTR_key(tdbb, m_relation, rpb->rpb_record, idx, &value, + ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); + + if (result != idx_e_ok) + { + IndexErrorContext context(m_relation, idx); + context.raise(tdbb, result, rpb->rpb_record); + } + + if (!compareKeys(idx, key.key_data, key.key_length, &value, 0)) + { + // mark in the navigational bitmap that we have visited this record + RBM_SET(tdbb->getDefaultPool(), &impure->irsb_nav_records_visited, + rpb->rpb_number.getValue()); + + rpb->rpb_number.setValid(true); + return true; + } + } + + nextPointer = getPosition(tdbb, impure, &window); + if (!nextPointer) + { + rpb->rpb_number.setValid(false); + return false; + } + } + + CCH_RELEASE(tdbb, &window); + + advanceStream(tdbb, impure, &window); + } while (true); // bof or eof must have been set at this point rpb->rpb_number.setValid(false); @@ -270,6 +310,7 @@ void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigne plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; + printOptInfo(plan); printInversion(tdbb, m_index, plan, true, level, true); if (m_inversion) @@ -461,14 +502,34 @@ bool IndexTableScan::findSavedNode(thread_db* tdbb, Impure* impure, win* window, } } +void IndexTableScan::advanceStream(thread_db* tdbb, Impure* impure, win* window) const +{ + impure->irsb_nav_current_lower = impure->irsb_nav_current_lower->key_next.get(); + impure->irsb_nav_current_upper = impure->irsb_nav_current_upper->key_next.get(); + setPage(tdbb, impure, NULL); + window->win_page = 0; +} + UCHAR* IndexTableScan::getPosition(thread_db* tdbb, Impure* impure, win* window) const { - // If this is the first time, start at the beginning - if (!window->win_page.getPageNum()) + while (impure->irsb_nav_current_lower) { - return openStream(tdbb, impure, window); + UCHAR* position = getStreamPosition(tdbb, impure, window); + + if (position) + return position; + + advanceStream(tdbb, impure, window); } + return NULL; +} + +UCHAR* IndexTableScan::getStreamPosition(thread_db* tdbb, Impure* impure, win* window) const +{ + if (!window->win_page.getPageNum()) + return openStream(tdbb, impure, window); + // Re-fetch page and get incarnation counter Ods::btree_page* page = (Ods::btree_page*) CCH_FETCH(tdbb, window, LCK_read, pag_index); @@ -505,9 +566,9 @@ UCHAR* IndexTableScan::getPosition(thread_db* tdbb, Impure* impure, win* window) UCHAR* IndexTableScan::openStream(thread_db* tdbb, Impure* impure, win* window) const { - // initialize for a retrieval - if (!setupBitmaps(tdbb, impure)) - return NULL; + temporary_key* lower = impure->irsb_nav_current_lower; + temporary_key* upper = impure->irsb_nav_current_upper; + const bool firstKeys = lower == impure->irsb_nav_lower; setPage(tdbb, impure, NULL); impure->irsb_nav_length = 0; @@ -515,8 +576,7 @@ UCHAR* IndexTableScan::openStream(thread_db* tdbb, Impure* impure, win* window) // Find the starting leaf page const IndexRetrieval* const retrieval = m_index->retrieval; index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset); - temporary_key lower, upper; - Ods::btree_page* page = BTR_find_page(tdbb, retrieval, window, idx, &lower, &upper); + Ods::btree_page* page = BTR_find_page(tdbb, retrieval, window, idx, lower, upper, firstKeys); setPage(tdbb, impure, window); // find the upper limit for the search @@ -526,12 +586,12 @@ UCHAR* IndexTableScan::openStream(thread_db* tdbb, Impure* impure, win* window) // If upper key length is greater than declared key length, we need // one "excess" byte for correct comparison. Without it there could // be false equality hits. - impure->irsb_nav_upper_length = MIN(m_length + 1, upper.key_length); - memcpy(impure->irsb_nav_data + m_length, upper.key_data, impure->irsb_nav_upper_length); + impure->irsb_nav_upper_length = MIN(m_length + 1, upper->key_length); + memcpy(impure->irsb_nav_data + m_length, upper->key_data, impure->irsb_nav_upper_length); } if (retrieval->irb_lower_count) - limit_ptr = &lower; + limit_ptr = lower; // If there is a starting descriptor, search down index to starting position. // This may involve sibling buckets if splits are in progress. If there diff --git a/src/jrd/recsrc/LocalTableStream.cpp b/src/jrd/recsrc/LocalTableStream.cpp index 727a3934d9..e678f47a9d 100644 --- a/src/jrd/recsrc/LocalTableStream.cpp +++ b/src/jrd/recsrc/LocalTableStream.cpp @@ -25,6 +25,7 @@ #include "../jrd/jrd.h" #include "../jrd/req.h" #include "../dsql/StmtNodes.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -42,6 +43,7 @@ LocalTableStream::LocalTableStream(CompilerScratch* csb, StreamType stream, cons fb_assert(m_table); m_impure = csb->allocImpure(); + m_cardinality = DEFAULT_CARDINALITY; } void LocalTableStream::open(thread_db* tdbb) const @@ -113,7 +115,10 @@ void LocalTableStream::print(thread_db* tdbb, string& plan, bool detailed, unsig //// TODO: Use Local Table name/alias. if (detailed) + { plan += printIndent(++level) + "Local Table Full Scan"; + printOptInfo(plan); + } else { if (!level) diff --git a/src/jrd/recsrc/LockedStream.cpp b/src/jrd/recsrc/LockedStream.cpp index a4d389a4a4..f25a577e30 100644 --- a/src/jrd/recsrc/LockedStream.cpp +++ b/src/jrd/recsrc/LockedStream.cpp @@ -40,11 +40,12 @@ LockedStream::LockedStream(CompilerScratch* csb, RecordSource* next) fb_assert(m_next); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); } void LockedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -54,7 +55,7 @@ void LockedStream::open(thread_db* tdbb) const void LockedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -72,7 +73,7 @@ bool LockedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -105,7 +106,10 @@ bool LockedStream::lockRecord(thread_db* tdbb) const void LockedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Write Lock"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -120,7 +124,7 @@ void LockedStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void LockedStream::invalidateRecords(jrd_req* request) const +void LockedStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index dbfd555577..8aedd7bdb3 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -20,10 +20,10 @@ #include "firebird.h" #include "../jrd/jrd.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/mov_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -42,6 +42,7 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, { const size_t size = sizeof(struct Impure) + count * sizeof(Impure::irsb_mrg_repeat); m_impure = csb->allocImpure(FB_ALIGNMENT, static_cast(size)); + m_cardinality = MINIMUM_CARDINALITY; m_args.resize(count); m_keys.resize(count); @@ -51,6 +52,10 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, fb_assert(args[i]); m_args[i] = args[i]; + m_cardinality *= args[i]->getCardinality(); + for (auto keyCount = keys[i]->getCount(); keyCount; keyCount--) + m_cardinality *= REDUCE_SELECTIVITY_FACTOR_EQUALITY; + fb_assert(keys[i]); m_keys[i] = keys[i]; } @@ -58,7 +63,7 @@ MergeJoin::MergeJoin(CompilerScratch* csb, FB_SIZE_T count, void MergeJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -94,7 +99,7 @@ void MergeJoin::open(thread_db* tdbb) const void MergeJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -129,7 +134,7 @@ bool MergeJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -341,6 +346,7 @@ void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned lev if (detailed) { plan += printIndent(++level) + "Merge Join (inner)"; + printOptInfo(plan); for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->print(tdbb, plan, true, level); @@ -372,7 +378,7 @@ void MergeJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_args[i]->findUsedStreams(streams, expandAll); } -void MergeJoin::invalidateRecords(jrd_req* request) const +void MergeJoin::invalidateRecords(Request* request) const { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->invalidateRecords(request); @@ -387,7 +393,7 @@ void MergeJoin::nullRecords(thread_db* tdbb) const int MergeJoin::compare(thread_db* tdbb, const NestValueArray* node1, const NestValueArray* node2) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); const NestConst* ptr1 = node1->begin(); const NestConst* ptr2 = node2->begin(); @@ -436,7 +442,7 @@ UCHAR* MergeJoin::getData(thread_db* /*tdbb*/, MergeFile* mfb, SLONG record) con SLONG MergeJoin::getRecord(thread_db* tdbb, FB_SIZE_T index) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); const SortedStream* const sort_rsb = m_args[index]; @@ -474,7 +480,7 @@ SLONG MergeJoin::getRecord(thread_db* tdbb, FB_SIZE_T index) const bool MergeJoin::fetchRecord(thread_db* tdbb, FB_SIZE_T index) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); Impure::irsb_mrg_repeat* tail = &impure->irsb_mrg_rpt[index]; diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index 0446f601b7..e7adca7aa1 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -24,6 +24,7 @@ #include "../jrd/evl_proto.h" #include "../jrd/met_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -38,11 +39,15 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSour : m_joinType(INNER_JOIN), m_args(csb->csb_pool), m_boolean(NULL) { m_impure = csb->allocImpure(); + m_cardinality = MINIMUM_CARDINALITY; m_args.resize(count); for (FB_SIZE_T i = 0; i < count; i++) + { m_args[i] = args[i]; + m_cardinality *= args[i]->getCardinality(); + } } NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner, @@ -55,11 +60,13 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, Record m_args.add(outer); m_args.add(inner); + + m_cardinality = outer->getCardinality() * inner->getCardinality(); } void NestedLoopJoin::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open | irsb_first | irsb_mustread; @@ -67,7 +74,7 @@ void NestedLoopJoin::open(thread_db* tdbb) const void NestedLoopJoin::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -86,7 +93,7 @@ bool NestedLoopJoin::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -226,6 +233,8 @@ void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigne fb_assert(false); } + printOptInfo(plan); + for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->print(tdbb, plan, true, level); } @@ -257,7 +266,7 @@ void NestedLoopJoin::findUsedStreams(StreamList& streams, bool expandAll) const m_args[i]->findUsedStreams(streams, expandAll); } -void NestedLoopJoin::invalidateRecords(jrd_req* request) const +void NestedLoopJoin::invalidateRecords(Request* request) const { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->invalidateRecords(request); diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index e80cd642f4..a6825ccaf9 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -29,6 +29,7 @@ #include "../jrd/vio_proto.h" #include "../jrd/trace/TraceManager.h" #include "../jrd/trace/TraceJrdHelpers.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -46,6 +47,7 @@ ProcedureScan::ProcedureScan(CompilerScratch* csb, const string& alias, StreamTy m_procedure(procedure), m_sourceList(sourceList), m_targetList(targetList), m_message(message) { m_impure = csb->allocImpure(); + m_cardinality = DEFAULT_CARDINALITY; fb_assert(!sourceList == !targetList); @@ -61,10 +63,16 @@ void ProcedureScan::open(thread_db* tdbb) const Arg::Gds(isc_proc_pack_not_implemented) << Arg::Str(m_procedure->getName().identifier) << Arg::Str(m_procedure->getName().package)); } + else if (!m_procedure->isDefined()) + { + status_exception::raise( + Arg::Gds(isc_prcnotdef) << Arg::Str(m_procedure->getName().toString()) << + Arg::Gds(isc_modnotfound)); + } const_cast(m_procedure)->checkReload(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -98,7 +106,7 @@ void ProcedureScan::open(thread_db* tdbb) const im = NULL; } - jrd_req* const proc_request = m_procedure->getStatement()->findRequest(tdbb); + Request* const proc_request = m_procedure->getStatement()->findRequest(tdbb); impure->irsb_req_handle = proc_request; // req_proc_fetch flag used only when fetching rows, so @@ -108,7 +116,7 @@ void ProcedureScan::open(thread_db* tdbb) const try { - proc_request->req_gmt_timestamp = request->req_gmt_timestamp; + proc_request->setGmtTimeStamp(request->getGmtTimeStamp()); TraceProcExecute trace(tdbb, proc_request, request, m_targetList); @@ -134,7 +142,7 @@ void ProcedureScan::open(thread_db* tdbb) const void ProcedureScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -144,7 +152,7 @@ void ProcedureScan::close(thread_db* tdbb) const { impure->irsb_flags &= ~irsb_open; - jrd_req* const proc_request = impure->irsb_req_handle; + Request* const proc_request = impure->irsb_req_handle; if (proc_request) { @@ -166,7 +174,7 @@ bool ProcedureScan::getRecord(thread_db* tdbb) const UserId* invoker = m_procedure->invoker ? m_procedure->invoker : tdbb->getAttachment()->att_ss_user; AutoSetRestore userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -185,7 +193,7 @@ bool ProcedureScan::getRecord(thread_db* tdbb) const Record* const record = VIO_record(tdbb, rpb, m_format, tdbb->getDefaultPool()); - jrd_req* const proc_request = impure->irsb_req_handle; + Request* const proc_request = impure->irsb_req_handle; TraceProcFetch trace(tdbb, proc_request); @@ -247,6 +255,7 @@ void ProcedureScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned { plan += printIndent(++level) + "Procedure " + printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan"; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index 425255707d..67c68191f2 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -25,7 +25,6 @@ #include "../jrd/btr.h" #include "../jrd/intl.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/dpm_proto.h" #include "../jrd/err_proto.h" @@ -40,6 +39,9 @@ using namespace Firebird; using namespace Jrd; +// Disabled so far, should be uncommented for debugging/testing +//#define PRINT_OPT_INFO // print optimizer info (cardinality, cost) in plans + // Record source class // ------------------- @@ -173,6 +175,16 @@ void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversio } } +void RecordSource::printOptInfo(string& plan) const +{ +#ifdef PRINT_OPT_INFO + string info; + // Add 0.5 to convert double->int truncation into rounding + info.printf(" [rows: %" UQUADFORMAT "]", (FB_UINT64) (m_cardinality + 0.5)); + plan += info; +#endif +} + RecordSource::~RecordSource() { } @@ -189,7 +201,7 @@ RecordStream::RecordStream(CompilerScratch* csb, StreamType stream, const Format bool RecordStream::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request->req_transaction; record_param* const rpb = &request->req_rpb[m_stream]; @@ -208,7 +220,7 @@ bool RecordStream::refetchRecord(thread_db* tdbb) const bool RecordStream::lockRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_tra* const transaction = request->req_transaction; record_param* const rpb = &request->req_rpb[m_stream]; @@ -232,7 +244,7 @@ void RecordStream::findUsedStreams(StreamList& streams, bool /*expandAll*/) cons streams.add(m_stream); } -void RecordStream::invalidateRecords(jrd_req* request) const +void RecordStream::invalidateRecords(Request* request) const { record_param* const rpb = &request->req_rpb[m_stream]; rpb->rpb_number.setValid(false); @@ -240,7 +252,7 @@ void RecordStream::invalidateRecords(jrd_req* request) const void RecordStream::nullRecords(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; rpb->rpb_number.setValid(false); diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 83b1b7777b..9b0f33d981 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -28,21 +28,20 @@ #include "../common/classes/NestConst.h" #include "../jrd/RecordSourceNodes.h" #include "../jrd/req.h" -#include "../jrd/rse.h" +#include "../jrd/RecordBuffer.h" #include "firebird/impl/inf_pub.h" #include "../jrd/evl_proto.h" namespace Jrd { class thread_db; - class jrd_req; + class Request; class jrd_prc; class AggNode; class BoolExprNode; class DeclareLocalTableNode; class Sort; class CompilerScratch; - class RecordBuffer; class BtrPageGCLock; struct index_desc; struct record_param; @@ -69,7 +68,7 @@ namespace Jrd bool detailed, unsigned level) const = 0; virtual void markRecursive() = 0; - virtual void invalidateRecords(jrd_req* request) const = 0; + virtual void invalidateRecords(Request* request) const = 0; virtual void findUsedStreams(StreamList& streams, bool expandAll = false) const = 0; virtual void nullRecords(thread_db* tdbb) const = 0; @@ -86,6 +85,11 @@ namespace Jrd return true; } + double getCardinality() const + { + return m_cardinality; + } + protected: // Generic impure block struct Impure @@ -100,7 +104,7 @@ namespace Jrd static const ULONG irsb_singular_processed = 16; RecordSource() - : m_impure(0), m_recursive(false) + : m_impure(0), m_recursive(false), m_cardinality(0.0) {} static Firebird::string printName(thread_db* tdbb, const Firebird::string& name, bool quote = true); @@ -111,12 +115,14 @@ namespace Jrd static void printInversion(thread_db* tdbb, const InversionNode* inversion, Firebird::string& plan, bool detailed, unsigned level, bool navigation = false); + void printOptInfo(Firebird::string& plan) const; static void saveRecord(thread_db* tdbb, record_param* rpb); static void restoreRecord(thread_db* tdbb, record_param* rpb); ULONG m_impure; bool m_recursive; + double m_cardinality; }; @@ -131,7 +137,7 @@ namespace Jrd bool lockRecord(thread_db* tdbb) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -144,7 +150,7 @@ namespace Jrd // Primary (table scan) access methods - class FullTableScan : public RecordStream + class FullTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -171,7 +177,7 @@ namespace Jrd Firebird::Array m_dbkeyRanges; }; - class BitmapTableScan : public RecordStream + class BitmapTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -180,7 +186,8 @@ namespace Jrd public: BitmapTableScan(CompilerScratch* csb, const Firebird::string& alias, - StreamType stream, jrd_rel* relation, InversionNode* inversion); + StreamType stream, jrd_rel* relation, + InversionNode* inversion, double selectivity); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -196,7 +203,7 @@ namespace Jrd NestConst const m_inversion; }; - class IndexTableScan : public RecordStream + class IndexTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -206,6 +213,10 @@ namespace Jrd RecordBitmap** irsb_nav_bitmap; // bitmap for inversion tree RecordBitmap* irsb_nav_records_visited; // bitmap of records already retrieved BtrPageGCLock* irsb_nav_btr_gc_lock; // lock to prevent removal of currently walked index page + temporary_key* irsb_nav_lower; // lower (possible multiple) key + temporary_key* irsb_nav_upper; // upper (possible multiple) key + temporary_key* irsb_nav_current_lower; // current lower key + temporary_key* irsb_nav_current_upper; // current upper key USHORT irsb_nav_offset; // page offset of current index node USHORT irsb_nav_upper_length; // length of upper key value USHORT irsb_nav_length; // length of expanded key @@ -215,7 +226,8 @@ namespace Jrd public: IndexTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, jrd_rel* relation, - InversionNode* index, USHORT keyLength); + InversionNode* index, USHORT keyLength, + double selectivity); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -235,7 +247,9 @@ namespace Jrd private: int compareKeys(const index_desc*, const UCHAR*, USHORT, const temporary_key*, USHORT) const; bool findSavedNode(thread_db* tdbb, Impure* impure, win* window, UCHAR**) const; + void advanceStream(thread_db* tdbb, Impure* impure, win* window) const; UCHAR* getPosition(thread_db* tdbb, Impure* impure, win* window) const; + UCHAR* getStreamPosition(thread_db* tdbb, Impure* impure, win* window) const; UCHAR* openStream(thread_db* tdbb, Impure* impure, win* window) const; void setPage(thread_db* tdbb, Impure* impure, win* window) const; void setPosition(thread_db* tdbb, Impure* impure, record_param*, @@ -251,7 +265,7 @@ namespace Jrd FB_SIZE_T m_offset; }; - class ExternalTableScan : public RecordStream + class ExternalTableScan final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -303,11 +317,11 @@ namespace Jrd const Firebird::string m_alias; }; - class ProcedureScan : public RecordStream + class ProcedureScan final : public RecordStream { struct Impure : public RecordSource::Impure { - jrd_req* irsb_req_handle; + Request* irsb_req_handle; UCHAR* irsb_message; }; @@ -356,7 +370,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -384,7 +398,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -414,7 +428,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -450,7 +464,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -468,7 +482,8 @@ namespace Jrd class FilteredStream : public RecordSource { public: - FilteredStream(CompilerScratch* csb, RecordSource* next, BoolExprNode* boolean); + FilteredStream(CompilerScratch* csb, RecordSource* next, + BoolExprNode* boolean, double selectivity); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -481,7 +496,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -531,12 +546,21 @@ namespace Jrd public: struct Item { - void clear() + void reset(NestConst _node, ULONG _flagOffset = 0) { desc.clear(); - flagOffset = fieldId = 0; - stream = 0; - node = NULL; + stream = fieldId = 0; + node = _node; + flagOffset = _flagOffset; + } + + void reset(StreamType _stream, SSHORT _fieldId, ULONG _flagOffset = 0) + { + desc.clear(); + node = nullptr; + stream = _stream; + fieldId = _fieldId; + flagOffset = _flagOffset; } StreamType stream; // stream for field id @@ -576,7 +600,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -599,7 +623,7 @@ namespace Jrd bool compareKeys(const UCHAR* p, const UCHAR* q) const; UCHAR* getData(thread_db* tdbb) const; - void mapData(thread_db* tdbb, jrd_req* request, UCHAR* data) const; + void mapData(thread_db* tdbb, Request* request, UCHAR* data) const; bool isKey(const dsc* desc) const { @@ -625,7 +649,7 @@ namespace Jrd class SlidingWindow { public: - SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, jrd_req* request, + SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, Request* request, FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd, FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd); ~SlidingWindow(); @@ -715,28 +739,28 @@ namespace Jrd bool lockRecord(thread_db* tdbb) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; protected: - Impure* getImpure(jrd_req* request) const + Impure* getImpure(Request* request) const { return request->getImpure(m_impure); } bool evaluateGroup(thread_db* tdbb) const; - void aggInit(thread_db* tdbb, jrd_req* request, const MapNode* map) const; - bool aggPass(thread_db* tdbb, jrd_req* request, + void aggInit(thread_db* tdbb, Request* request, const MapNode* map) const; + bool aggPass(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const; - void aggExecute(thread_db* tdbb, jrd_req* request, + void aggExecute(thread_db* tdbb, Request* request, const NestValueArray& sourceList, const NestValueArray& targetList) const; - void aggFinish(thread_db* tdbb, jrd_req* request, const MapNode* map) const; + void aggFinish(thread_db* tdbb, Request* request, const MapNode* map) const; // Cache the values of a group/order in the impure. template - void cacheValues(thread_db* tdbb, jrd_req* request, + void cacheValues(thread_db* tdbb, Request* request, const NestValueArray* group, impure_value* values, AdjustFunctor adjustFunctor) const { @@ -762,11 +786,11 @@ namespace Jrd } } - int lookForChange(thread_db* tdbb, jrd_req* request, + int lookForChange(thread_db* tdbb, Request* request, const NestValueArray* group, const SortNode* sort, impure_value* values) const; private: - bool getNextRecord(thread_db* tdbb, jrd_req* request) const; + bool getNextRecord(thread_db* tdbb, Request* request) const; protected: NestConst m_next; @@ -775,7 +799,7 @@ namespace Jrd bool m_oneRowWhenEmpty; }; - class AggregatedStream : public BaseAggWinStream + class AggregatedStream final : public BaseAggWinStream { public: AggregatedStream(thread_db* tdbb, CompilerScratch* csb, StreamType stream, @@ -793,7 +817,7 @@ namespace Jrd using FrameExtent = WindowClause::FrameExtent; using Exclusion = WindowClause::Exclusion; - class WindowStream : public BaseAggWinStream + class WindowStream final : public BaseAggWinStream { private: struct AdjustFunctor @@ -830,7 +854,7 @@ namespace Jrd }; public: - struct Impure : public BaseAggWinStream::Impure + struct Impure final : public BaseAggWinStream::Impure { impure_value* orderValues; SINT64 partitionPending, rangePending; @@ -856,16 +880,16 @@ namespace Jrd void nullRecords(thread_db* tdbb) const; protected: - Impure* getImpure(jrd_req* request) const + Impure* getImpure(Request* request) const { return request->getImpure(m_impure); } private: - const void getFrameValue(thread_db* tdbb, jrd_req* request, + const void getFrameValue(thread_db* tdbb, Request* request, const Frame* frame, impure_value_ex* impureValue) const; - SINT64 locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure, + SINT64 locateFrameRange(thread_db* tdbb, Request* request, Impure* impure, const Frame* frame, const dsc* offsetDesc, SINT64 position) const; private: @@ -880,7 +904,7 @@ namespace Jrd }; public: - WindowedStream(thread_db* tdbb, CompilerScratch* csb, + WindowedStream(thread_db* tdbb, Optimizer* opt, Firebird::ObjectsArray& windows, RecordSource* next); void open(thread_db* tdbb) const override; @@ -894,7 +918,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -910,7 +934,7 @@ namespace Jrd public: virtual void locate(thread_db* tdbb, FB_UINT64 position) const = 0; virtual FB_UINT64 getCount(thread_db* tdbb) const = 0; - virtual FB_UINT64 getPosition(jrd_req* request) const = 0; + virtual FB_UINT64 getPosition(Request* request) const = 0; }; class BufferedStream : public BaseBufferedStream @@ -954,7 +978,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -962,7 +986,7 @@ namespace Jrd void locate(thread_db* tdbb, FB_UINT64 position) const override; FB_UINT64 getCount(thread_db* tdbb) const override; - FB_UINT64 getPosition(jrd_req* request) const override + FB_UINT64 getPosition(Request* request) const override { Impure* const impure = request->getImpure(m_impure); return impure->irsb_position; @@ -994,7 +1018,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1023,7 +1047,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1072,13 +1096,15 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; + static unsigned maxCapacity(); + private: - ULONG computeHash(thread_db* tdbb, jrd_req* request, + ULONG computeHash(thread_db* tdbb, Request* request, const SubStream& sub, UCHAR* buffer) const; bool fetchRecord(thread_db* tdbb, Impure* impure, FB_SIZE_T stream) const; @@ -1132,7 +1158,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1148,7 +1174,7 @@ namespace Jrd Firebird::Array m_keys; }; - class LocalTableStream : public RecordStream + class LocalTableStream final : public RecordStream { public: LocalTableStream(CompilerScratch* csb, StreamType stream, const DeclareLocalTableNode* table); @@ -1166,7 +1192,7 @@ namespace Jrd const DeclareLocalTableNode* m_table; }; - class Union : public RecordStream + class Union final : public RecordStream { struct Impure : public RecordSource::Impure { @@ -1176,7 +1202,7 @@ namespace Jrd public: Union(CompilerScratch* csb, StreamType stream, FB_SIZE_T argCount, RecordSource* const* args, NestConst* maps, - FB_SIZE_T streamCount, const StreamType* streams); + const StreamList& streams); void open(thread_db* tdbb) const override; void close(thread_db* tdbb) const override; @@ -1189,7 +1215,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; private: @@ -1198,7 +1224,7 @@ namespace Jrd StreamList m_streams; }; - class RecursiveStream : public RecordStream + class RecursiveStream final : public RecordStream { static const FB_SIZE_T MAX_RECURSE_LEVEL = 1024; @@ -1216,7 +1242,7 @@ namespace Jrd RecursiveStream(CompilerScratch* csb, StreamType stream, StreamType mapStream, RecordSource* root, RecordSource* inner, const MapNode* rootMap, const MapNode* innerMap, - FB_SIZE_T streamCount, const StreamType* innerStreams, + const StreamList& innerStreams, ULONG saveOffset); void open(thread_db* tdbb) const override; @@ -1230,11 +1256,11 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; private: - void cleanupLevel(jrd_req* request, Impure* impure) const; + void cleanupLevel(Request* request, Impure* impure) const; const StreamType m_mapStream; NestConst m_root; @@ -1268,7 +1294,7 @@ namespace Jrd bool detailed, unsigned level) const override; void markRecursive() override; - void invalidateRecords(jrd_req* request) const override; + void invalidateRecords(Request* request) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; diff --git a/src/jrd/recsrc/RecursiveStream.cpp b/src/jrd/recsrc/RecursiveStream.cpp index f6339df6f9..837b43b419 100644 --- a/src/jrd/recsrc/RecursiveStream.cpp +++ b/src/jrd/recsrc/RecursiveStream.cpp @@ -39,25 +39,21 @@ using namespace Jrd; RecursiveStream::RecursiveStream(CompilerScratch* csb, StreamType stream, StreamType mapStream, RecordSource* root, RecordSource* inner, const MapNode* rootMap, const MapNode* innerMap, - FB_SIZE_T streamCount, const StreamType* innerStreams, + const StreamList& innerStreams, ULONG saveOffset) : RecordStream(csb, stream), m_mapStream(mapStream), m_root(root), m_inner(inner), m_rootMap(rootMap), m_innerMap(innerMap), - m_innerStreams(csb->csb_pool), + m_innerStreams(csb->csb_pool, innerStreams), m_saveOffset(saveOffset) { fb_assert(m_root && m_inner && m_rootMap && m_innerMap); m_impure = csb->allocImpure(); + m_cardinality = root->getCardinality() * inner->getCardinality(); m_saveSize = csb->csb_impure - saveOffset; - m_innerStreams.resize(streamCount); - - for (FB_SIZE_T i = 0; i < streamCount; i++) - m_innerStreams[i] = innerStreams[i]; - // To make aggregates, unions and nested recursions inside the inner stream work correctly, // we need to add all the child streams as well. See CORE-3683 for the test case. m_inner->findUsedStreams(m_innerStreams, true); @@ -68,7 +64,7 @@ RecursiveStream::RecursiveStream(CompilerScratch* csb, StreamType stream, Stream void RecursiveStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -96,7 +92,7 @@ void RecursiveStream::open(thread_db* tdbb) const void RecursiveStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); invalidateRecords(request); @@ -119,7 +115,7 @@ bool RecursiveStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); Impure* const saveImpure = request->getImpure(m_saveOffset); @@ -248,6 +244,8 @@ void RecursiveStream::print(thread_db* tdbb, string& plan, bool detailed, unsign if (detailed) { plan += printIndent(++level) + "Recursion"; + printOptInfo(plan); + m_root->print(tdbb, plan, true, level); m_inner->print(tdbb, plan, true, level); } @@ -273,7 +271,7 @@ void RecursiveStream::markRecursive() m_inner->markRecursive(); } -void RecursiveStream::invalidateRecords(jrd_req* request) const +void RecursiveStream::invalidateRecords(Request* request) const { m_root->invalidateRecords(request); m_inner->invalidateRecords(request); @@ -293,7 +291,7 @@ void RecursiveStream::findUsedStreams(StreamList& streams, bool expandAll) const } } -void RecursiveStream::cleanupLevel(jrd_req* request, Impure* impure) const +void RecursiveStream::cleanupLevel(Request* request, Impure* impure) const { Impure* const saveImpure = request->getImpure(m_saveOffset); diff --git a/src/jrd/recsrc/SingularStream.cpp b/src/jrd/recsrc/SingularStream.cpp index 45e3f0b03e..25cce31f91 100644 --- a/src/jrd/recsrc/SingularStream.cpp +++ b/src/jrd/recsrc/SingularStream.cpp @@ -22,6 +22,7 @@ #include "../jrd/req.h" #include "../jrd/cmp_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -40,11 +41,12 @@ SingularStream::SingularStream(CompilerScratch* csb, RecordSource* next) m_next->findUsedStreams(m_streams); m_impure = csb->allocImpure(); + m_cardinality = MINIMUM_CARDINALITY; } void SingularStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -54,7 +56,7 @@ void SingularStream::open(thread_db* tdbb) const void SingularStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -72,7 +74,7 @@ bool SingularStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -92,7 +94,7 @@ bool SingularStream::getRecord(thread_db* tdbb) const void SingularStream::doGetRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); const FB_SIZE_T streamCount = m_streams.getCount(); @@ -145,7 +147,10 @@ bool SingularStream::lockRecord(thread_db* tdbb) const void SingularStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Singularity Check"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -160,7 +165,7 @@ void SingularStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void SingularStream::invalidateRecords(jrd_req* request) const +void SingularStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/SkipRowsStream.cpp b/src/jrd/recsrc/SkipRowsStream.cpp index 6a22ea099f..c2625d2221 100644 --- a/src/jrd/recsrc/SkipRowsStream.cpp +++ b/src/jrd/recsrc/SkipRowsStream.cpp @@ -42,11 +42,12 @@ SkipRowsStream::SkipRowsStream(CompilerScratch* csb, RecordSource* next, ValueEx fb_assert(m_next && m_value); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); } void SkipRowsStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -66,7 +67,7 @@ void SkipRowsStream::open(thread_db* tdbb) const void SkipRowsStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -84,7 +85,7 @@ bool SkipRowsStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -116,7 +117,10 @@ bool SkipRowsStream::lockRecord(thread_db* tdbb) const void SkipRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Skip N Records"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -131,7 +135,7 @@ void SkipRowsStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void SkipRowsStream::invalidateRecords(jrd_req* request) const +void SkipRowsStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index 9c160508ad..3c57bfcebf 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -32,6 +32,7 @@ #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/vio_proto.h" +#include "../jrd/optimizer/Optimizer.h" #include "RecordSource.h" @@ -48,11 +49,15 @@ SortedStream::SortedStream(CompilerScratch* csb, RecordSource* next, SortMap* ma fb_assert(m_next && m_map); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); + + if (m_map->flags & FLAG_PROJECT) + m_cardinality *= DEFAULT_SELECTIVITY; } void SortedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -67,7 +72,7 @@ void SortedStream::open(thread_db* tdbb) const void SortedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -88,7 +93,7 @@ bool SortedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -128,6 +133,7 @@ void SortedStream::print(thread_db* tdbb, string& plan, plan += printIndent(++level) + ((m_map->flags & FLAG_PROJECT) ? "Unique Sort" : "Sort") + extras; + printOptInfo(plan); m_next->print(tdbb, plan, true, level); } @@ -150,7 +156,7 @@ void SortedStream::findUsedStreams(StreamList& streams, bool expandAll) const m_next->findUsedStreams(streams, expandAll); } -void SortedStream::invalidateRecords(jrd_req* request) const +void SortedStream::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -162,7 +168,7 @@ void SortedStream::nullRecords(thread_db* tdbb) const Sort* SortedStream::init(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); m_next->open(tdbb); ULONG records = 0; @@ -310,7 +316,7 @@ bool SortedStream::compareKeys(const UCHAR* p, const UCHAR* q) const UCHAR* SortedStream::getData(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); ULONG* data = nullptr; @@ -319,7 +325,7 @@ UCHAR* SortedStream::getData(thread_db* tdbb) const return reinterpret_cast(data); } -void SortedStream::mapData(thread_db* tdbb, jrd_req* request, UCHAR* data) const +void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const { StreamType stream = INVALID_STREAM; dsc from, to; diff --git a/src/jrd/recsrc/Union.cpp b/src/jrd/recsrc/Union.cpp index 1f3692ea50..6ad1f3bb39 100644 --- a/src/jrd/recsrc/Union.cpp +++ b/src/jrd/recsrc/Union.cpp @@ -35,9 +35,9 @@ using namespace Jrd; Union::Union(CompilerScratch* csb, StreamType stream, FB_SIZE_T argCount, RecordSource* const* args, NestConst* maps, - FB_SIZE_T streamCount, const StreamType* streams) + const StreamList& streams) : RecordStream(csb, stream), m_args(csb->csb_pool), m_maps(csb->csb_pool), - m_streams(csb->csb_pool) + m_streams(csb->csb_pool, streams) { fb_assert(argCount); @@ -46,22 +46,20 @@ Union::Union(CompilerScratch* csb, StreamType stream, m_args.resize(argCount); for (FB_SIZE_T i = 0; i < argCount; i++) + { m_args[i] = args[i]; + m_cardinality += args[i]->getCardinality(); + } m_maps.resize(argCount); for (FB_SIZE_T i = 0; i < argCount; i++) m_maps[i] = maps[i]; - - m_streams.resize(streamCount); - - for (FB_SIZE_T i = 0; i < streamCount; i++) - m_streams[i] = streams[i]; } void Union::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -82,7 +80,7 @@ void Union::open(thread_db* tdbb) const void Union::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -101,7 +99,7 @@ bool Union::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -144,7 +142,7 @@ bool Union::getRecord(thread_db* tdbb) const bool Union::refetchRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_count >= m_args.getCount()) @@ -155,7 +153,7 @@ bool Union::refetchRecord(thread_db* tdbb) const bool Union::lockRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_count >= m_args.getCount()) @@ -169,6 +167,7 @@ void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level) if (detailed) { plan += printIndent(++level) + (m_args.getCount() == 1 ? "Materialize" : "Union"); + printOptInfo(plan); for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->print(tdbb, plan, true, level); @@ -197,7 +196,7 @@ void Union::markRecursive() m_args[i]->markRecursive(); } -void Union::invalidateRecords(jrd_req* request) const +void Union::invalidateRecords(Request* request) const { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) m_args[i]->invalidateRecords(request); diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 0cc73fe9d2..21cfac5ac5 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -23,7 +23,6 @@ #include "firebird.h" #include "../jrd/jrd.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/cmp_proto.h" #include "../jrd/met_proto.h" #include "../jrd/vio_proto.h" @@ -42,11 +41,12 @@ VirtualTableScan::VirtualTableScan(CompilerScratch* csb, const string& alias, : RecordStream(csb, stream), m_relation(relation), m_alias(csb->csb_pool, alias) { m_impure = csb->allocImpure(); + m_cardinality = csb->csb_rpt[stream].csb_cardinality; } void VirtualTableScan::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -61,7 +61,7 @@ void VirtualTableScan::open(thread_db* tdbb) const void VirtualTableScan::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -75,7 +75,7 @@ bool VirtualTableScan::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = request->getImpure(m_impure); @@ -116,6 +116,7 @@ void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsig { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; + printOptInfo(plan); } else { diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index e4d041fe98..f261a7b02c 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -23,10 +23,11 @@ #include "firebird.h" #include "../dsql/Nodes.h" #include "../jrd/mov_proto.h" -#include "../jrd/opt_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/par_proto.h" +#include "../jrd/optimizer/Optimizer.h" + #include "RecordSource.h" using namespace Firebird; @@ -60,14 +61,14 @@ namespace void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level) const; void markRecursive(); - void invalidateRecords(jrd_req* request) const; + void invalidateRecords(Request* request) const; void findUsedStreams(StreamList& streams, bool expandAll) const; void nullRecords(thread_db* tdbb) const; void locate(thread_db* tdbb, FB_UINT64 position) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_position = position; } @@ -77,7 +78,7 @@ namespace return m_next->getCount(tdbb); } - FB_UINT64 getPosition(jrd_req* request) const + FB_UINT64 getPosition(Request* request) const { Impure* const impure = request->getImpure(m_impure); return impure->irsb_position; @@ -97,7 +98,7 @@ namespace void BufferedStreamWindow::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -106,7 +107,7 @@ namespace void BufferedStreamWindow::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -118,7 +119,7 @@ namespace bool BufferedStreamWindow::getRecord(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -157,7 +158,7 @@ namespace m_next->findUsedStreams(streams, expandAll); } - void BufferedStreamWindow::invalidateRecords(jrd_req* request) const + void BufferedStreamWindow::invalidateRecords(Request* request) const { m_next->invalidateRecords(request); } @@ -182,12 +183,15 @@ namespace // ------------------------------ -WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, +WindowedStream::WindowedStream(thread_db* tdbb, Optimizer* opt, ObjectsArray& windows, RecordSource* next) - : m_next(FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, next)), - m_joinedStream(NULL) + : m_joinedStream(nullptr) { + const auto csb = opt->getCompilerScratch(); + + m_next = FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, next); m_impure = csb->allocImpure(); + m_cardinality = next->getCardinality(); // Process the unpartioned and unordered map, if existent. @@ -236,7 +240,7 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, nullptr, window.map, window.frameExtent, window.exclusion); } - OPT_gen_aggregate_distincts(tdbb, csb, window.map); + opt->generateAggregateDistincts(window.map); } } @@ -317,22 +321,22 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, streams.clear(); m_joinedStream->findUsedStreams(streams); - SortedStream* sortedStream = OPT_gen_sort(tdbb, csb, streams, nullptr, - m_joinedStream, windowOrder, false, false); + const auto sortedStream = + opt->generateSort(streams, nullptr, m_joinedStream, windowOrder, false, false); m_joinedStream = FB_NEW_POOL(csb->csb_pool) WindowStream(tdbb, csb, window.stream, (window.group ? &window.group->expressions : nullptr), FB_NEW_POOL(csb->csb_pool) BufferedStream(csb, sortedStream), window.order, window.map, window.frameExtent, window.exclusion); - OPT_gen_aggregate_distincts(tdbb, csb, window.map); + opt->generateAggregateDistincts(window.map); } } } void WindowedStream::open(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); impure->irsb_flags = irsb_open; @@ -343,7 +347,7 @@ void WindowedStream::open(thread_db* tdbb) const void WindowedStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); invalidateRecords(request); @@ -361,7 +365,7 @@ bool WindowedStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (!(impure->irsb_flags & irsb_open)) @@ -394,7 +398,7 @@ void WindowedStream::markRecursive() m_joinedStream->markRecursive(); } -void WindowedStream::invalidateRecords(jrd_req* request) const +void WindowedStream::invalidateRecords(Request* request) const { m_joinedStream->invalidateRecords(request); } @@ -506,7 +510,7 @@ void WindowedStream::WindowStream::open(thread_db* tdbb) const { BaseAggWinStream::open(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = getImpure(request); impure->partitionBlock.startPosition = impure->partitionBlock.endPosition = @@ -530,7 +534,7 @@ void WindowedStream::WindowStream::open(thread_db* tdbb) const void WindowedStream::WindowStream::close(thread_db* tdbb) const { - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); Impure* const impure = request->getImpure(m_impure); if (impure->irsb_flags & irsb_open) @@ -543,7 +547,7 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const { JRD_reschedule(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); record_param* const rpb = &request->req_rpb[m_stream]; Impure* const impure = getImpure(request); @@ -871,7 +875,10 @@ void WindowedStream::WindowStream::print(thread_db* tdbb, string& plan, bool det unsigned level) const { if (detailed) + { plan += printIndent(++level) + "Window"; + printOptInfo(plan); + } m_next->print(tdbb, plan, detailed, level); } @@ -890,7 +897,7 @@ void WindowedStream::WindowStream::nullRecords(thread_db* tdbb) const m_next->nullRecords(tdbb); } -const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* request, +const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, Request* request, const Frame* frame, impure_value_ex* impureValue) const { dsc* desc = EVL_expr(tdbb, request, frame->value); @@ -925,7 +932,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* } } -SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, jrd_req* request, Impure* impure, +SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, Request* request, Impure* impure, const Frame* frame, const dsc* offsetDesc, SINT64 position) const { if (m_order->expressions.getCount() != 1) @@ -1041,7 +1048,7 @@ SINT64 WindowedStream::WindowStream::locateFrameRange(thread_db* tdbb, jrd_req* // ------------------------------ SlidingWindow::SlidingWindow(thread_db* aTdbb, const BaseBufferedStream* aStream, - jrd_req* request, + Request* request, FB_UINT64 aPartitionStart, FB_UINT64 aPartitionEnd, FB_UINT64 aFrameStart, FB_UINT64 aFrameEnd) : tdbb(aTdbb), // Note: instantiate the class only as local variable diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 2e00e9c488..1016ad7c8e 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -561,6 +561,7 @@ RELATION(nam_mon_statements, rel_mon_statements, ODS_11_1, rel_virtual) FIELD(f_mon_stmt_expl_plan, nam_mon_expl_plan, fld_source, 0, ODS_11_1) FIELD(f_mon_stmt_timeout, nam_stmt_timeout, fld_stmt_timeout, 0, ODS_13_0) FIELD(f_mon_stmt_timer, nam_stmt_timer, fld_stmt_timer, 0, ODS_13_0) + FIELD(f_mon_stmt_cmp_stmt_id, nam_mon_cmp_stmt_id, fld_stmt_id, 0, ODS_13_1) END_RELATION // Relation 37 (MON$CALL_STACK) @@ -575,6 +576,7 @@ RELATION(nam_mon_calls, rel_mon_calls, ODS_11_1, rel_virtual) FIELD(f_mon_call_src_column, nam_mon_src_column, fld_src_info, 0, ODS_11_1) FIELD(f_mon_call_stat_id, nam_mon_stat_id, fld_stat_id, 0, ODS_11_1) FIELD(f_mon_call_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, ODS_12_0) + FIELD(f_mon_call_cmp_stmt_id, nam_mon_cmp_stmt_id, fld_stmt_id, 0, ODS_13_1) END_RELATION // Relation 38 (MON$IO_STATS) @@ -741,3 +743,14 @@ RELATION(nam_keywords, rel_keywords, ODS_13_1, rel_virtual) FIELD(f_keyword_name, nam_keyword_name, fld_keyword_name, 0, ODS_13_1) FIELD(f_keyword_reserved, nam_keyword_reserved, fld_keyword_reserved, 0, ODS_13_1) END_RELATION + +// Relation 55 (MON$COMPILED_STATEMENTS) +RELATION(nam_mon_compiled_statements, rel_mon_compiled_statements, ODS_13_1, rel_virtual) + FIELD(f_mon_cmp_stmt_id, nam_mon_cmp_stmt_id, fld_stmt_id, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_sql_text, nam_mon_sql_text, fld_source, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_expl_plan, nam_mon_expl_plan, fld_source, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_name, nam_mon_obj_name, fld_gnr_name, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_type, nam_mon_obj_type, fld_obj_type, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, ODS_13_1) + FIELD(f_mon_cmp_stmt_stat_id, nam_mon_stat_id, fld_stat_id, 0, ODS_13_1) +END_RELATION diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index 93b3a687fa..03d2427cec 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -204,7 +204,7 @@ namespace class LocalThreadContext { public: - LocalThreadContext(thread_db* tdbb, jrd_tra* tra, jrd_req* req = NULL) + LocalThreadContext(thread_db* tdbb, jrd_tra* tra, Request* req = NULL) : m_tdbb(tdbb) { tdbb->setTransaction(tra); @@ -240,8 +240,8 @@ Applier* Applier::create(thread_db* tdbb) Jrd::ContextPoolHolder context(tdbb, req_pool); AutoPtr csb(FB_NEW_POOL(*req_pool) CompilerScratch(*req_pool)); - const auto request = JrdStatement::makeRequest(tdbb, csb, true); - TimeZoneUtil::validateGmtTimeStamp(request->req_gmt_timestamp); + const auto request = Statement::makeRequest(tdbb, csb, true); + request->validateTimeStamp(); request->req_attachment = attachment; auto& att_pool = *attachment->att_pool; diff --git a/src/jrd/replication/Applier.h b/src/jrd/replication/Applier.h index 2be2e54cc2..a0cd28cd76 100644 --- a/src/jrd/replication/Applier.h +++ b/src/jrd/replication/Applier.h @@ -125,7 +125,7 @@ namespace Jrd public: Applier(Firebird::MemoryPool& pool, const Firebird::PathName& database, - Jrd::jrd_req* request) + Jrd::Request* request) : PermanentStorage(pool), m_txnMap(pool), m_database(pool, database), m_request(request), m_bitmap(FB_NEW_POOL(pool) RecordBitmap(pool)), m_record(NULL) @@ -150,7 +150,7 @@ namespace Jrd private: TransactionMap m_txnMap; const Firebird::PathName m_database; - jrd_req* m_request; + Request* m_request; Firebird::AutoPtr m_bitmap; Record* m_record; JReplicator* m_interface; diff --git a/src/jrd/replication/Config.cpp b/src/jrd/replication/Config.cpp index e441d64896..d66464c1a1 100644 --- a/src/jrd/replication/Config.cpp +++ b/src/jrd/replication/Config.cpp @@ -384,7 +384,6 @@ void Config::enumerate(Firebird::Array& replicas) { config->sourceDirectory = value.c_str(); PathUtils::ensureSeparator(config->sourceDirectory); - checkAccess(config->sourceDirectory, key); } else if (key == "source_guid") { diff --git a/src/jrd/req.h b/src/jrd/req.h index e48137010d..c6c266e562 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -31,7 +31,7 @@ #include "../jrd/exe.h" #include "../jrd/sort.h" #include "../jrd/Attachment.h" -#include "../jrd/JrdStatement.h" +#include "../jrd/Statement.h" #include "../jrd/Record.h" #include "../jrd/RecordNumber.h" #include "../common/classes/timestamp.h" @@ -165,14 +165,111 @@ private: // request block -class jrd_req : public pool_alloc +class Request : public pool_alloc { +private: + class TimeStampCache + { + public: + void invalidate() + { + gmtTimeStamp.invalidate(); + } + + ISC_TIMESTAMP getLocalTimeStamp(USHORT currentTimeZone) const + { + fb_assert(!gmtTimeStamp.isEmpty()); + + if (timeZone != currentTimeZone) + update(currentTimeZone); + + return localTimeStamp; + } + + ISC_TIMESTAMP getGmtTimeStamp() const + { + fb_assert(!gmtTimeStamp.isEmpty()); + return gmtTimeStamp.value(); + } + + void setGmtTimeStamp(USHORT currentTimeZone, ISC_TIMESTAMP ts) + { + gmtTimeStamp = ts; + update(currentTimeZone); + } + + ISC_TIMESTAMP_TZ getTimeStampTz(USHORT currentTimeZone) const + { + fb_assert(!gmtTimeStamp.isEmpty()); + + ISC_TIMESTAMP_TZ timeStampTz; + timeStampTz.utc_timestamp = gmtTimeStamp.value(); + timeStampTz.time_zone = currentTimeZone; + return timeStampTz; + } + + ISC_TIME_TZ getTimeTz(USHORT currentTimeZone) const + { + fb_assert(!gmtTimeStamp.isEmpty()); + + ISC_TIME_TZ timeTz; + + if (timeZone != currentTimeZone) + update(currentTimeZone); + + if (localTimeValid) + { + timeTz.utc_time = localTime; + timeTz.time_zone = timeZone; + } + else + { + ISC_TIMESTAMP_TZ timeStamp; + timeStamp.utc_timestamp = gmtTimeStamp.value(); + timeStamp.time_zone = timeZone; + + timeTz = Firebird::TimeZoneUtil::timeStampTzToTimeTz(timeStamp); + + localTime = timeTz.utc_time; + localTimeValid = true; + } + + return timeTz; + } + + void validate(USHORT currentTimeZone) + { + if (gmtTimeStamp.isEmpty()) + { + Firebird::TimeZoneUtil::validateGmtTimeStamp(gmtTimeStamp); + update(currentTimeZone); + } + } + + private: + void update(USHORT currentTimeZone) const + { + localTimeStamp = Firebird::TimeZoneUtil::timeStampTzToTimeStamp( + getTimeStampTz(currentTimeZone), currentTimeZone); + timeZone = currentTimeZone; + localTimeValid = false; + } + + private: + Firebird::TimeStamp gmtTimeStamp; // Start time of request in GMT time zone + + // These are valid only when !gmtTimeStamp.isEmpty(), so no initialization is necessary. + mutable ISC_TIMESTAMP localTimeStamp; // Timestamp in timeZone's zone + mutable ISC_USHORT timeZone; // Timezone borrowed from the attachment when updated + mutable ISC_TIME localTime; // gmtTimeStamp converted to local time (WITH TZ) + mutable bool localTimeValid; // localTime calculation is expensive. So is it valid (calculated)? + }; + public: - jrd_req(Attachment* attachment, /*const*/ JrdStatement* aStatement, - Firebird::MemoryStats* parent_stats) + Request(Firebird::AutoMemoryPool& pool, Attachment* attachment, /*const*/ Statement* aStatement) : statement(aStatement), - req_pool(statement->pool), - req_memory_stats(parent_stats), + req_pool(pool), + req_memory_stats(&aStatement->pool->getStatsGroup()), req_blobs(req_pool), req_stats(*req_pool), req_base_stats(*req_pool), @@ -190,39 +287,55 @@ public: setAttachment(attachment); req_rpb = statement->rpbsSetup; impureArea.grow(statement->impureSize); + + pool->setStatsGroup(req_memory_stats); + pool.release(); } - JrdStatement* getStatement() + Statement* getStatement() { return statement; } - const JrdStatement* getStatement() const + const Statement* getStatement() const { return statement; } bool hasInternalStatement() const { - return statement->flags & JrdStatement::FLAG_INTERNAL; + return statement->flags & Statement::FLAG_INTERNAL; } bool hasPowerfulStatement() const { - return statement->flags & JrdStatement::FLAG_POWERFUL; + return statement->flags & Statement::FLAG_POWERFUL; } void setAttachment(Attachment* newAttachment) { req_attachment = newAttachment; - charSetId = statement->flags & JrdStatement::FLAG_INTERNAL ? - CS_METADATA : req_attachment->att_charset; + } + + bool isRoot() const + { + return statement->requests.hasData() && this == statement->requests[0]; + } + + bool isRequestIdUnassigned() const + { + return req_id == 0; } StmtNumber getRequestId() const { if (!req_id) - req_id = JRD_get_thread_data()->getDatabase()->generateStatementId(); + { + req_id = isRoot() ? + statement->getStatementId() : + JRD_get_thread_data()->getDatabase()->generateStatementId(); + } + return req_id; } @@ -232,22 +345,23 @@ public: } private: - JrdStatement* const statement; + Statement* const statement; mutable StmtNumber req_id; // request identifier + TimeStampCache req_timeStampCache; // time stamp cache public: MemoryPool* req_pool; + Firebird::MemoryStats req_memory_stats; Attachment* req_attachment; // database attachment USHORT req_incarnation; // incarnation number - Firebird::MemoryStats req_memory_stats; // Transaction pointer and doubly linked list pointers for requests in this // transaction. Maintained by TRA_attach_request/TRA_detach_request. jrd_tra* req_transaction; - jrd_req* req_tra_next; - jrd_req* req_tra_prev; + Request* req_tra_next; + Request* req_tra_prev; - jrd_req* req_caller; // Caller of this request + Request* req_caller; // Caller of this request // This field may be used to reconstruct the whole call stack TempBlobIdTree req_blobs; // Temporary BLOBs owned by this request const StmtNode* req_message; // Current message for send/receive @@ -268,14 +382,13 @@ public: ULONG req_flags; // misc request flags Savepoint* req_savepoints; // Looper savepoint list Savepoint* req_proc_sav_point; // procedure savepoint list - Firebird::TimeStamp req_gmt_timestamp; // Start time of request in GMT time zone - unsigned int req_timeout; // query timeout in milliseconds, set by the dsql_req::setupTimer - Firebird::RefPtr req_timer; // timeout timer, shared with dsql_req + unsigned int req_timeout; // query timeout in milliseconds, set by the DsqlRequest::setupTimer + Firebird::RefPtr req_timer; // timeout timer, shared with DsqlRequest Firebird::AutoPtr req_fetch_baseline; // State of request performance counters when we reported it last time SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time SINT64 req_fetch_rowcount; // Total number of rows returned by this request - jrd_req* req_proc_caller; // Procedure's caller request + Request* req_proc_caller; // Procedure's caller request const ValueListNode* req_proc_inputs; // and its node with input parameters TraNumber req_conflict_txn; // Transaction number for update conflict in read consistency mode @@ -286,13 +399,12 @@ public: SortOwner req_sorts; Firebird::Array req_rpb; // record parameter blocks Firebird::Array impureArea; // impure area - USHORT charSetId; // "client" character set of the request TriggerAction req_trigger_action; // action that caused trigger to fire // Fields to support read consistency in READ COMMITTED transactions struct snapshot_data { - jrd_req* m_owner; + Request* m_owner; SnapshotHandle m_handle; CommitNumber m_number; @@ -369,21 +481,39 @@ public: return tmp.m_transaction; } - Firebird::TimeStamp getLocalTimeStamp() const + void invalidateTimeStamp() { - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = req_gmt_timestamp.value(); - timeStampTz.time_zone = Firebird::TimeZoneUtil::GMT_ZONE; + req_timeStampCache.invalidate(); + } - return Firebird::TimeZoneUtil::timeStampTzToTimeStamp(timeStampTz, req_attachment->att_current_timezone); + ISC_TIMESTAMP getLocalTimeStamp() const + { + return req_timeStampCache.getLocalTimeStamp(req_attachment->att_current_timezone); + } + + ISC_TIMESTAMP getGmtTimeStamp() const + { + return req_timeStampCache.getGmtTimeStamp(); + } + + void setGmtTimeStamp(ISC_TIMESTAMP ts) + { + req_timeStampCache.setGmtTimeStamp(req_attachment->att_current_timezone, ts); } ISC_TIMESTAMP_TZ getTimeStampTz() const { - ISC_TIMESTAMP_TZ timeStampTz; - timeStampTz.utc_timestamp = req_gmt_timestamp.value(); - timeStampTz.time_zone = req_attachment->att_current_timezone; - return timeStampTz; + return req_timeStampCache.getTimeStampTz(req_attachment->att_current_timezone); + } + + ISC_TIME_TZ getTimeTz() const + { + return req_timeStampCache.getTimeTz(req_attachment->att_current_timezone); + } + + void validateTimeStamp() + { + req_timeStampCache.validate(req_attachment->att_current_timezone); } }; diff --git a/src/jrd/rse.h b/src/jrd/rse.h deleted file mode 100644 index c3f967dcd5..0000000000 --- a/src/jrd/rse.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: rse.h - * DESCRIPTION: Record source block definitions - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - * - * 2001.07.28: Added rsb_t.rsb_skip to support LIMIT functionality. - */ - -#ifndef JRD_RSE_H -#define JRD_RSE_H - -#include "../include/fb_blk.h" -#include "../common/classes/array.h" -#include "../jrd/constants.h" -#include "../common/dsc.h" -#include "../jrd/lls.h" -#include "../jrd/sbm.h" -#include "../jrd/ExtEngineManager.h" -#include "../jrd/RecordBuffer.h" -#include "../dsql/Nodes.h" - -struct dsc; - -namespace Jrd { - -struct sort_key_def; -class CompilerScratch; -class BoolExprNode; - -// Array which stores relative pointers to impure areas of invariant nodes -typedef Firebird::SortedArray VarInvariantArray; - -class RseNode; - -// General optimizer block - -class OptimizerBlk : public pool_alloc -{ -public: - OptimizerBlk(MemoryPool* pool, RseNode* aRse) - : opt_pool(pool), - opt_conjuncts(*pool), - opt_streams(*pool), - rse(aRse), - outerStreams(*pool), - subStreams(*pool), - compileStreams(*pool), - beds(*pool), - localStreams(*pool), - keyStreams(*pool) - {} - -private: - Firebird::MemoryPool* opt_pool; - -public: - CompilerScratch* opt_csb; // compiler scratch block - double opt_best_cost; // cost of best join order - StreamType opt_best_count; // longest length of indexable streams - USHORT opt_base_conjuncts; // number of conjuncts in our rse, next conjuncts are distributed parent - USHORT opt_base_parent_conjuncts; // number of conjuncts in our rse + distributed with parent, next are parent - USHORT opt_base_missing_conjuncts; // number of conjuncts in our and parent rse, but without missing - - struct opt_conjunct - { - // Conjunctions and their options - BoolExprNode* opt_conjunct_node; // conjunction - UCHAR opt_conjunct_flags; - }; - - struct opt_stream - { - // Streams and their options - StreamType opt_best_stream; // stream in best join order seen so far - StreamType opt_stream_number; // stream in position of join order - }; - - Firebird::MemoryPool& getPool() - { - return *opt_pool; - } - - Firebird::HalfStaticArray opt_conjuncts; - Firebird::HalfStaticArray opt_streams; - - RseNode* const rse; - StreamList outerStreams, subStreams; - StreamList compileStreams, beds, localStreams, keyStreams; - bool favorFirstRows; -}; - -// values for opt_conjunct_flags - -const USHORT opt_conjunct_used = 1; // conjunct is used -const USHORT opt_conjunct_matched = 2; // conjunct matches an index segment - -} //namespace Jrd - -#endif // JRD_RSE_H diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 361589ba99..b91f6ce2d1 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -75,10 +75,10 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClas static SecurityClass::flags_t get_sys_privileges(thread_db* tdbb); static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl&, const MetaName&, SLONG, const MetaName&); -static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, const MetaName& name, +static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, ObjectType type, const MetaName& name, const MetaName& r_name, const MetaName &invoker); static bool check_object(thread_db* tdbb, bool found, const SecurityClass* s_class, SLONG obj_type, - const MetaName& obj_name, SecurityClass::flags_t mask, SLONG type, const MetaName& name); + const MetaName& obj_name, SecurityClass::flags_t mask, ObjectType type, const MetaName& name); namespace @@ -105,79 +105,10 @@ namespace { SCL_create, priv_create, "CREATE" }, { 0, 0, "" } }; - - - // Database is never requested through CMP_post_access. - const char* const object_str_database = "DATABASE"; - //const char* const object_str_schema = "SCHEMA"; - const char* const object_str_table = "TABLE"; - const char* const object_str_package = "PACKAGE"; - const char* const object_str_procedure = "PROCEDURE"; - const char* const object_str_function = "FUNCTION"; - const char* const object_str_column = "COLUMN"; - const char* const object_str_charset = "CHARACTER SET"; - const char* const object_str_collation = "COLLATION"; - const char* const object_str_domain = "DOMAIN"; - const char* const object_str_exception = "EXCEPTION"; - const char* const object_str_generator = "GENERATOR"; - const char* const object_str_view = "VIEW"; - const char* const object_str_role = "ROLE"; - const char* const object_str_filter = "FILTER"; - - - struct SecObjectNamePriority - { - const char* name; - SLONG num; - }; - - const SecObjectNamePriority priorities[] = - { - {object_str_database, SCL_object_database}, - //{object_str_schema, SCL_object_schema}, - {object_str_table, SCL_object_table}, - {object_str_package, SCL_object_package}, - {object_str_procedure, SCL_object_procedure}, - {object_str_function, SCL_object_function}, - {object_str_column, SCL_object_column}, - {object_str_charset, SCL_object_charset}, - {object_str_collation, SCL_object_collation}, - {object_str_domain, SCL_object_domain}, - {object_str_exception, SCL_object_exception}, - {object_str_generator, SCL_object_generator}, - {object_str_view, SCL_object_view}, - {object_str_role, SCL_object_role}, - {object_str_filter, SCL_object_filter}, - {"", 0} - }; - - /* Unused, may be needed - SLONG accTypeStrToNum(const char* name) - { - for (const SecObjectNamePriority* p = priorities; p->num != 0; ++p) - { - if (strcmp(p->name, name) == 0) - return p->num; - } - fb_assert(false); - return 0; - } - */ - - const char* accTypeNumToStr(const SLONG num) - { - for (const SecObjectNamePriority* p = priorities; p->num != 0; ++p) - { - if (p->num == num) - return p->name; - } - fb_assert(false); - return ""; - } } // anonymous namespace -static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, const MetaName& name, +static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, ObjectType type, const MetaName& name, const MetaName& r_name, const MetaName& invoker) { const P_NAMES* names; @@ -187,13 +118,13 @@ static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, break; } - const char* const typeAsStr = accTypeNumToStr(type); + const char* const ddlObjectName = getDdlObjectName(type); const Firebird::string fullName = r_name.hasData() ? r_name.c_str() + Firebird::string(".") + name.c_str() : name.c_str(); Arg::StatusVector status; status << Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(typeAsStr) << + Arg::Str(ddlObjectName) << Arg::Str(fullName); if (invoker.hasData()) { @@ -207,10 +138,10 @@ static void raiseError(thread_db* tdbb, SecurityClass::flags_t mask, SLONG type, void SCL_check_access(thread_db* tdbb, const SecurityClass* s_class, - SLONG obj_type, + SLONG obj_id, const MetaName& obj_name, SecurityClass::flags_t mask, - SLONG type, + ObjectType type, bool recursive, const MetaName& name, const MetaName& r_name) @@ -252,13 +183,13 @@ void SCL_check_access(thread_db* tdbb, return; // Check global DDL permissions with ANY option which allow user to make changes non owned objects - if ((type > obj_last_non_ddl) && (mask & SCL_get_object_mask(type))) + if (isDdlObject(type) && (mask & SCL_get_object_mask(type))) return; if (!s_class || (mask & s_class->scl_flags) ) return; - if (obj_name.hasData() && (compute_access(tdbb, s_class, obj_type, obj_name) & mask) ) + if (obj_name.hasData() && (compute_access(tdbb, s_class, obj_id, obj_name) & mask) ) { return; } @@ -266,8 +197,8 @@ void SCL_check_access(thread_db* tdbb, // Allow recursive procedure/function call if (recursive && - ((type == SCL_object_procedure && obj_type == id_procedure) || - (type == SCL_object_function && obj_type == id_function)) && + ((type == obj_procedures && obj_id == id_procedure) || + (type == obj_functions && obj_id == id_function)) && obj_name == name) { return; @@ -277,7 +208,7 @@ void SCL_check_access(thread_db* tdbb, } -void SCL_check_create_access(thread_db* tdbb, int type) +void SCL_check_create_access(thread_db* tdbb, ObjectType type) { /************************************** * @@ -305,7 +236,7 @@ void SCL_check_create_access(thread_db* tdbb, int type) if (!(obj_mask & SCL_create)) { - const char* name = accTypeNumToStr(type); + const char* name = getDdlObjectName(type); ERR_post(Arg::Gds(isc_dyn_no_create_priv) << name); } } @@ -338,7 +269,7 @@ void SCL_check_charset(thread_db* tdbb, const MetaName& name, SecurityClass::fla } END_FOR - SCL_check_access(tdbb, s_class, 0, name, mask, SCL_object_charset, false, name); + SCL_check_access(tdbb, s_class, 0, name, mask, obj_charsets, false, name); } @@ -369,7 +300,7 @@ void SCL_check_collation(thread_db* tdbb, const MetaName& name, SecurityClass::f } END_FOR - SCL_check_access(tdbb, s_class, 0, name, mask, SCL_object_collation, false, name); + SCL_check_access(tdbb, s_class, 0, name, mask, obj_collations, false, name); } @@ -406,7 +337,7 @@ void SCL_check_database(thread_db* tdbb, SecurityClass::flags_t mask) } ERR_post(Arg::Gds(isc_no_priv) << Arg::Str(names->p_names_string) << - Arg::Str(object_str_database) << + Arg::Str(getDdlObjectName(obj_database)) << Arg::Str("")); } @@ -438,7 +369,7 @@ void SCL_check_domain(thread_db* tdbb, const MetaName& name, SecurityClass::flag } END_FOR - SCL_check_access(tdbb, s_class, 0, name, mask, SCL_object_domain, false, name); + SCL_check_access(tdbb, s_class, 0, name, mask, obj_domains, false, name); } @@ -471,7 +402,7 @@ bool SCL_check_exception(thread_db* tdbb, const MetaName& name, SecurityClass::f } END_FOR - return check_object(tdbb, found, s_class, 0, name, mask, SCL_object_exception, name); + return check_object(tdbb, found, s_class, 0, name, mask, obj_exceptions, name); } @@ -504,7 +435,7 @@ bool SCL_check_generator(thread_db* tdbb, const MetaName& name, SecurityClass::f } END_FOR - return check_object(tdbb, found, s_class, 0, name, mask, SCL_object_generator, name); + return check_object(tdbb, found, s_class, 0, name, mask, obj_generators, name); } @@ -590,7 +521,7 @@ void SCL_check_index(thread_db* tdbb, const MetaName& index_name, UCHAR index_id { // Someone is going to reference system table in FK // Usually it's not good idea - raiseError(tdbb, mask, SCL_object_table, reln_name, "", NULL); + raiseError(tdbb, mask, obj_relations, reln_name, "", NULL); } // Check if the relation exists. It may not have been created yet. @@ -599,7 +530,7 @@ void SCL_check_index(thread_db* tdbb, const MetaName& index_name, UCHAR index_id if (reln_name.isEmpty()) return; - SCL_check_access(tdbb, s_class, 0, NULL, mask, SCL_object_table, false, reln_name); + SCL_check_access(tdbb, s_class, 0, NULL, mask, obj_relations, false, reln_name); request.reset(); @@ -620,7 +551,7 @@ void SCL_check_index(thread_db* tdbb, const MetaName& index_name, UCHAR index_id s_class = (!RF.RDB$SECURITY_CLASS.NULL) ? SCL_get_class(tdbb, RF.RDB$SECURITY_CLASS) : default_s_class; SCL_check_access(tdbb, s_class, 0, NULL, mask, - SCL_object_column, false, RF.RDB$FIELD_NAME, reln_name); + obj_column, false, RF.RDB$FIELD_NAME, reln_name); } END_FOR } @@ -663,7 +594,7 @@ bool SCL_check_package(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flag } END_FOR - return check_object(tdbb, found, s_class, id_package, name, mask, SCL_object_package, name); + return check_object(tdbb, found, s_class, id_package, name, mask, obj_packages, name); } @@ -705,7 +636,7 @@ bool SCL_check_procedure(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fl } END_FOR - return check_object(tdbb, found, s_class, id_procedure, name, mask, SCL_object_procedure, name); + return check_object(tdbb, found, s_class, id_procedure, name, mask, obj_procedures, name); } @@ -747,7 +678,7 @@ bool SCL_check_function(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla } END_FOR - return check_object(tdbb, found, s_class, id_function, name, mask, SCL_object_function, name); + return check_object(tdbb, found, s_class, id_function, name, mask, obj_functions, name); } @@ -781,7 +712,7 @@ void SCL_check_filter(thread_db* tdbb, const MetaName &name, SecurityClass::flag } END_FOR - SCL_check_access(tdbb, s_class, id_filter, name, mask, SCL_object_filter, false, name); + SCL_check_access(tdbb, s_class, id_filter, name, mask, obj_filters, false, name); } @@ -819,7 +750,7 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla { // Someone is going to modify system table layout // Usually it's not good idea - raiseError(tdbb, mask, SCL_object_table, name, "", NULL); + raiseError(tdbb, mask, obj_relations, name, "", NULL); } if (!REL.RDB$SECURITY_CLASS.NULL) @@ -827,7 +758,7 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla } END_FOR - SCL_check_access(tdbb, s_class, 0, NULL, mask, SCL_object_table, false, name); + SCL_check_access(tdbb, s_class, 0, NULL, mask, obj_relations, false, name); } bool SCL_check_view(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t mask) @@ -866,7 +797,7 @@ bool SCL_check_view(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t } END_FOR - return check_object(tdbb, found, s_class, 0, NULL, mask, SCL_object_view, name); + return check_object(tdbb, found, s_class, 0, NULL, mask, obj_views, name); } void SCL_check_role(thread_db* tdbb, const MetaName& name, SecurityClass::flags_t mask) @@ -896,7 +827,7 @@ void SCL_check_role(thread_db* tdbb, const MetaName& name, SecurityClass::flags_ } END_FOR - SCL_check_access(tdbb, s_class, 0, NULL, mask, SCL_object_role, false, name); + SCL_check_access(tdbb, s_class, 0, NULL, mask, obj_roles, false, name); } SecurityClass* SCL_get_class(thread_db* tdbb, const TEXT* par_string) @@ -1055,44 +986,59 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) void UserId::findGrantedRoles(thread_db* tdbb) const { - SET_TDBB(tdbb); - Jrd::Attachment* const attachment = tdbb->getAttachment(); - - PreparedStatement::Builder sql; - MetaName usr_get_role; - string usr_get_priv; - sql << "with recursive role_tree as ( " - << " select rdb$relation_name as nm, 0 as ur from rdb$user_privileges " - << " where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user = " << usr_user_name << " and rdb$user_type = 8 " - << " union all " - << " select rdb$role_name as nm, 1 as ur from rdb$roles " - << " where rdb$role_name = " << usr_sql_role_name - << " union all " - << " select p.rdb$relation_name as nm, t.ur from rdb$user_privileges p " - << " join role_tree t on t.nm = p.rdb$user " - << " where p.rdb$privilege = 'M' and (p.rdb$field_name = 'D' or t.ur = 1)) " - << "select " << sql("r.rdb$role_name, ", usr_get_role) - << sql("r.rdb$system_privileges ", usr_get_priv) - << " from role_tree t join rdb$roles r on t.nm = r.rdb$role_name "; - - AutoPreparedStatement stmt(attachment->prepareStatement(tdbb, attachment->getSysTransaction(), sql)); - AutoResultSet rs(stmt->executeQuery(tdbb, attachment->getSysTransaction())); - - usr_granted_roles.clear(); - usr_privileges.clearAll(); - - while (rs->fetch(tdbb)) + try { - if (!usr_granted_roles.exist(usr_get_role)) // SQL request can return duplicates + SET_TDBB(tdbb); + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + PreparedStatement::Builder sql; + MetaName usr_get_role; + string usr_get_priv; + sql << "with recursive role_tree as ( " + << " select rdb$relation_name as nm, 0 as ur from rdb$user_privileges " + << " where rdb$privilege = 'M' and rdb$field_name = 'D' and rdb$user = " << usr_user_name << " and rdb$user_type = 8 " + << " union all " + << " select rdb$role_name as nm, 1 as ur from rdb$roles " + << " where rdb$role_name = " << usr_sql_role_name + << " union all " + << " select p.rdb$relation_name as nm, t.ur from rdb$user_privileges p " + << " join role_tree t on t.nm = p.rdb$user " + << " where p.rdb$privilege = 'M' and (p.rdb$field_name = 'D' or t.ur = 1)) " + << "select " << sql("r.rdb$role_name, ", usr_get_role) + << sql("r.rdb$system_privileges ", usr_get_priv) + << " from role_tree t join rdb$roles r on t.nm = r.rdb$role_name "; + + AutoPreparedStatement stmt(attachment->prepareStatement(tdbb, attachment->getSysTransaction(), sql)); + AutoResultSet rs(stmt->executeQuery(tdbb, attachment->getSysTransaction())); + + usr_granted_roles.clear(); + usr_privileges.clearAll(); + + while (rs->fetch(tdbb)) { - usr_granted_roles.add(usr_get_role); - Privileges p; - p.load(usr_get_priv.c_str()); - usr_privileges |= p; + if (!usr_granted_roles.exist(usr_get_role)) // SQL request can return duplicates + { + usr_granted_roles.add(usr_get_role); + Privileges p; + p.load(usr_get_priv.c_str()); + usr_privileges |= p; + } } } + catch (const Exception& e) + { + if (!(usr_flags & USR_sysdba)) + throw; - usr_flags &= ~USR_newrole; + e.stuffException(tdbb->tdbb_status_vector); + if (!fb_utils::containsErrorCode(tdbb->tdbb_status_vector->getErrors(), isc_collation_not_installed)) + throw; + + usr_privileges.setAll(); + tdbb->tdbb_status_vector->init(); + } + + usr_flags &= ~(USR_newrole | USR_sysdba); } @@ -1104,7 +1050,7 @@ void UserId::setRoleTrusted() } -void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) +void UserId::sclInit(thread_db* tdbb, bool create) { /************************************** * @@ -1123,13 +1069,13 @@ void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); - const TEXT* sql_role = tempId.getSqlRole().nullStr(); + const TEXT* sql_role = getSqlRole().nullStr(); // CVC: We'll verify the role and wipe it out when it doesn't exist - if (tempId.getUserName().hasData() && !create) + if (getUserName().hasData() && !create) { - const TEXT* login_name = tempId.getUserName().c_str(); + const TEXT* login_name = getUserName().c_str(); AutoCacheRequest request(tdbb, irq_get_role_name, IRQ_REQUESTS); @@ -1146,17 +1092,17 @@ void UserId::sclInit(thread_db* tdbb, bool create, const UserId& tempId) if (!create && sql_role && *sql_role) { - if (!SCL_role_granted(tdbb, tempId, sql_role)) + if (!SCL_role_granted(tdbb, *this, sql_role)) sql_role = NULL; } if (!sql_role) - sql_role = tempId.getTrustedRole().nullStr(); + sql_role = getTrustedRole().nullStr(); MetaString role_name(sql_role ? sql_role : NULL_ROLE); MemoryPool& pool = *attachment->att_pool; - UserId* const user = FB_NEW_POOL(pool) UserId(pool, tempId); + UserId* const user = FB_NEW_POOL(pool) UserId(pool, *this); user->setSqlRole(role_name); user->usr_init_role = role_name; attachment->att_user = user; @@ -1292,7 +1238,7 @@ void SCL_release_all(SecurityClassList*& list) } -SecurityClass::flags_t SCL_get_object_mask(const int object_type) +SecurityClass::flags_t SCL_get_object_mask(ObjectType object_type) { /************************************** * @@ -1307,7 +1253,7 @@ SecurityClass::flags_t SCL_get_object_mask(const int object_type) thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); - const TEXT* object_name = get_object_name(object_type); + const TEXT* object_name = getSecurityClassName(object_type); if (!*object_name) return 0; @@ -1776,7 +1722,11 @@ void UserId::makeRoleName(Firebird::MetaString& role, const int dialect) // Invoke utility twice: first to strip quotes, next to uppercase if needed // For unquoted string nothing bad happens fb_utils::dpbItemUpper(role); - fb_utils::dpbItemUpper(role); + { + Firebird::string tmp(role); + tmp.upper(); + role = tmp; + } break; case SQL_DIALECT_V6_TRANSITION: @@ -1832,7 +1782,7 @@ static bool check_object(thread_db* tdbb, SLONG obj_type, const MetaName& obj_name, SecurityClass::flags_t mask, - SLONG type, + ObjectType type, const MetaName& name) { if (s_class) diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 191dae62ff..caaef2f4eb 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -94,8 +94,7 @@ const SecurityClass::flags_t SCL_MODIFY_ANY = SCL_create | SCL_alter | SCL_contr const USHORT USR_mapdown = 1; // Mapping failed when getting context const USHORT USR_newrole = 2; // usr_granted_roles array needs refresh - -const USHORT USR_external = USR_mapdown; +const USHORT USR_sysdba = 4; // User detected as SYSDBA class UserId { @@ -311,7 +310,7 @@ public: return usr_privileges.test(sp); } - static void sclInit(thread_db* tdbb, bool create, const UserId& tempId); + void sclInit(thread_db* tdbb, bool create); void setUserName(const Firebird::MetaString& userName) { @@ -367,6 +366,13 @@ public: return usr_granted_roles.exist(role); } + const auto& getGrantedRoles(thread_db* tdbb) const + { + if (testFlag(USR_newrole)) + findGrantedRoles(tdbb); + return usr_granted_roles; + } + void makeRoleName(const int dialect) { makeRoleName(usr_sql_role_name, dialect); @@ -380,7 +386,7 @@ public: void setFlag(USHORT mask) { - usr_flags |= (mask & USR_external); + usr_flags |= mask; } static void makeRoleName(Firebird::MetaString& role, const int dialect); @@ -389,24 +395,6 @@ private: void findGrantedRoles(thread_db* tdbb) const; }; -// These numbers are arbitrary and only used at run-time. Can be changed if necessary at any moment. -// We need to include here the new objects that accept ACLs. -const SLONG SCL_object_database = obj_database; -const SLONG SCL_object_table = obj_relations; -const SLONG SCL_object_package = obj_packages; -const SLONG SCL_object_procedure = obj_procedures; -const SLONG SCL_object_function = obj_functions; -const SLONG SCL_object_collation = obj_collations; -const SLONG SCL_object_exception = obj_exceptions; -const SLONG SCL_object_generator = obj_generators; -const SLONG SCL_object_charset = obj_charsets; -const SLONG SCL_object_domain = obj_domains; -const SLONG SCL_object_view = obj_views; -const SLONG SCL_object_role = obj_roles; -const SLONG SCL_object_filter = obj_filters; -// Please keep it with code more than other objects -// - relations and procedures should be sorted before columns. -const SLONG SCL_object_column = obj_type_MAX + 1; } //namespace Jrd diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index 9a9524df83..ab5fb5429d 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -35,9 +35,9 @@ struct dsc; void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, SLONG, const Jrd::MetaName&, - Jrd::SecurityClass::flags_t, SLONG type, bool recursive, const Jrd::MetaName&, + Jrd::SecurityClass::flags_t, ObjectType type, bool recursive, const Jrd::MetaName&, const Jrd::MetaName& = ""); -void SCL_check_create_access(Jrd::thread_db*, int type); +void SCL_check_create_access(Jrd::thread_db*, ObjectType type); void SCL_check_charset(Jrd::thread_db* tdbb, const Jrd::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_collation(Jrd::thread_db* tdbb, const Jrd::MetaName&, Jrd::SecurityClass::flags_t); void SCL_check_database(Jrd::thread_db* tdbb, Jrd::SecurityClass::flags_t mask); @@ -57,7 +57,7 @@ Jrd::SecurityClass::flags_t SCL_get_mask(Jrd::thread_db* tdbb, const TEXT*, cons void SCL_clear_classes(Jrd::thread_db*, const TEXT*); void SCL_release_all(Jrd::SecurityClassList*&); bool SCL_role_granted(Jrd::thread_db* tdbb, const Jrd::UserId& usr, const TEXT* sql_role); -Jrd::SecurityClass::flags_t SCL_get_object_mask(const int object_type); +Jrd::SecurityClass::flags_t SCL_get_object_mask(ObjectType object_type); ULONG SCL_get_number(const UCHAR*); USHORT SCL_convert_privilege(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, const Firebird::string& priv); diff --git a/src/jrd/shut.cpp b/src/jrd/shut.cpp index 1b18a3835c..6b06cd8dee 100644 --- a/src/jrd/shut.cpp +++ b/src/jrd/shut.cpp @@ -26,6 +26,7 @@ #include "../jrd/scl.h" #include "../jrd/nbak.h" #include "../jrd/ods.h" +#include "../jrd/Mapping.h" #include "../jrd/cch_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/err_proto.h" @@ -222,6 +223,9 @@ void SHUT_database(thread_db* tdbb, SSHORT flag, SSHORT delay, Sync* guard) check_backup_state(tdbb); } + // Clear old mapping cache data (if present) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + attachment->att_flags |= ATT_shutdown_manager; // Database is being shutdown. First notification gives shutdown type and delay in seconds. @@ -401,6 +405,9 @@ void SHUT_online(thread_db* tdbb, SSHORT flag, Sync* guard) check_backup_state(tdbb); } + // Clear old mapping cache data (if present) + Mapping::clearCache(dbb->dbb_filename.c_str(), Mapping::ALL_CACHE); + // Reset shutdown flag on database header page WIN window(HEADER_PAGE_NUMBER); diff --git a/src/jrd/sort.cpp b/src/jrd/sort.cpp index 923dcc8461..6916672485 100644 --- a/src/jrd/sort.cpp +++ b/src/jrd/sort.cpp @@ -38,7 +38,6 @@ #include "../common/TimeZoneUtil.h" #include "../common/gdsassert.h" #include "../jrd/req.h" -#include "../jrd/rse.h" #include "../jrd/val.h" #include "../jrd/err_proto.h" #include "../yvalve/gds_proto.h" diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 6a21b6032e..84c03981b9 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -1972,7 +1972,7 @@ THREAD_ENTRY_DECLARE Service::run(THREAD_ENTRY_PARAM arg) Thread::Handle thrHandle = svc->svc_thread; svc->started(); - svc->svc_sem_full.release(); + svc->unblockQueryGet(); svc->finish(SVC_finished); threadCollect->add(thrHandle); @@ -2292,7 +2292,7 @@ void Service::enqueue(const UCHAR* s, ULONG len) { if (checkForShutdown() || (svc_flags & SVC_detached)) { - svc_sem_full.release(); + unblockQueryGet(); return; } @@ -2304,13 +2304,13 @@ void Service::enqueue(const UCHAR* s, ULONG len) { if (flagFirst) { - svc_sem_full.release(); + unblockQueryGet(true); flagFirst = false; } svc_sem_empty.tryEnter(1, 0); if (checkForShutdown() || (svc_flags & SVC_detached)) { - svc_sem_full.release(); + unblockQueryGet(); return; } } @@ -2332,6 +2332,13 @@ void Service::enqueue(const UCHAR* s, ULONG len) s += cnt; len -= cnt; } + unblockQueryGet(); +} + + +void Service::unblockQueryGet(bool over) +{ + svc_output_overflow = over; svc_sem_full.release(); } @@ -2422,8 +2429,11 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US buffer[(*return_length)++] = ch; } - if (!(flags & GET_LINE)) + if (svc_output_overflow || !(flags & GET_LINE)) + { + svc_output_overflow = false; svc_stdout_head = head; + } } if (flags & GET_LINE) @@ -2522,7 +2532,7 @@ ULONG Service::getBytes(UCHAR* buffer, ULONG size) svc_stdin_size_requested = size; svc_stdin_buffer = buffer; // Wakeup Service::query() if it waits for data from service - svc_sem_full.release(); + unblockQueryGet(); } // Wait for data from client @@ -2562,7 +2572,7 @@ void Service::finish(USHORT flag) if (svc_flags & SVC_finished) { - svc_sem_full.release(); + unblockQueryGet(); } else { @@ -2897,7 +2907,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) spb.getString(s); bool inStr = false; - for (FB_SIZE_T i = 0; i < s.length(); ++i) + for (FB_SIZE_T i = 0; i < s.length(); ) { if (s[i] == SVC_TRMNTR) { diff --git a/src/jrd/svc.h b/src/jrd/svc.h index dd528101dc..3f2e1d8647 100644 --- a/src/jrd/svc.h +++ b/src/jrd/svc.h @@ -347,6 +347,9 @@ public: private: Firebird::Semaphore svc_sem_empty, svc_sem_full; + bool svc_output_overflow; + + void unblockQueryGet(bool over = false); class Validate { diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 580bc9c30b..702cd463f2 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -37,7 +37,6 @@ #include "../jrd/req.h" #include "../jrd/exe.h" #include "../jrd/extds/ExtDS.h" -#include "../jrd/rse.h" #include "../jrd/intl_classes.h" #include "../common/ThreadStart.h" #include "../jrd/TimeZone.h" @@ -116,10 +115,10 @@ static const UCHAR sweep_tpb[] = }; -jrd_req* TRA_get_prior_request(thread_db* tdbb) +Request* TRA_get_prior_request(thread_db* tdbb) { // See if there is any request right above us in the call stack - jrd_req* org_request; + Request* org_request; thread_db* jrd_ctx = tdbb; do { @@ -144,7 +143,7 @@ jrd_req* TRA_get_prior_request(thread_db* tdbb) return org_request; } -void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) +void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::Request* request) { // This function is called whenever request is started in a transaction. // Setup context to preserve read consistency in READ COMMITTED transactions. @@ -159,7 +158,7 @@ void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) return; // See if there is any request right above us in the call stack - jrd_req* org_request = TRA_get_prior_request(tdbb); + Request* org_request = TRA_get_prior_request(tdbb); if (org_request && org_request->req_transaction == transaction) { @@ -180,7 +179,7 @@ void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) } -void TRA_release_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) +void TRA_release_request_snapshot(Jrd::thread_db* tdbb, Jrd::Request* request) { // This function is called whenever request has completed processing // in a transaction (normally or abnormally) @@ -200,7 +199,7 @@ void TRA_release_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request) } -void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request) +void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::Request* request) { // When request finishes normally transaction reference is not cleared. // Then if afterwards request is restarted TRA_attach_request is called again. @@ -228,7 +227,7 @@ void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request) transaction->tra_requests = request; } -void TRA_detach_request(Jrd::jrd_req* request) +void TRA_detach_request(Jrd::Request* request) { if (!request->req_transaction) { @@ -1641,12 +1640,12 @@ int TRA_snapshot_state(thread_db* tdbb, const jrd_tra* trans, TraNumber number, if ((trans->tra_flags & TRA_read_consistency) && state == tra_committed) { // GC thread accesses data directly without any request - if (jrd_req* current_request = tdbb->getRequest()) + if (Request* current_request = tdbb->getRequest()) { // Notes: // 1) There is no request snapshot when we build expression index // 2) Disable read committed snapshot after we encountered update conflict - jrd_req* snapshot_request = current_request->req_snapshot.m_owner; + Request* snapshot_request = current_request->req_snapshot.m_owner; if (snapshot_request && !(snapshot_request->req_flags & req_update_conflict)) { if (stateCn > snapshot_request->req_snapshot.m_number) @@ -1690,7 +1689,11 @@ jrd_tra* TRA_start(thread_db* tdbb, ULONG flags, SSHORT lock_timeout, Jrd::jrd_t Database* const dbb = tdbb->getDatabase(); Jrd::Attachment* const attachment = tdbb->getAttachment(); - if (dbb->dbb_ast_flags & DBB_shut_tran) + // Starting new transactions should be allowed for threads which + // are running purge_attachment() because it's needed for + // ON DISCONNECT triggers + if (dbb->dbb_ast_flags & DBB_shut_tran && + attachment->att_purge_tid != Thread::getId()) { ERR_post(Arg::Gds(isc_shutinprog) << Arg::Str(attachment->att_filename)); } @@ -1743,7 +1746,11 @@ jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb, Jrd::jrd_t Database* dbb = tdbb->getDatabase(); Jrd::Attachment* attachment = tdbb->getAttachment(); - if (dbb->dbb_ast_flags & DBB_shut_tran) + // Starting new transactions should be allowed for threads which + // are running purge_attachment() because it's needed for + // ON DISCONNECT triggers + if (dbb->dbb_ast_flags & DBB_shut_tran && + attachment->att_purge_tid != Thread::getId()) { ERR_post(Arg::Gds(isc_shutinprog) << Arg::Str(attachment->att_filename)); } @@ -2528,15 +2535,15 @@ static void restart_requests(thread_db* tdbb, jrd_tra* trans) **************************************/ SET_TDBB(tdbb); - for (jrd_req** i = trans->tra_attachment->att_requests.begin(); + for (Request** i = trans->tra_attachment->att_requests.begin(); i != trans->tra_attachment->att_requests.end(); ++i) { - Array& requests = (*i)->getStatement()->requests; + Array& requests = (*i)->getStatement()->requests; - for (jrd_req** j = requests.begin(); j != requests.end(); ++j) + for (Request** j = requests.begin(); j != requests.end(); ++j) { - jrd_req* request = *j; + Request* request = *j; if (request && request->req_transaction) { @@ -4077,12 +4084,12 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool if (fld) { - SCL_check_access(tdbb, s_class, 0, 0, SCL_select, SCL_object_column, + SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_column, false, fld->fld_name, blb_relation->rel_name); } else { - SCL_check_access(tdbb, s_class, 0, 0, SCL_select, SCL_object_table, + SCL_check_access(tdbb, s_class, 0, 0, SCL_select, obj_relations, false, blb_relation->rel_name); } diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 19f265dcc7..1f705424b4 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -85,7 +85,7 @@ struct BlobIndex { ULONG bli_temp_id; bool bli_materialized; - jrd_req* bli_request; + Request* bli_request; union { bid bli_blob_id; // ID of materialized blob @@ -286,7 +286,7 @@ public: SSHORT tra_lock_timeout; // in seconds, -1 means infinite, 0 means NOWAIT ULONG tra_next_blob_id; // ID of the previous blob or array created in this transaction const ISC_TIMESTAMP_TZ tra_timestamp; // transaction start time - jrd_req* tra_requests; // Doubly linked list of requests active in this transaction + Request* tra_requests; // Doubly linked list of requests active in this transaction MonitoringSnapshot* tra_mon_snapshot; // Database state snapshot (for monitoring purposes) RuntimeStatistics tra_stats; Firebird::Array tra_open_cursors; diff --git a/src/jrd/tra_proto.h b/src/jrd/tra_proto.h index 8a0efbf58a..3c2d4b0ebb 100644 --- a/src/jrd/tra_proto.h +++ b/src/jrd/tra_proto.h @@ -61,11 +61,11 @@ int TRA_state(const UCHAR*, TraNumber oldest, TraNumber number); void TRA_sweep(Jrd::thread_db* tdbb); void TRA_update_counters(Jrd::thread_db*, Jrd::Database*); int TRA_wait(Jrd::thread_db* tdbb, Jrd::jrd_tra* trans, TraNumber number, Jrd::jrd_tra::wait_t wait); -void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request); -void TRA_detach_request(Jrd::jrd_req* request); -void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request); -void TRA_release_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request); -Jrd::jrd_req* TRA_get_prior_request(Jrd::thread_db*); +void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::Request* request); +void TRA_detach_request(Jrd::Request* request); +void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::Request* request); +void TRA_release_request_snapshot(Jrd::thread_db*, Jrd::Request* request); +Jrd::Request* TRA_get_prior_request(Jrd::thread_db*); void TRA_shutdown_sweep(); #endif // JRD_TRA_PROTO_H diff --git a/src/jrd/trace/TraceConfigStorage.h b/src/jrd/trace/TraceConfigStorage.h index 19c985c785..3d623158f8 100644 --- a/src/jrd/trace/TraceConfigStorage.h +++ b/src/jrd/trace/TraceConfigStorage.h @@ -44,7 +44,7 @@ namespace Jrd { Slots are sorted by session id. Slot for new session is always last slot. When session is removed its slot is marked as unused. - Unused slot could be reused: slot itself moved at last position in slots array, + Unused slot could be reused: slot itself moved at last position in slots array, higher slots are moved down on its former place, slot data is not moved. Slot is reused with best-fit algorithm. */ @@ -78,11 +78,11 @@ struct TraceCSHeader : public Firebird::MemoryHeader Slot slots[TRACE_STORAGE_MAX_SLOTS]; }; -static_assert(sizeof(TraceCSHeader) < TraceCSHeader::TRACE_STORAGE_MIN_SIZE, +static_assert(sizeof(TraceCSHeader) < TraceCSHeader::TRACE_STORAGE_MIN_SIZE, "TraceCSHeader not fits TRACE_STORAGE_MIN_SIZE"); -class ConfigStorage FB_FINAL : public Firebird::GlobalStorage, public Firebird::IpcObject, public Firebird::Reasons +class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::IpcObject, public Firebird::Reasons { public: enum GET_FLAGS {ALL, FLAGS, AUTH}; @@ -116,7 +116,7 @@ private: void checkAudit(); - class TouchFile FB_FINAL : + class TouchFile final : public Firebird::RefCntIface > { public: @@ -166,7 +166,7 @@ private: ULONG allocSlot(ULONG slotSize); void markDeleted(TraceCSHeader::Slot * slot); - // remove unused space between slots data + // remove unused space between slots data void compact(); bool validate(); diff --git a/src/jrd/trace/TraceDSQLHelpers.h b/src/jrd/trace/TraceDSQLHelpers.h index cb4a176975..7c1f110f71 100644 --- a/src/jrd/trace/TraceDSQLHelpers.h +++ b/src/jrd/trace/TraceDSQLHelpers.h @@ -28,6 +28,7 @@ #ifndef JRD_TRACE_DSQL_HELPERS_H #define JRD_TRACE_DSQL_HELPERS_H +#include "../../jrd/trace/TraceManager.h" #include "../../jrd/trace/TraceObjects.h" namespace Jrd { @@ -65,7 +66,7 @@ public: prepare(ITracePlugin::RESULT_FAILED); } - void setStatement(dsql_req* request) + void setStatement(DsqlRequest* request) { m_request = request; } @@ -100,7 +101,7 @@ private: bool m_need_trace; Attachment* m_attachment; jrd_tra* const m_transaction; - dsql_req* m_request; + DsqlRequest* m_request; SINT64 m_start_clock; FB_SIZE_T m_string_len; const TEXT* m_string; @@ -110,32 +111,31 @@ private: class TraceDSQLExecute { public: - TraceDSQLExecute(Attachment* attachment, dsql_req* request) : + TraceDSQLExecute(Attachment* attachment, DsqlRequest* dsqlRequest) : m_attachment(attachment), - m_request(request) + m_dsqlRequest(dsqlRequest) { - m_need_trace = m_request->req_traced && TraceManager::need_dsql_execute(m_attachment); + m_need_trace = m_dsqlRequest->req_traced && TraceManager::need_dsql_execute(m_attachment); if (!m_need_trace) return; { // scope - TraceSQLStatementImpl stmt(request, NULL); - TraceManager::event_dsql_execute(m_attachment, request->req_transaction, &stmt, true, + TraceSQLStatementImpl stmt(dsqlRequest, NULL); + TraceManager::event_dsql_execute(m_attachment, dsqlRequest->req_transaction, &stmt, true, ITracePlugin::RESULT_SUCCESS); } m_start_clock = fb_utils::query_performance_counter(); - m_request->req_fetch_elapsed = 0; - m_request->req_fetch_rowcount = 0; - fb_assert(!m_request->req_fetch_baseline); - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_elapsed = 0; + m_dsqlRequest->req_fetch_rowcount = 0; + fb_assert(!m_dsqlRequest->req_fetch_baseline); + m_dsqlRequest->req_fetch_baseline = NULL; - jrd_req* jrd_request = m_request->req_request; - if (jrd_request) + if (auto request = m_dsqlRequest->getRequest()) { MemoryPool* pool = MemoryPool::getContextPool(); - m_request->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, jrd_request->req_stats); + m_dsqlRequest->req_fetch_baseline = FB_NEW_POOL(*pool) RuntimeStatistics(*pool, request->req_stats); } } @@ -147,19 +147,19 @@ public: m_need_trace = false; if (have_cursor) { - m_request->req_fetch_elapsed = fb_utils::query_performance_counter() - m_start_clock; + m_dsqlRequest->req_fetch_elapsed = fb_utils::query_performance_counter() - m_start_clock; return; } - TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - m_request->req_request ? &m_request->req_request->req_stats : NULL, + TraceRuntimeStats stats(m_attachment, m_dsqlRequest->req_fetch_baseline, + m_dsqlRequest->getRequest() ? &m_dsqlRequest->getRequest()->req_stats : NULL, fb_utils::query_performance_counter() - m_start_clock, - m_request->req_fetch_rowcount); + m_dsqlRequest->req_fetch_rowcount); - TraceSQLStatementImpl stmt(m_request, stats.getPerf()); - TraceManager::event_dsql_execute(m_attachment, m_request->req_transaction, &stmt, false, result); + TraceSQLStatementImpl stmt(m_dsqlRequest, stats.getPerf()); + TraceManager::event_dsql_execute(m_attachment, m_dsqlRequest->req_transaction, &stmt, false, result); - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_baseline = NULL; } ~TraceDSQLExecute() @@ -170,23 +170,23 @@ public: private: bool m_need_trace; Attachment* const m_attachment; - dsql_req* const m_request; + DsqlRequest* const m_dsqlRequest; SINT64 m_start_clock; }; class TraceDSQLFetch { public: - TraceDSQLFetch(Attachment* attachment, dsql_req* request) : + TraceDSQLFetch(Attachment* attachment, DsqlRequest* request) : m_attachment(attachment), - m_request(request) + m_dsqlRequest(request) { - m_need_trace = m_request->req_traced && TraceManager::need_dsql_execute(m_attachment) && - m_request->req_request && (m_request->req_request->req_flags & req_active); + m_need_trace = m_dsqlRequest->req_traced && TraceManager::need_dsql_execute(m_attachment) && + m_dsqlRequest->getRequest() && (m_dsqlRequest->getRequest()->req_flags & req_active); if (!m_need_trace) { - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_baseline = NULL; return; } @@ -204,30 +204,30 @@ public: return; m_need_trace = false; - m_request->req_fetch_elapsed += fb_utils::query_performance_counter() - m_start_clock; + m_dsqlRequest->req_fetch_elapsed += fb_utils::query_performance_counter() - m_start_clock; if (!eof) { - m_request->req_fetch_rowcount++; + m_dsqlRequest->req_fetch_rowcount++; return; } - TraceRuntimeStats stats(m_attachment, m_request->req_fetch_baseline, - &m_request->req_request->req_stats, m_request->req_fetch_elapsed, - m_request->req_fetch_rowcount); + TraceRuntimeStats stats(m_attachment, m_dsqlRequest->req_fetch_baseline, + &m_dsqlRequest->getRequest()->req_stats, m_dsqlRequest->req_fetch_elapsed, + m_dsqlRequest->req_fetch_rowcount); - TraceSQLStatementImpl stmt(m_request, stats.getPerf()); + TraceSQLStatementImpl stmt(m_dsqlRequest, stats.getPerf()); - TraceManager::event_dsql_execute(m_attachment, m_request->req_transaction, + TraceManager::event_dsql_execute(m_attachment, m_dsqlRequest->req_transaction, &stmt, false, result); - m_request->req_fetch_elapsed = 0; - m_request->req_fetch_baseline = NULL; + m_dsqlRequest->req_fetch_elapsed = 0; + m_dsqlRequest->req_fetch_baseline = NULL; } private: bool m_need_trace; Attachment* const m_attachment; - dsql_req* const m_request; + DsqlRequest* const m_dsqlRequest; SINT64 m_start_clock; }; diff --git a/src/jrd/trace/TraceJrdHelpers.h b/src/jrd/trace/TraceJrdHelpers.h index eef6e7b16c..61a5589fe8 100644 --- a/src/jrd/trace/TraceJrdHelpers.h +++ b/src/jrd/trace/TraceJrdHelpers.h @@ -94,7 +94,7 @@ private: class TraceProcExecute { public: - TraceProcExecute(thread_db* tdbb, jrd_req* request, jrd_req* caller, const ValueListNode* inputs) : + TraceProcExecute(thread_db* tdbb, Request* request, Request* caller, const ValueListNode* inputs) : m_tdbb(tdbb), m_request(request) { @@ -161,14 +161,14 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; }; class TraceProcFetch { public: - TraceProcFetch(thread_db* tdbb, jrd_req* request) : + TraceProcFetch(thread_db* tdbb, Request* request) : m_tdbb(tdbb), m_request(request) { @@ -219,7 +219,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; }; @@ -227,7 +227,7 @@ private: class TraceFuncExecute { public: - TraceFuncExecute(thread_db* tdbb, jrd_req* request, jrd_req* caller, + TraceFuncExecute(thread_db* tdbb, Request* request, Request* caller, const UCHAR* inMsg, ULONG inMsgLength) : m_tdbb(tdbb), m_request(request), @@ -301,7 +301,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; const UCHAR* m_inMsg; ULONG m_inMsgLength; SINT64 m_start_clock; @@ -311,13 +311,13 @@ private: class TraceTrigExecute { public: - TraceTrigExecute(thread_db* tdbb, jrd_req* trigger, int which_trig) : + TraceTrigExecute(thread_db* tdbb, Request* trigger, int which_trig) : m_tdbb(tdbb), m_request(trigger), m_which_trig(which_trig) { TraceManager* trace_mgr = m_tdbb->getAttachment()->att_trace_manager; - m_need_trace = !(m_request->getStatement()->flags & JrdStatement::FLAG_SYS_TRIGGER) && + m_need_trace = !(m_request->getStatement()->flags & Statement::FLAG_SYS_TRIGGER) && trace_mgr->needs(ITraceFactory::TRACE_EVENT_TRIGGER_EXECUTE); if (!m_need_trace) @@ -367,7 +367,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; const int m_which_trig; }; @@ -392,7 +392,7 @@ public: m_start_clock = fb_utils::query_performance_counter(); } - void finish(jrd_req* request, ntrace_result_t result) + void finish(Statement* statement, ntrace_result_t result) { if (!m_need_trace) return; @@ -406,9 +406,9 @@ public: TraceConnectionImpl conn(m_tdbb->getAttachment()); TraceTransactionImpl tran(m_tdbb->getTransaction()); - if (request) + if (statement) { - TraceBLRStatementImpl stmt(request, NULL); + TraceBLRStatementImpl stmt(statement, NULL); trace_mgr->event_blr_compile(&conn, m_tdbb->getTransaction() ? &tran : NULL, &stmt, m_start_clock, result); } @@ -437,16 +437,16 @@ private: class TraceBlrExecute { public: - TraceBlrExecute(thread_db* tdbb, jrd_req* request) : + TraceBlrExecute(thread_db* tdbb, Request* request) : m_tdbb(tdbb), m_request(request) { Attachment* attachment = m_tdbb->getAttachment(); - JrdStatement* statement = m_request->getStatement(); + Statement* statement = m_request->getStatement(); m_need_trace = attachment->att_trace_manager->needs(ITraceFactory::TRACE_EVENT_BLR_EXECUTE) && !statement->sqlText && - !(statement->flags & JrdStatement::FLAG_INTERNAL) && + !(statement->flags & Statement::FLAG_INTERNAL) && !attachment->isUtility(); if (!m_need_trace) @@ -474,7 +474,7 @@ public: TraceConnectionImpl conn(m_tdbb->getAttachment()); TraceTransactionImpl tran(m_tdbb->getTransaction()); - TraceBLRStatementImpl stmt(m_request, stats.getPerf()); + TraceBLRStatementImpl stmt(m_request->getStatement(), stats.getPerf()); TraceManager* trace_mgr = m_tdbb->getAttachment()->att_trace_manager; trace_mgr->event_blr_execute(&conn, &tran, &stmt, result); @@ -490,7 +490,7 @@ public: private: bool m_need_trace; thread_db* const m_tdbb; - jrd_req* const m_request; + Request* const m_request; SINT64 m_start_clock; }; diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 9d8e6a5e9e..e25be60b04 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -222,6 +222,8 @@ void TraceManager::update_sessions() } // add new sessions + new_needs = trace_needs; + trace_needs = 0; while (newSessions.hasData()) { TraceSession* s = newSessions.pop(); @@ -234,6 +236,10 @@ void TraceManager::update_sessions() { trace_needs = 0; } + else + { + trace_needs = new_needs; + } } void TraceManager::update_session(const TraceSession& session) @@ -265,7 +271,7 @@ void TraceManager::update_session(const TraceSession& session) curr_user = attachment->getUserName().c_str(); if (session.ses_auth.hasData()) - { // scope + { AutoSetRestoreFlag autoRestore(&attachment->att_flags, ATT_mapping, true); Database* dbb = attachment->att_database; @@ -277,6 +283,8 @@ void TraceManager::update_session(const TraceSession& session) mapping.setSecurityDbAlias(dbb->dbb_config->getSecurityDatabase(), dbb->dbb_filename.c_str()); mapping.setDb(attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), attachment->getInterface()); + + EngineCheckout guard(attachment, FB_FUNCTION); mapResult = mapping.mapUser(s_user, t_role); } } @@ -344,7 +352,7 @@ void TraceManager::update_session(const TraceSession& session) sesInfo.ses_id = session.ses_id; trace_sessions.add(sesInfo); - trace_needs |= info->factory->trace_needs(); + new_needs |= info->factory->trace_needs(); } else if (status->getState() & IStatus::STATE_ERRORS) { diff --git a/src/jrd/trace/TraceManager.h b/src/jrd/trace/TraceManager.h index 0c581caa19..924ed7f18b 100644 --- a/src/jrd/trace/TraceManager.h +++ b/src/jrd/trace/TraceManager.h @@ -163,7 +163,7 @@ private: Attachment* attachment; Service* service; const char* filename; - NotificationNeeds trace_needs; + NotificationNeeds trace_needs, new_needs; // This structure should be POD-like to be stored in Array struct FactoryInfo diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 64ac68d778..93863a9acb 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -42,8 +42,8 @@ #include "../../jrd/evl_proto.h" #include "../../jrd/intl_proto.h" #include "../../jrd/mov_proto.h" -#include "../../jrd/opt_proto.h" #include "../../jrd/pag_proto.h" +#include "../../jrd/optimizer/Optimizer.h" #include "../../common/os/path_utils.h" #include "../../dsql/dsql_proto.h" @@ -169,21 +169,21 @@ ISC_INT64 TraceTransactionImpl::getInitialID() ISC_INT64 TraceSQLStatementImpl::getStmtID() { - if (m_stmt->req_request) - return m_stmt->req_request->getRequestId(); + if (m_stmt->getRequest()) + return m_stmt->getRequest()->getRequestId(); return 0; } const char* TraceSQLStatementImpl::getText() { - const string* stmtText = m_stmt->getStatement()->getSqlText(); + const string* stmtText = m_stmt->getDsqlStatement()->getSqlText(); return stmtText ? stmtText->c_str() : ""; } const char* TraceSQLStatementImpl::getTextUTF8() { - const string* stmtText = m_stmt->getStatement()->getSqlText(); + const string* stmtText = m_stmt->getDsqlStatement()->getSqlText(); if (m_textUTF8.isEmpty() && stmtText && !stmtText->isEmpty()) { @@ -211,8 +211,8 @@ void TraceSQLStatementImpl::fillPlan(bool explained) if (m_plan.isEmpty() || m_planExplained != explained) { m_planExplained = explained; - if (m_stmt->req_request) - m_plan = OPT_get_plan(JRD_get_thread_data(), m_stmt->req_request, m_planExplained); + if (m_stmt->getStatement()) + m_plan = Optimizer::getPlan(JRD_get_thread_data(), m_stmt->getStatement(), m_planExplained); } } @@ -231,9 +231,17 @@ ITraceParams* TraceSQLStatementImpl::getInputs() void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() { - if (m_descs.getCount() || !m_params) + if (m_descs.getCount() || !m_params || m_params->getCount() == 0) return; + if (!m_stmt->getDsqlStatement()->isDml()) + { + fb_assert(false); + return; + } + + const auto dmlRequest = (DsqlDmlRequest*) m_stmt; + USHORT first_index = 0; for (FB_SIZE_T i = 0 ; i < m_params->getCount(); ++i) { @@ -246,7 +254,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() if (parameter->par_null) { const UCHAR* msgBuffer = - m_stmt->req_msg_buffers[parameter->par_null->par_message->msg_buffer_number]; + dmlRequest->req_msg_buffers[parameter->par_null->par_message->msg_buffer_number]; if (*(SSHORT*) (msgBuffer + (IPTR) parameter->par_null->par_desc.dsc_address)) null_flag = DSC_null; @@ -263,7 +271,7 @@ void TraceSQLStatementImpl::DSQLParamsImpl::fillParams() *desc = parameter->par_desc; desc->dsc_flags |= null_flag; - UCHAR* msgBuffer = m_stmt->req_msg_buffers[parameter->par_message->msg_buffer_number]; + UCHAR* msgBuffer = dmlRequest->req_msg_buffers[parameter->par_message->msg_buffer_number]; desc->dsc_address = msgBuffer + (IPTR) desc->dsc_address; } } @@ -496,7 +504,7 @@ const char* TraceTriggerImpl::getRelationName() /// TraceLogWriterImpl -class TraceLogWriterImpl FB_FINAL : +class TraceLogWriterImpl final : public RefCntIface > { public: diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index 8b13bca77d..ed661a8a76 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -146,17 +146,17 @@ private: class TraceBLRStatementImpl : public BLRPrinter { public: - TraceBLRStatementImpl(const jrd_req* stmt, Firebird::PerformanceInfo* perf) : - BLRPrinter(stmt->getStatement()->blr.begin(), stmt->getStatement()->blr.getCount()), + TraceBLRStatementImpl(const Statement* stmt, Firebird::PerformanceInfo* perf) : + BLRPrinter(stmt->blr.begin(), stmt->blr.getCount()), m_stmt(stmt), m_perf(perf) {} - ISC_INT64 getStmtID() { return m_stmt->getRequestId(); } + ISC_INT64 getStmtID() { return m_stmt->getStatementId(); } Firebird::PerformanceInfo* getPerf() { return m_perf; } private: - const jrd_req* const m_stmt; + const Statement* const m_stmt; Firebird::PerformanceInfo* const m_perf; }; @@ -177,7 +177,7 @@ class TraceSQLStatementImpl : public Firebird::AutoIface > { public: - TraceSQLStatementImpl(const dsql_req* stmt, Firebird::PerformanceInfo* perf) : + TraceSQLStatementImpl(DsqlRequest* stmt, Firebird::PerformanceInfo* perf) : m_stmt(stmt), m_perf(perf), m_planExplained(false), @@ -198,12 +198,12 @@ private: public Firebird::AutoIface > { public: - DSQLParamsImpl(Firebird::MemoryPool& pool, const dsql_req* const stmt) : + DSQLParamsImpl(Firebird::MemoryPool& pool, DsqlRequest* const stmt) : m_stmt(stmt), m_params(NULL), m_descs(pool) { - const dsql_msg* msg = m_stmt->getStatement()->getSendMsg(); + const dsql_msg* msg = m_stmt->getDsqlStatement()->getSendMsg(); if (msg) m_params = &msg->msg_parameters; } @@ -215,7 +215,7 @@ private: private: void fillParams(); - const dsql_req* const m_stmt; + DsqlRequest* const m_stmt; const Firebird::Array* m_params; Firebird::HalfStaticArray m_descs; Firebird::string temp_utf8_text; @@ -223,7 +223,7 @@ private: void fillPlan(bool explained); - const dsql_req* const m_stmt; + DsqlRequest* const m_stmt; Firebird::PerformanceInfo* const m_perf; Firebird::string m_plan; bool m_planExplained; @@ -342,7 +342,7 @@ private: class TraceDscFromValues : public TraceDescriptors { public: - TraceDscFromValues(Firebird::MemoryPool& pool, jrd_req* request, const ValueListNode* params) : + TraceDscFromValues(Firebird::MemoryPool& pool, Request* request, const ValueListNode* params) : TraceDescriptors(pool), m_request(request), m_params(params) @@ -352,7 +352,7 @@ protected: void fillParams(); private: - jrd_req* m_request; + Request* m_request; const ValueListNode* m_params; }; @@ -402,7 +402,7 @@ class TraceProcedureImpl : public Firebird::AutoIface > { public: - TraceProcedureImpl(jrd_req* request, Firebird::PerformanceInfo* perf) : + TraceProcedureImpl(Request* request, Firebird::PerformanceInfo* perf) : m_request(request), m_perf(perf), m_inputs(*getDefaultMemoryPool(), request->req_proc_caller, request->req_proc_inputs), @@ -420,7 +420,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; }; private: - jrd_req* const m_request; + Request* const m_request; Firebird::PerformanceInfo* const m_perf; TraceDscFromValues m_inputs; Firebird::string m_name; @@ -431,7 +431,7 @@ class TraceFunctionImpl : public Firebird::AutoIface > { public: - TraceFunctionImpl(jrd_req* request, Firebird::ITraceParams* inputs, Firebird::PerformanceInfo* perf, const dsc* value) : + TraceFunctionImpl(Request* request, Firebird::ITraceParams* inputs, Firebird::PerformanceInfo* perf, const dsc* value) : m_request(request), m_perf(perf), m_inputs(inputs), @@ -450,7 +450,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; }; private: - jrd_req* const m_request; + Request* const m_request; Firebird::PerformanceInfo* const m_perf; Firebird::ITraceParams* m_inputs; TraceDscFromDsc m_value; @@ -462,7 +462,7 @@ class TraceTriggerImpl : public Firebird::AutoIface > { public: - TraceTriggerImpl(const jrd_req* trig, SSHORT which, Firebird::PerformanceInfo* perf) : + TraceTriggerImpl(const Request* trig, SSHORT which, Firebird::PerformanceInfo* perf) : m_trig(trig), m_which(which), m_perf(perf) @@ -476,7 +476,7 @@ public: Firebird::PerformanceInfo* getPerf() { return m_perf; } private: - const jrd_req* const m_trig; + const Request* const m_trig; const SSHORT m_which; Firebird::PerformanceInfo* const m_perf; }; diff --git a/src/jrd/val.h b/src/jrd/val.h index 91e024f0c5..e44b17a140 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -55,7 +55,7 @@ namespace Jrd { class ArrayField; class blb; -class jrd_req; +class Request; class jrd_tra; // Various structures in the impure area @@ -254,7 +254,7 @@ public: blb* arr_blob; // Blob for data access jrd_tra* arr_transaction; // Parent transaction block ArrayField* arr_next; // Next array in transaction - jrd_req* arr_request; // request + Request* arr_request; // request SLONG arr_effective_length; // Length of array instance USHORT arr_desc_length; // Length of array descriptor ULONG arr_temp_id; // Temporary ID for open array inside the transaction diff --git a/src/jrd/validation.cpp b/src/jrd/validation.cpp index c7a05417c4..f03551166b 100644 --- a/src/jrd/validation.cpp +++ b/src/jrd/validation.cpp @@ -550,7 +550,6 @@ VI. ADDITIONAL NOTES #include "../jrd/btr.h" #include "../jrd/btn.h" #include "../jrd/cch.h" -#include "../jrd/rse.h" #include "../jrd/tra.h" #include "../jrd/svc.h" #include "../jrd/btr_proto.h" @@ -725,6 +724,7 @@ static int validate(Firebird::UtilSvc* svc) { dpb.insertString(isc_dpb_trusted_auth, userName); } + dpb.insertTag(isc_dpb_no_garbage_collect); PathName expandedFilename; if (expandDatabaseName(dbName, expandedFilename, NULL)) diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 8af88effdc..5ecbdfc3f3 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -58,7 +58,6 @@ #include "../jrd/os/pio.h" #include "../jrd/btr.h" #include "../jrd/exe.h" -#include "../jrd/rse.h" #include "../jrd/scl.h" #include "../common/classes/alloc.h" #include "../common/ThreadStart.h" @@ -162,7 +161,7 @@ const int PREPARE_LOCKERR = 3; static int prepare_update(thread_db*, jrd_tra*, TraNumber commit_tid_read, record_param*, record_param*, record_param*, PageStack&, bool); -static void protect_system_table_insert(thread_db* tdbb, const jrd_req* req, const jrd_rel* relation, +static void protect_system_table_insert(thread_db* tdbb, const Request* req, const jrd_rel* relation, bool force_flag = false); static void protect_system_table_delupd(thread_db* tdbb, const jrd_rel* relation, const char* operation, bool force_flag = false); @@ -224,7 +223,7 @@ static bool assert_gc_enabled(const jrd_tra* transaction, const jrd_rel* relatio inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation, const char* op) { const Attachment* const attachment = tdbb->getAttachment(); - const jrd_req* const request = tdbb->getRequest(); + const Request* const request = tdbb->getRequest(); if (relation->isSystem() && attachment->isGbak() && !(attachment->att_flags & ATT_creator) && !request->hasInternalStatement()) @@ -238,7 +237,6 @@ inline void check_gbak_cheating_insupd(thread_db* tdbb, const jrd_rel* relation, inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation) { const Attachment* const attachment = tdbb->getAttachment(); - const jrd_tra* const transaction = tdbb->getTransaction(); if (relation->isSystem() && attachment->isGbak()) { @@ -1453,7 +1451,7 @@ void VIO_data(thread_db* tdbb, record_param* rpb, MemoryPool* pool) } -static bool check_prepare_result(int prepare_result, jrd_tra* transaction, jrd_req* request, record_param* rpb) +static bool check_prepare_result(int prepare_result, jrd_tra* transaction, Request* request, record_param* rpb) { /************************************** * @@ -1471,7 +1469,7 @@ static bool check_prepare_result(int prepare_result, jrd_tra* transaction, jrd_r if (prepare_result == PREPARE_OK) return true; - jrd_req* top_request = request->req_snapshot.m_owner; + Request* top_request = request->req_snapshot.m_owner; const bool restart_ready = top_request && (top_request->req_flags & req_restart_ready); @@ -1522,7 +1520,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) MetaName object_name, package_name; SET_TDBB(tdbb); - jrd_req* request = tdbb->getRequest(); + Request* request = tdbb->getRequest(); jrd_rel* relation = rpb->rpb_relation; #ifdef VIO_DEBUG @@ -2527,8 +2525,6 @@ bool VIO_get_current(thread_db* tdbb, **************************************/ SET_TDBB(tdbb); - Attachment* const attachment = tdbb->getAttachment(); - #ifdef VIO_DEBUG jrd_rel* relation = rpb->rpb_relation; VIO_trace(DEBUG_TRACE, @@ -3480,7 +3476,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) * **************************************/ SET_TDBB(tdbb); - jrd_req* const request = tdbb->getRequest(); + Request* const request = tdbb->getRequest(); jrd_rel* relation = rpb->rpb_relation; DeferredWork* work = NULL; @@ -3717,7 +3713,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_trg_rname, &desc); // check if this request go through without checking permissions - if (!(request->getStatement()->flags & (JrdStatement::FLAG_IGNORE_PERM | JrdStatement::FLAG_INTERNAL))) + if (!(request->getStatement()->flags & (Statement::FLAG_IGNORE_PERM | Statement::FLAG_INTERNAL))) SCL_check_relation(tdbb, &desc, SCL_control | SCL_alter); if (EVL_field(0, rpb->rpb_record, f_trg_rname, &desc2)) @@ -4085,7 +4081,7 @@ bool VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction) case PREPARE_DELETE: if ((transaction->tra_flags & TRA_read_consistency)) { - jrd_req* top_request = tdbb->getRequest()->req_snapshot.m_owner; + Request* top_request = tdbb->getRequest()->req_snapshot.m_owner; if (top_request && !(top_request->req_flags & req_update_conflict)) { if (!(top_request->req_flags & req_restart_ready)) @@ -5120,7 +5116,7 @@ static void invalidate_cursor_records(jrd_tra* transaction, record_param* mod_rp **************************************/ fb_assert(mod_rpb && mod_rpb->rpb_relation); - for (jrd_req* request = transaction->tra_requests; request; request = request->req_tra_next) + for (Request* request = transaction->tra_requests; request; request = request->req_tra_next) { if (request->req_flags & req_active) { @@ -5195,12 +5191,15 @@ static void list_staying_fast(thread_db* tdbb, record_param* rpb, RecordStack& s } } - const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; + ///const TraNumber oldest_active = tdbb->getTransaction()->tra_oldest_active; while (temp.rpb_b_page) { - ULONG page = temp.rpb_page = temp.rpb_b_page; - USHORT line = temp.rpb_line = temp.rpb_b_line; + ///ULONG page = temp.rpb_page = temp.rpb_b_page; + ///USHORT line = temp.rpb_line = temp.rpb_b_line; + temp.rpb_page = temp.rpb_b_page; + temp.rpb_line = temp.rpb_b_line; + temp.rpb_record = NULL; if (temp.rpb_flags & rpb_delta) @@ -5871,7 +5870,7 @@ static int prepare_update( thread_db* tdbb, static void protect_system_table_insert(thread_db* tdbb, - const jrd_req* request, + const Request* request, const jrd_rel* relation, bool force_flag) { @@ -5919,7 +5918,7 @@ static void protect_system_table_delupd(thread_db* tdbb, * **************************************/ const Attachment* const attachment = tdbb->getAttachment(); - const jrd_req* const request = tdbb->getRequest(); + const Request* const request = tdbb->getRequest(); if (!force_flag) { diff --git a/src/lock/print.cpp b/src/lock/print.cpp index dae655264f..fa1ba8f26d 100644 --- a/src/lock/print.cpp +++ b/src/lock/print.cpp @@ -140,7 +140,7 @@ using namespace Firebird; namespace { - class sh_mem FB_FINAL : public IpcObject + class sh_mem final : public IpcObject { public: explicit sh_mem(bool p_consistency, const char* filename) diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 0070254189..fb68430959 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=5 MinorVer=0 RevNo=0 -BuildNum=351 +BuildNum=477 NowAt=`pwd` cd `dirname $0` diff --git a/src/plugins/crypt/arc4/Arc4.cpp b/src/plugins/crypt/arc4/Arc4.cpp index 5319d6ff46..6f46de5310 100644 --- a/src/plugins/crypt/arc4/Arc4.cpp +++ b/src/plugins/crypt/arc4/Arc4.cpp @@ -85,7 +85,7 @@ private: namespace Crypt { -class Arc4 FB_FINAL : public StdPlugin > +class Arc4 final : public StdPlugin > { public: explicit Arc4(IPluginConfig*) diff --git a/src/plugins/crypt/chacha/ChaCha.cpp b/src/plugins/crypt/chacha/ChaCha.cpp index 33d9c0d4ec..02cb9c76f7 100644 --- a/src/plugins/crypt/chacha/ChaCha.cpp +++ b/src/plugins/crypt/chacha/ChaCha.cpp @@ -89,7 +89,7 @@ private: template -class ChaCha FB_FINAL : public StdPlugin, CheckStatusWrapper> > +class ChaCha final : public StdPlugin, CheckStatusWrapper> > { public: explicit ChaCha(IPluginConfig*) @@ -189,7 +189,7 @@ SimpleFactory > factory64; } // anonymous namespace -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { CachedMasterInterface::set(master); PluginManagerInterfacePtr()->registerPluginFactory(IPluginManager::TYPE_WIRE_CRYPT, "ChaCha", &factory); diff --git a/src/plugins/udr_engine/UdrEngine.cpp b/src/plugins/udr_engine/UdrEngine.cpp index 25a35cb5e0..e6549c35f2 100644 --- a/src/plugins/udr_engine/UdrEngine.cpp +++ b/src/plugins/udr_engine/UdrEngine.cpp @@ -718,7 +718,7 @@ class IExternalEngineFactoryImpl : public SimpleFactory { } factory; -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { CachedMasterInterface::set(master); diff --git a/src/remote/CMakeLists.txt b/src/remote/CMakeLists.txt index 49d6dac09b..8c2fdc6e1b 100644 --- a/src/remote/CMakeLists.txt +++ b/src/remote/CMakeLists.txt @@ -17,8 +17,6 @@ set(remote_src ../auth/trusted/AuthSspi.cpp ) add_src_win32(remote_src - os/win32/wnet.cpp - os/win32/wnet_proto.h os/win32/xnet.cpp os/win32/xnet.h os/win32/xnet_proto.h diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index ba4aec4d46..8f0a6d374c 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -83,7 +83,6 @@ #if defined(WIN_NT) #include "../common/isc_proto.h" -#include "../remote/os/win32/wnet_proto.h" #include "../remote/os/win32/xnet_proto.h" #endif @@ -97,15 +96,10 @@ const char* const PROTOCOL_INET4 = "inet4"; const char* const PROTOCOL_INET6 = "inet6"; #ifdef WIN_NT -const char* const PROTOCOL_WNET = "wnet"; const char* const PROTOCOL_XNET = "xnet"; - -const char* const WNET_SEPARATOR = "@"; -const char* const WNET_LOCALHOST = "\\\\."; #endif const char* const INET_SEPARATOR = "/"; - const char* const INET_LOCALHOST = "localhost"; @@ -160,7 +154,7 @@ namespace Remote { class Attachment; class Statement; -class Blob FB_FINAL : public RefCntIface > +class Blob final : public RefCntIface > { public: // IBlob implementation @@ -210,7 +204,7 @@ int Blob::release() return 0; } -class Transaction FB_FINAL : public RefCntIface > +class Transaction final : public RefCntIface > { public: // ITransaction implementation @@ -281,7 +275,7 @@ int Transaction::release() return 0; } -class ResultSet FB_FINAL : public RefCntIface > +class ResultSet final : public RefCntIface > { public: // IResultSet implementation @@ -339,7 +333,7 @@ int ResultSet::release() return 0; } -class Batch FB_FINAL : public RefCntIface > +class Batch final : public RefCntIface > { public: static const ULONG DEFER_BATCH_LIMIT = 64; @@ -588,7 +582,7 @@ int Batch::release() return 0; } -class Replicator FB_FINAL : public RefCntIface > +class Replicator final : public RefCntIface > { public: // IReplicator implementation @@ -623,7 +617,7 @@ int Replicator::release() return 0; } -class Statement FB_FINAL : public RefCntIface > +class Statement final : public RefCntIface > { public: // IStatement implementation @@ -729,7 +723,7 @@ int Statement::release() return 0; } -class Request FB_FINAL : public RefCntIface > +class Request final : public RefCntIface > { public: // IRequest implementation @@ -779,7 +773,7 @@ int Request::release() return 0; } -class Events FB_FINAL : public RefCntIface > +class Events final : public RefCntIface > { public: // IEvents implementation @@ -823,7 +817,7 @@ int Events::release() return 0; } -class Attachment FB_FINAL : public RefCntIface > +class Attachment final : public RefCntIface > { public: // IAttachment implementation @@ -930,7 +924,7 @@ int Attachment::release() return 0; } -class Service FB_FINAL : public RefCntIface > +class Service final : public RefCntIface > { public: // IService implementation @@ -1049,7 +1043,7 @@ void registerRedirector(Firebird::IPluginManager* iPlugin) } // namespace Remote /* -extern "C" void FB_PLUGIN_ENTRY_POINT(IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master) { IPluginManager* pi = master->getPluginManager(); registerRedirector(pi); @@ -4729,13 +4723,11 @@ bool ResultSet::fetch(CheckStatusWrapper* status, void* buffer, P_FETCH operatio ( // Low in inventory (statement->rsr_rows_pending <= statement->rsr_reorder_level) && (statement->rsr_msgs_waiting <= statement->rsr_reorder_level) && - // not using named pipe on NT // Pipelining causes both server & client to - // write at the same time. In named pipes, writes + // write at the same time. In XNET, writes // block for the other end to read - and so when both // attempt to write simultaneously, they end up - // waiting indefinitely for the other end to read - (port->port_type != rem_port::PIPE) && + // waiting indefinitely for the other end to read. (port->port_type != rem_port::XNET) && // We're fetching either forward or backward (operation == fetch_next || operation == fetch_prior) && @@ -5906,12 +5898,11 @@ void Request::receive(CheckStatusWrapper* status, int level, unsigned int msg_ty (tail->rrq_rows_pending <= tail->rrq_reorder_level && // Low in inventory tail->rrq_msgs_waiting <= tail->rrq_reorder_level && // Pipelining causes both server & client to - // write at the same time. In named pipes, writes + // write at the same time. In XNET, writes // block for the other end to read - and so when both // attempt to write simultaenously, they end up - // waiting indefinetly for the other end to read - (port->port_type != rem_port::PIPE) && // not named pipe on NT - (port->port_type != rem_port::XNET) && // not shared memory on NT + // waiting indefinetly for the other end to read. + (port->port_type != rem_port::XNET) && request->rrq_max_msg <= 1))) { // there's only one message type @@ -7330,20 +7321,6 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned #ifdef WIN_NT if (ISC_analyze_protocol(PROTOCOL_XNET, attach_name, node_name, NULL, needFile)) port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_USER_VFY, cBlock.getConfig(), ref_db_name); - else if (ISC_analyze_protocol(PROTOCOL_WNET, attach_name, node_name, WNET_SEPARATOR, needFile) || - ISC_analyze_pclan(attach_name, node_name)) - { - if (node_name.isEmpty()) - node_name = WNET_LOCALHOST; - else - { - ISC_unescape(node_name); - ISC_utf8ToSystem(node_name); - } - - port = WNET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_USER_VFY, - cBlock.getConfig(), ref_db_name); - } else #endif @@ -7376,15 +7353,13 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned if (!port) { PathName expanded_name = attach_name; - ISC_expand_share(expanded_name); - if (ISC_analyze_pclan(expanded_name, node_name)) { ISC_unescape(node_name); ISC_utf8ToSystem(node_name); - port = WNET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_USER_VFY, - cBlock.getConfig(), ref_db_name); + port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_USER_VFY, pb, + cBlock.getConfig(), ref_db_name, cryptCb); } } #endif @@ -7418,12 +7393,6 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_USER_VFY, cBlock.getConfig(), ref_db_name); } - - if (!port) - { - port = WNET_analyze(&cBlock, attach_name, WNET_LOCALHOST, flags & ANALYZE_USER_VFY, - cBlock.getConfig(), ref_db_name); - } #endif if (!port) { @@ -7844,20 +7813,9 @@ static void disconnect( rem_port* port) } } - // BAND-AID: - // It seems as if we are disconnecting the port - // on both the server and client side. For now - // let the server handle this for named pipes + packet->p_operation = op_disconnect; + port->send(packet); - // 8-Aug-1997 M. Duquette - // R. Kumar - // M. Romanini - - if (port->port_type != rem_port::PIPE) - { - packet->p_operation = op_disconnect; - port->send(packet); - } REMOTE_free_packet(port, packet); } @@ -8647,6 +8605,11 @@ static void receive_packet_noqueue(rem_port* port, PACKET* packet) bCheckResponse = true; break; + case op_batch_create: + stmt_id = p->packet.p_batch_create.p_batch_statement; + bCheckResponse = true; + break; + case op_free_statement: stmt_id = p->packet.p_sqlfree.p_sqlfree_statement; bFreeStmt = (p->packet.p_sqlfree.p_sqlfree_option == DSQL_drop); diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index b706178066..103169eb1e 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -671,7 +671,7 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, ISC_get_user(&buffer, &eff_uid, &eff_gid); #ifdef WIN_NT - // WNET and XNET lowercase user names (as it's always case-insensitive in Windows) + // XNET lowercases user names (as it's always case-insensitive in Windows), // so let's be consistent and use the same trick for INET as well buffer.lower(); #endif @@ -716,7 +716,8 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_lazy_send, 5), REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_lazy_send, 6), REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_lazy_send, 7), - REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_lazy_send, 8) + REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_lazy_send, 8), + REMOTE_PROTOCOL(PROTOCOL_VERSION18, ptype_lazy_send, 9) }; fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); cnct->p_cnct_count = FB_NELEM(protocols_to_try); diff --git a/src/remote/os/win32/wnet.cpp b/src/remote/os/win32/wnet.cpp deleted file mode 100644 index 192f34322e..0000000000 --- a/src/remote/os/win32/wnet.cpp +++ /dev/null @@ -1,1476 +0,0 @@ -/* - * PROGRAM: JRD Remote Interface/Server - * MODULE: wnet.cpp - * DESCRIPTION: Windows Net Communications module. - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifdef DEBUG -// define WNET_trace to 0 (zero) for no packet debugging -#define WNET_trace -#endif - -#include "firebird.h" -#include -#include -#include "../remote/remote.h" -#include "ibase.h" - -#include "../utilities/install/install_nt.h" - -#include "../remote/proto_proto.h" -#include "../remote/remot_proto.h" -#include "../remote/os/win32/wnet_proto.h" -#include "../yvalve/gds_proto.h" -#include "../common/isc_proto.h" -#include "../common/isc_f_proto.h" -#include "../common/config/config.h" -#include "../common/utils_proto.h" -#include "../common/classes/ClumpletWriter.h" -#include "../common/classes/init.h" - -#include - -using namespace Firebird; - -const int MAX_DATA = 2048; -const int BUFFER_SIZE = MAX_DATA; - -const char* PIPE_PREFIX = "pipe"; // win32-specific -const char* SERVER_PIPE_SUFFIX = "server"; -const char* EVENT_PIPE_SUFFIX = "event"; -AtomicCounter event_counter; - -static GlobalPtr wnet_ports; -static GlobalPtr init_mutex; -static volatile bool wnet_initialized = false; -static volatile bool wnet_shutdown = false; - -static bool accept_connection(rem_port*, const P_CNCT*); -static rem_port* alloc_port(rem_port*); -static rem_port* aux_connect(rem_port*, PACKET*); -static rem_port* aux_request(rem_port*, PACKET*); -static bool connect_client(rem_port*); -static void disconnect(rem_port*); -#ifdef NOT_USED_OR_REPLACED -static void exit_handler(void*); -#endif -static void force_close(rem_port*); -static rem_str* make_pipe_name(const RefPtr&, const TEXT*, const TEXT*, const TEXT*); -static rem_port* receive(rem_port*, PACKET*); -static int send_full(rem_port*, PACKET*); -static int send_partial(rem_port*, PACKET*); -static RemoteXdr* xdrwnet_create(rem_port*, UCHAR *, USHORT, xdr_op); -static bool_t xdrwnet_endofrecord(RemoteXdr*);//, int); -static bool wnet_error(rem_port*, const TEXT*, ISC_STATUS, int); -static void wnet_gen_error(rem_port*, const Arg::StatusVector& v); -static bool_t wnet_read(RemoteXdr*); -static bool_t wnet_write(RemoteXdr*); //, int); -#ifdef DEBUG -static void packet_print(const TEXT*, const UCHAR*, const int); -#endif -static bool packet_receive(rem_port*, UCHAR*, SSHORT, SSHORT*); -static bool packet_send(rem_port*, const SCHAR*, SSHORT); -static void wnet_make_file_name(TEXT*, DWORD); - -static int cleanup_ports(const int, const int, void*); - -struct WnetXdr : public RemoteXdr -{ - virtual bool_t x_getbytes(SCHAR *, unsigned); // get some bytes from " - virtual bool_t x_putbytes(const SCHAR*, unsigned); // put some bytes to " -}; - - -rem_port* WNET_analyze(ClntAuthBlock* cBlock, - const PathName& file_name, - const TEXT* node_name, - bool uv_flag, - RefPtr* config, - const Firebird::PathName* ref_db_name) -{ -/************************************** - * - * W N E T _ a n a l y z e - * - ************************************** - * - * Functional description - * Determine whether the file name has a "\\nodename". - * If so, establish an external connection to the node. - * - * If a connection is established, return a port block, otherwise - * return NULL. - * - **************************************/ - - // We need to establish a connection to a remote server. Allocate the necessary - // blocks and get ready to go. - - Rdb* rdb = FB_NEW Rdb; - PACKET* packet = &rdb->rdb_packet; - - // Pick up some user identification information - string buffer; - ClumpletWriter user_id(ClumpletReader::UnTagged, 64000); - if (cBlock) - { - cBlock->extractDataFromPluginTo(user_id); - } - - ISC_get_user(&buffer, 0, 0); - buffer.lower(); - ISC_systemToUtf8(buffer); - user_id.insertString(CNCT_user, buffer); - - ISC_get_host(buffer); - buffer.lower(); - ISC_systemToUtf8(buffer); - user_id.insertString(CNCT_host, buffer); - - if (uv_flag) { - user_id.insertTag(CNCT_user_verification); - } - - // Establish connection to server - - P_CNCT* const cnct = &packet->p_cnct; - packet->p_operation = op_connect; - cnct->p_cnct_operation = 0; - cnct->p_cnct_cversion = CONNECT_VERSION3; - cnct->p_cnct_client = ARCHITECTURE; - - const PathName& cnct_file(ref_db_name ? (*ref_db_name) : file_name); - cnct->p_cnct_file.cstr_length = (ULONG) cnct_file.length(); - cnct->p_cnct_file.cstr_address = reinterpret_cast(cnct_file.c_str()); - - // If we want user verification, we can't speak anything less than version 7 - - cnct->p_cnct_user_id.cstr_length = (ULONG) user_id.getBufferLength(); - cnct->p_cnct_user_id.cstr_address = user_id.getBuffer(); - - static const p_cnct::p_cnct_repeat protocols_to_try[] = - { - REMOTE_PROTOCOL(PROTOCOL_VERSION10, ptype_batch_send, 1), - REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_batch_send, 2), - REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_batch_send, 3), - REMOTE_PROTOCOL(PROTOCOL_VERSION13, ptype_batch_send, 4), - REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_batch_send, 5), - REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_batch_send, 6), - REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_batch_send, 7), - REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_batch_send, 8) - }; - fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); - cnct->p_cnct_count = FB_NELEM(protocols_to_try); - - for (size_t i = 0; i < cnct->p_cnct_count; i++) { - cnct->p_cnct_versions[i] = protocols_to_try[i]; - } - - // If we can't talk to a server, punt. Let somebody else generate an error. - - rem_port* port = NULL; - try - { - port = WNET_connect(node_name, packet, 0, config); - } - catch (const Exception&) - { - delete rdb; - throw; - } - - // Get response packet from server. - - rdb->rdb_port = port; - port->port_context = rdb; - port->receive(packet); - - P_ACPT* accept = NULL; - switch (packet->p_operation) - { - case op_accept_data: - case op_cond_accept: - accept = &packet->p_acpd; - if (cBlock) - { - cBlock->storeDataForPlugin(packet->p_acpd.p_acpt_data.cstr_length, - packet->p_acpd.p_acpt_data.cstr_address); - cBlock->authComplete = packet->p_acpd.p_acpt_authenticated; - port->addServerKeys(&packet->p_acpd.p_acpt_keys); - cBlock->resetClnt(&packet->p_acpd.p_acpt_keys); - } - break; - - case op_accept: - if (cBlock) - { - cBlock->resetClnt(); - } - accept = &packet->p_acpt; - break; - - case op_response: - try - { - Firebird::LocalStatus warning; // Ignore connect warnings for a while - REMOTE_check_response(&warning, rdb, packet); - } - catch (const Firebird::Exception&) - { - disconnect(port); - delete rdb; - throw; - } - // fall through - response is not a required accept - - default: - disconnect(port); - delete rdb; - Arg::Gds(isc_connect_reject).raise(); - break; - } - - fb_assert(accept); - fb_assert(port); - port->port_protocol = accept->p_acpt_version; - - // once we've decided on a protocol, concatenate the version - // string to reflect it... - - string temp; - temp.printf("%s/P%d", port->port_version->str_data, - port->port_protocol & FB_PROTOCOL_MASK); - delete port->port_version; - port->port_version = REMOTE_make_string(temp.c_str()); - - if (accept->p_acpt_architecture == ARCHITECTURE) - port->port_flags |= PORT_symmetric; - - if (accept->p_acpt_type != ptype_out_of_band) - port->port_flags |= PORT_no_oob; - - return port; -} - - -rem_port* WNET_connect(const TEXT* name, PACKET* packet, USHORT flag, Firebird::RefPtr* config) -{ -/************************************** - * - * W N E T _ c o n n e c t - * - ************************************** - * - * Functional description - * Establish half of a communication link. If a connect packet is given, - * the connection is on behalf of a remote interface. Otherwise the - * connect is for a server process. - * - **************************************/ - rem_port* const port = alloc_port(0); - if (config) - { - port->port_config = *config; - } - - delete port->port_connection; - port->port_connection = make_pipe_name(port->getPortConfig(), name, SERVER_PIPE_SUFFIX, 0); - - // If we're a host, just make the connection - - if (packet) - { - while (true) - { - port->port_pipe = CreateFile(port->port_connection->str_data, - GENERIC_WRITE | GENERIC_READ, - 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (port->port_pipe != INVALID_HANDLE_VALUE) { - break; - } - const ISC_STATUS status = GetLastError(); - if (status != ERROR_PIPE_BUSY) - { - wnet_error(port, "CreateFile", isc_net_connect_err, status); - disconnect(port); - return NULL; - } - WaitNamedPipe(port->port_connection->str_data, 3000L); - } - send_full(port, packet); - return port; - } - - // We're a server, so wait for a host to show up - - wnet_ports->registerPort(port); - while (!wnet_shutdown) - { - port->port_pipe = - CreateNamedPipe(port->port_connection->str_data, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_WAIT | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, - PIPE_UNLIMITED_INSTANCES, - MAX_DATA, - MAX_DATA, - 0, - ISC_get_security_desc()); - if (port->port_pipe == INVALID_HANDLE_VALUE) - { - const DWORD dwError = GetLastError(); - if (dwError == ERROR_CALL_NOT_IMPLEMENTED) - { - disconnect(port); - wnet_shutdown = true; - break; - } - - wnet_error(port, "CreateNamedPipe", isc_net_connect_listen_err, dwError); - disconnect(port); - return NULL; - } - - if (!connect_client(port)) - break; - - if (flag & (SRVR_debug | SRVR_multi_client)) - { - port->port_server_flags |= SRVR_server; - port->port_flags |= PORT_server; - if (flag & SRVR_multi_client) - { - port->port_server_flags |= SRVR_multi_client; - } - - return port; - } - - TEXT name[MAXPATHLEN]; - GetModuleFileName(NULL, name, sizeof(name)); - - string cmdLine; - cmdLine.printf("%s -w -h %" HANDLEFORMAT"@%" ULONGFORMAT, name, port->port_pipe, GetCurrentProcessId()); - - STARTUPINFO start_crud; - PROCESS_INFORMATION pi; - start_crud.cb = sizeof(STARTUPINFO); - start_crud.lpReserved = NULL; - start_crud.lpReserved2 = NULL; - start_crud.cbReserved2 = 0; - start_crud.lpDesktop = NULL; - start_crud.lpTitle = NULL; - start_crud.dwFlags = STARTF_FORCEOFFFEEDBACK; - - if (CreateProcess(NULL, cmdLine.begin(), NULL, NULL, FALSE, - (flag & SRVR_high_priority ? - HIGH_PRIORITY_CLASS | DETACHED_PROCESS : - NORMAL_PRIORITY_CLASS | DETACHED_PROCESS), - NULL, NULL, &start_crud, &pi)) - { - // hvlad: child process will close our handle of client pipe - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - } - else - { - gds__log("WNET/wnet_error: fork/CreateProcess errno = %d", GetLastError()); - CloseHandle(port->port_pipe); - } - - if (wnet_shutdown) - disconnect(port); - } - - if (wnet_shutdown) - { - Arg::Gds temp(isc_net_server_shutdown); - temp << Arg::Str("WNET"); - temp.raise(); - } - - return NULL; -} - - -rem_port* WNET_reconnect(HANDLE handle) -{ -/************************************** - * - * W N E T _ r e c o n n e c t - * - ************************************** - * - * Functional description - * A communications link has been established by another - * process. We have inherited the handle. Set up - * a port block. - * - **************************************/ - rem_port* const port = alloc_port(0); - - delete port->port_connection; - port->port_connection = make_pipe_name(port->getPortConfig(), NULL, SERVER_PIPE_SUFFIX, 0); - - port->port_pipe = handle; - port->port_server_flags |= SRVR_server; - port->port_flags |= PORT_server; - - return port; -} - - -static bool accept_connection( rem_port* port, const P_CNCT* cnct) -{ -/************************************** - * - * a c c e p t _ c o n n e c t i o n - * - ************************************** - * - * Functional description - * Accept an incoming request for connection. This is purely a lower - * level handshaking function, and does not constitute the server - * response for protocol selection. - * - **************************************/ - // Default account to "guest" (in theory all packets contain a name) - - string user_name("guest"), host_name; - - // Pick up account and host name, if given - - ClumpletReader id(ClumpletReader::UnTagged, - cnct->p_cnct_user_id.cstr_address, - cnct->p_cnct_user_id.cstr_length); - - for (id.rewind(); !id.isEof(); id.moveNext()) - { - switch (id.getClumpTag()) - { - case CNCT_user: - id.getString(user_name); - break; - - case CNCT_host: - id.getString(host_name); - break; - - default: - break; - } - } - - port->port_login = port->port_user_name = user_name; - port->port_peer_name = host_name; - port->port_protocol_id = "WNET"; - - return true; -} - - -static rem_port* alloc_port( rem_port* parent) -{ -/************************************** - * - * a l l o c _ p o r t - * - ************************************** - * - * Functional description - * Allocate a port block, link it in to parent (if there is a parent), - * and initialize input and output XDR streams. - * - **************************************/ - - if (!wnet_initialized) - { - MutexLockGuard guard(init_mutex, FB_FUNCTION); - if (!wnet_initialized) - { - wnet_initialized = true; - fb_shutdown_callback(0, cleanup_ports, fb_shut_postproviders, 0); - } - } - - rem_port* port = FB_NEW rem_port(rem_port::PIPE, BUFFER_SIZE * 2); - - TEXT buffer[BUFFER_TINY]; - ISC_get_host(buffer, sizeof(buffer)); - port->port_host = REMOTE_make_string(buffer); - port->port_connection = REMOTE_make_string(buffer); - sprintf(buffer, "WNet (%s)", port->port_host->str_data); - port->port_version = REMOTE_make_string(buffer); - - port->port_accept = accept_connection; - port->port_disconnect = disconnect; - port->port_force_close = force_close; - port->port_receive_packet = receive; - port->port_send_packet = send_full; - port->port_send_partial = send_partial; - port->port_connect = aux_connect; - port->port_request = aux_request; - port->port_buff_size = BUFFER_SIZE; - - port->port_event = CreateEvent(NULL, TRUE, TRUE, NULL); - - port->port_send = xdrwnet_create(port, &port->port_buffer[BUFFER_SIZE], BUFFER_SIZE, XDR_ENCODE); - - port->port_receive = xdrwnet_create(port, port->port_buffer, 0, XDR_DECODE); - - if (parent) - { - delete port->port_connection; - port->port_connection = nullptr; - port->port_connection = REMOTE_make_string(parent->port_connection->str_data); - - port->linkParent(parent); - } - - return port; -} - - -static rem_port* aux_connect( rem_port* port, PACKET* packet) -{ -/************************************** - * - * a u x _ c o n n e c t - * - ************************************** - * - * Functional description - * Try to establish an alternative connection. Somebody has already - * done a successfull connect request ("packet" contains the response). - * - **************************************/ - // If this is a server, we're got an auxiliary connection. Accept it - - if (port->port_server_flags) - { - if (!connect_client(port)) - return NULL; - - port->port_flags |= PORT_async; - return port; - } - - // The server will be sending its process id in the packet to - // create a unique pipe name. - - P_RESP* response = &packet->p_resp; - - TEXT str_pid[32]; - const TEXT* p = 0; - if (response->p_resp_data.cstr_length) - { - // Avoid B.O. - const size_t len = MIN(response->p_resp_data.cstr_length, sizeof(str_pid) - 1); - memcpy(str_pid, response->p_resp_data.cstr_address, len); - str_pid[len] = 0; - p = str_pid; - } - - rem_port* const new_port = alloc_port(port->port_parent); - port->port_async = new_port; - new_port->port_flags = port->port_flags & PORT_no_oob; - new_port->port_flags |= PORT_async; - new_port->port_connection = make_pipe_name(port->getPortConfig(), - port->port_connection->str_data, EVENT_PIPE_SUFFIX, p); - - while (true) - { - new_port->port_pipe = - CreateFile(new_port->port_connection->str_data, GENERIC_READ, 0, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (new_port->port_pipe != INVALID_HANDLE_VALUE) - break; - const ISC_STATUS status = GetLastError(); - if (status != ERROR_PIPE_BUSY) - { - wnet_error(new_port, "CreateFile", isc_net_event_connect_err, status); - return NULL; - } - WaitNamedPipe(new_port->port_connection->str_data, 3000L); - } - - return new_port; -} - - -static rem_port* aux_request( rem_port* vport, PACKET* packet) -{ -/************************************** - * - * a u x _ r e q u e s t - * - ************************************** - * - * Functional description - * A remote interface has requested the server prepare an auxiliary - * connection; the server calls aux_request to set up the connection. - * Send the servers process id on the packet. If at a later time - * a multi client server is used, there may be a need to - * generate a unique id based on connection. - * - **************************************/ - - const DWORD server_pid = (vport->port_server_flags & SRVR_multi_client) ? - ++event_counter : GetCurrentProcessId(); - rem_port* const new_port = alloc_port(vport->port_parent); - new_port->port_server_flags = vport->port_server_flags; - new_port->port_flags = (vport->port_flags & PORT_no_oob) | PORT_connecting; - vport->port_async = new_port; - - TEXT str_pid[32]; - wnet_make_file_name(str_pid, server_pid); - new_port->port_connection = make_pipe_name(vport->getPortConfig(), - vport->port_connection->str_data, EVENT_PIPE_SUFFIX, str_pid); - - new_port->port_pipe = - CreateNamedPipe(new_port->port_connection->str_data, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_WAIT | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, - PIPE_UNLIMITED_INSTANCES, - MAX_DATA, - MAX_DATA, - 0, - ISC_get_security_desc()); - - if (new_port->port_pipe == INVALID_HANDLE_VALUE) - { - wnet_error(new_port, "CreateNamedPipe", isc_net_event_listen_err, ERRNO); - disconnect(new_port); - return NULL; - } - - P_RESP* response = &packet->p_resp; - response->p_resp_data.cstr_length = (ULONG) strlen(str_pid); - memcpy(response->p_resp_data.cstr_address, str_pid, response->p_resp_data.cstr_length); - - return new_port; -} - - -static bool connect_client(rem_port *port) -{ -/************************************** - * - * c o n n e c t _ c l i e n t - * - ************************************** - * - * Functional description - * Wait for new client connected. - * - **************************************/ - - OVERLAPPED ovrl = {0}; - ovrl.hEvent = port->port_event; - if (!ConnectNamedPipe(port->port_pipe, &ovrl)) - { - DWORD err = GetLastError(); - switch (err) - { - case ERROR_PIPE_CONNECTED: - break; - - case ERROR_IO_PENDING: - if (WaitForSingleObject(port->port_event, INFINITE) == WAIT_OBJECT_0) - { - if (!wnet_shutdown) - break; - } - else - err = GetLastError(); // fall thru - - default: - if (!wnet_shutdown) { - wnet_error(port, "ConnectNamedPipe", isc_net_connect_err, err); - } - disconnect(port); - return false; - } - } - return true; -} - - -static void disconnect(rem_port* port) -{ -/************************************** - * - * d i s c o n n e c t - * - ************************************** - * - * Functional description - * Break a remote connection. - * - **************************************/ - - if (port->port_state == rem_port::DISCONNECTED) - return; - - port->port_state = rem_port::DISCONNECTED; - - if (port->port_async) - { - disconnect(port->port_async); - port->port_async = NULL; - } - port->port_context = NULL; - - // If this is a sub-port, unlink it from its parent - port->unlinkParent(); - port->port_flags &= ~PORT_connecting; - - if (port->port_server_flags & SRVR_server) - { - FlushFileBuffers(port->port_pipe); - DisconnectNamedPipe(port->port_pipe); - } - if (port->port_event != INVALID_HANDLE_VALUE) - { - CloseHandle(port->port_event); - port->port_event = INVALID_HANDLE_VALUE; - } - if (port->port_pipe != INVALID_HANDLE_VALUE) - { - CloseHandle(port->port_pipe); - port->port_pipe = INVALID_HANDLE_VALUE; - } - - wnet_ports->unRegisterPort(port); - - if (port->port_thread_guard && port->port_events_thread && !port->port_events_threadId.isCurrent()) - port->port_thread_guard->setWait(port->port_events_thread); - else - port->releasePort(); -} - - -static void force_close(rem_port* port) -{ -/************************************** - * - * f o r c e _ c l o s e - * - ************************************** - * - * Functional description - * Forcibly close remote connection. - * - **************************************/ - - if (port->port_event != INVALID_HANDLE_VALUE) - { - port->port_state = rem_port::BROKEN; - - const HANDLE handle = port->port_pipe; - port->port_pipe = INVALID_HANDLE_VALUE; - SetEvent(port->port_event); - CloseHandle(handle); - } -} - - -#ifdef NOT_USED_OR_REPLACED -static void exit_handler(void* main_port) -{ -/************************************** - * - * e x i t _ h a n d l e r - * - ************************************** - * - * Functional description - * Shutdown all active connections - * to allow restart. - * - **************************************/ - for (rem_port* vport = static_cast(main_port); vport; vport = vport->port_next) - CloseHandle(vport->port_pipe); -} -#endif - - -static rem_str* make_pipe_name(const RefPtr& config, const TEXT* connect_name, - const TEXT* suffix_name, const TEXT* str_pid) -{ -/************************************** - * - * m a k e _ p i p e _ n a m e - * - ************************************** - * - * Functional description - * Construct a name for the pipe connection. - * Figure out whether we need a remote node name, - * and construct the pipe name accordingly. - * If a server pid != 0, append it to pipe name as <>/ - * - **************************************/ - string buffer("\\\\"); - - const TEXT* p = connect_name; - - if (!p || *p++ != '\\' || *p++ != '\\') - p = "."; - - while (*p && *p != '\\' && *p != '@') - buffer += *p++; - - const TEXT* protocol = NULL; - switch (*p) - { - case 0: - protocol = config->getRemoteServiceName(); - break; - case '@': - protocol = p + 1; - break; - default: - while (*p) - { - if (*p++ == '\\') - protocol = p; - } - } - - buffer += '\\'; - buffer += PIPE_PREFIX; - buffer += '\\'; - const char *pipe_name = config->getRemotePipeName(); - buffer += pipe_name; - buffer += '\\'; - buffer += suffix_name; - buffer += '\\'; - buffer += protocol; - - if (str_pid) - { - buffer += '\\'; - buffer += str_pid; - } - - return REMOTE_make_string(buffer.c_str()); -} - - -static rem_port* receive( rem_port* main_port, PACKET* packet) -{ -/************************************** - * - * r e c e i v e - * - ************************************** - * - * Functional description - * Receive a message from a port or clients of a port. If the process - * is a server and a connection request comes in, generate a new port - * block for the client. - * - **************************************/ - - if (!xdr_protocol(main_port->port_receive, packet)) - packet->p_operation = op_exit; - - return main_port; -} - - -static int send_full( rem_port* port, PACKET* packet) -{ -/************************************** - * - * s e n d _ f u l l - * - ************************************** - * - * Functional description - * Send a packet across a port to another process. - * - **************************************/ - - if (!xdr_protocol(port->port_send, packet)) - return FALSE; - - return xdrwnet_endofrecord(port->port_send); //, TRUE); -} - - -static int send_partial( rem_port* port, PACKET* packet) -{ -/************************************** - * - * s e n d _ p a r t i a l - * - ************************************** - * - * Functional description - * Send a packet across a port to another process. - * - **************************************/ - - return xdr_protocol(port->port_send, packet); -} - - -static RemoteXdr* xdrwnet_create(rem_port* port, UCHAR* buffer, USHORT length, xdr_op x_op) -{ -/************************************** - * - * x d r w n e t _ c r e a t e - * - ************************************** - * - * Functional description - * Initialize an XDR stream for Apollo mailboxes. - * - **************************************/ - - RemoteXdr* xdrs = FB_NEW WnetXdr; - - xdrs->x_public = port; - xdrs->create(reinterpret_cast(buffer), length, x_op); - - return xdrs; -} - - -static bool_t xdrwnet_endofrecord( RemoteXdr* xdrs) //, bool_t flushnow) -{ -/************************************** - * - * x d r w n e t _ e n d o f r e c o r d - * - ************************************** - * - * Functional description - * Write out the rest of a record. - * - **************************************/ - - return wnet_write(xdrs); //, flushnow); -} - - -static bool wnet_error(rem_port* port, - const TEXT* function, ISC_STATUS operation, int status) -{ -/************************************** - * - * w n e t _ e r r o r - * - ************************************** - * - * Functional description - * An I/O error has occurred. If a status vector is present, - * generate an error return. In any case, return NULL, which - * is used to indicate and error. - * - **************************************/ - if (status) - { - if (port->port_state == rem_port::PENDING) - { - gds__log("WNET/wnet_error: %s errno = %d", function, status); - } - - wnet_gen_error(port, Arg::Gds(operation) << SYS_ERR(status)); - } - else - { - wnet_gen_error(port, Arg::Gds(operation)); - } - - return false; -} - - -static void wnet_gen_error (rem_port* port, const Arg::StatusVector& v) -{ -/************************************** - * - * w n e t _ g e n _ e r r o r - * - ************************************** - * - * Functional description - * An error has occurred. Mark the port as broken. - * Format the status vector if there is one and - * save the status vector strings in a permanent place. - * - **************************************/ - port->port_state = rem_port::BROKEN; - - TEXT node_name[MAXPATHLEN]; - if (port->port_connection) - { - fb_utils::copy_terminate(node_name, port->port_connection->str_data + 2, sizeof(node_name)); - TEXT* const p = strchr(node_name, '\\'); - if (p != NULL) - *p = '\0'; - } - else - { - strcpy(node_name, "(unknown)"); - } - - Arg::Gds error(isc_network_error); - error << Arg::Str(node_name) << v; - error.raise(); -} - - -bool_t WnetXdr::x_getbytes(SCHAR* buff, unsigned bytecount) -{ -/************************************** - * - * w n e t _ g e t b y t e s - * - ************************************** - * - * Functional description - * Get a bunch of bytes from a memory stream if it fits. - * - **************************************/ - // Use memcpy to optimize bulk transfers. - - while (bytecount > (SLONG) sizeof(ISC_QUAD)) - { - if (x_handy >= bytecount) - { - memcpy(buff, x_private, bytecount); - x_private += bytecount; - x_handy -= bytecount; - return TRUE; - } - if (x_handy > 0) - { - memcpy(buff, x_private, x_handy); - x_private += x_handy; - buff += x_handy; - bytecount -= x_handy; - x_handy = 0; - } - if (!wnet_read(this)) - return FALSE; - } - - // Scalar values and bulk transfer remainder fall thru - // to be moved byte-by-byte to avoid memcpy setup costs. - - if (!bytecount) - return TRUE; - - if (x_handy >= bytecount) - { - x_handy -= bytecount; - do { - *buff++ = *x_private++; - } while (--bytecount); - return TRUE; - } - - while (bytecount--) - { - if (x_handy == 0 && !wnet_read(this)) - return FALSE; - *buff++ = *x_private++; - --x_handy; - } - - return TRUE; -} - - -bool_t WnetXdr::x_putbytes(const SCHAR* buff, unsigned count) -{ -/************************************** - * - * w n e t _ p u t b y t e s - * - ************************************** - * - * Functional description - * Put a bunch of bytes to a memory stream if it fits. - * - **************************************/ - SLONG bytecount = count; - - // Use memcpy to optimize bulk transfers. - - while (bytecount > (SLONG) sizeof(ISC_QUAD)) - { - if (x_handy >= bytecount) - { - memcpy(x_private, buff, bytecount); - x_private += bytecount; - x_handy -= bytecount; - return TRUE; - } - if (x_handy > 0) - { - memcpy(x_private, buff, x_handy); - x_private += x_handy; - buff += x_handy; - bytecount -= x_handy; - x_handy = 0; - } - if (!wnet_write(this /*, 0*/)) - return FALSE; - } - - // Scalar values and bulk transfer remainder fall thru - // to be moved byte-by-byte to avoid memcpy setup costs. - - if (!bytecount) - return TRUE; - - if (x_handy >= bytecount) - { - x_handy -= bytecount; - do { - *x_private++ = *buff++; - } while (--bytecount); - return TRUE; - } - - while (bytecount--) - { - if (x_handy == 0 && !wnet_write(this /*, 0*/)) - return FALSE; - --x_handy; - *x_private++ = *buff++; - } - - return TRUE; -} - - -static bool_t wnet_read( RemoteXdr* xdrs) -{ -/************************************** - * - * w n e t _ r e a d - * - ************************************** - * - * Functional description - * Read a buffer full of data. If we receive a bad packet, - * send the moral equivalent of a NAK and retry. ACK all - * partial packets. Don't ACK the last packet -- the next - * message sent will handle this. - * - **************************************/ - rem_port* port = xdrs->x_public; - SCHAR* p = xdrs->x_base; - const SCHAR* const end = p + BUFFER_SIZE; - - // If buffer is not completely empty, slide down what what's left - - if (xdrs->x_handy > 0) - { - memmove(p, xdrs->x_private, xdrs->x_handy); - p += xdrs->x_handy; - } - - while (true) - { - SSHORT length = end - p; - if (!packet_receive(port, reinterpret_cast(p), length, &length)) - { - return FALSE; - } - if (length >= 0) - { - p += length; - break; - } - p -= length; - if (!packet_send(port, 0, 0)) - return FALSE; - } - - xdrs->x_handy = p - xdrs->x_base; - xdrs->x_private = xdrs->x_base; - - return TRUE; -} - - -static bool_t wnet_write( RemoteXdr* xdrs /*, bool_t end_flag*/) -{ -/************************************** - * - * w n e t _ w r i t e - * - ************************************** - * - * Functional description - * Write a buffer fulll of data. - * Obsolete: If the end_flag isn't set, indicate - * that the buffer is a fragment, and reset the XDR for another buffer - * load. - * - **************************************/ - // Encode the data portion of the packet - - rem_port* vport = xdrs->x_public; - const SCHAR* p = xdrs->x_base; - SSHORT length = xdrs->x_private - p; - - // Send data in manageable hunks. If a packet is partial, indicate - // that with a negative length. A positive length marks the end. - - while (length) - { - const SSHORT l = MIN(length, MAX_DATA); - length -= l; - if (!packet_send(vport, p, (SSHORT) (length ? -l : l))) - return FALSE; - p += l; - } - - xdrs->x_private = xdrs->x_base; - xdrs->x_handy = BUFFER_SIZE; - - return TRUE; -} - - -#ifdef DEBUG -static void packet_print(const TEXT* string, const UCHAR* packet, const int length) -{ -/************************************** - * - * p a c k e t _ p r i n t - * - ************************************** - * - * Functional description - * Print a summary of packet. - * - **************************************/ - int sum = 0; - int l = length; - - if (l) - { - do { - sum += *packet++; - } while (--l); - } - - printf("%s\t: length = %d, checksum = %d\n", string, length, sum); -} -#endif - - -static bool packet_receive(rem_port* port, UCHAR* buffer, SSHORT buffer_length, SSHORT* length) -{ -/************************************** - * - * p a c k e t _ r e c e i v e - * - ************************************** - * - * Functional description - * Receive a packet and pass on it's goodness. If it's good, - * return true and the reported length of the packet, and update - * the receive sequence number. If it's bad, return false. If it's - * a duplicate message, just ignore it. - * - **************************************/ - DWORD n = 0; - OVERLAPPED ovrl = {0}; - ovrl.hEvent = port->port_event; - - BOOL status = ReadFile(port->port_pipe, buffer, buffer_length, &n, &ovrl); - DWORD dwError = GetLastError(); - - if (!status && dwError == ERROR_IO_PENDING) - { - status = GetOverlappedResult(port->port_pipe, &ovrl, &n, TRUE); - dwError = GetLastError(); - } - if (!status && dwError != ERROR_BROKEN_PIPE) { - return wnet_error(port, "ReadFile", isc_net_read_err, dwError); - } - - if (!n) - { - if (port->port_flags & (PORT_detached | PORT_disconnect)) - return false; - - return wnet_error(port, "ReadFile end-of-file", isc_net_read_err, dwError); - } - - // decrypt - if (port->port_crypt_plugin) - { - LocalStatus ls; - CheckStatusWrapper st(&ls); - - port->port_crypt_plugin->decrypt(&st, n, buffer, buffer); - if (st.getState() & IStatus::STATE_ERRORS) - { - status_exception::raise(&st); - } - } - -#if defined(DEBUG) && defined(WNET_trace) - packet_print("receive", buffer, n); -#endif - - port->port_rcv_packets++; - port->port_rcv_bytes += n; - - *length = (SSHORT) n; - - return true; -} - - -static bool packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_length) -{ -/************************************** - * - * p a c k e t _ s e n d - * - ************************************** - * - * Functional description - * Send some data on it's way. - * - **************************************/ - const SCHAR* data = buffer; - const DWORD length = buffer_length; - - // encrypt - HalfStaticArray b; - if (port->port_crypt_plugin && port->port_crypt_complete) - { - LocalStatus ls; - CheckStatusWrapper st(&ls); - - char* d = b.getBuffer(buffer_length); - port->port_crypt_plugin->encrypt(&st, buffer_length, data, d); - if (st.getState() & IStatus::STATE_ERRORS) - { - status_exception::raise(&st); - } - - data = d; - } - - OVERLAPPED ovrl = {0}; - ovrl.hEvent = port->port_event; - - DWORD n; - BOOL status = WriteFile(port->port_pipe, data, length, &n, &ovrl); - DWORD dwError = GetLastError(); - - if (!status && dwError == ERROR_IO_PENDING) - { - status = GetOverlappedResult(port->port_pipe, &ovrl, &n, TRUE); - dwError = GetLastError(); - } - if (!status && dwError != ERROR_NO_DATA) - return wnet_error(port, "WriteFile", isc_net_write_err, dwError); - if (n != length) - { - if (port->port_flags & (PORT_detached | PORT_disconnect)) - return false; - - return wnet_error(port, "WriteFile truncated", isc_net_write_err, dwError); - } - -#if defined(DEBUG) && defined(WNET_trace) - packet_print("send", reinterpret_cast(buffer), buffer_length); -#endif - - port->port_snd_packets++; - port->port_snd_bytes += buffer_length; - - return true; -} - - -static void wnet_make_file_name( TEXT* name, DWORD number) -{ -/************************************** - * - * w n e t _ m a k e _ f i l e _ n a m e - * - ************************************** - * - * Functional description - * Create a file name out of a number making sure - * the Windows <8>.<3> limitations are handled. - * - **************************************/ - TEXT temp[32]; - - sprintf(temp, "%lu", number); - - size_t length = strlen(temp); - if (length < 8) - { - strcpy(name, temp); - return; - } - - TEXT* p = name; - const TEXT* q = temp; - - while (length) - { - size_t len = (length > 8) ? 8 : length; - length -= len; - do { - *p++ = *q++; - } while (--len != 0); - - if (length) - *p++ = '\\'; - } - *p++ = 0; -} - -static int cleanup_ports(const int, const int, void*) -{ -/************************************** - * - * c l e a n u p _ p o r t s - * - ************************************** - * - * Functional description - * Shutdown all active connections - * to allow correct shutdown. - * - **************************************/ - wnet_shutdown = true; - - wnet_ports->closePorts(); - return 0; -} diff --git a/src/remote/os/win32/wnet_proto.h b/src/remote/os/win32/wnet_proto.h deleted file mode 100644 index e3251ecebd..0000000000 --- a/src/remote/os/win32/wnet_proto.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * PROGRAM: JRD Remote Interface/Server - * MODULE: wnet_proto.h - * DESCRIPTION: Prototpe header file for wnet.cpp - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifndef REMOTE_WNET_PROTO_H -#define REMOTE_WNET_PROTO_H - -#include "../common/classes/fb_string.h" - -rem_port* WNET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, bool, - Firebird::RefPtr*, const Firebird::PathName*); -rem_port* WNET_connect(const TEXT*, struct packet*, USHORT, Firebird::RefPtr*); -rem_port* WNET_reconnect(HANDLE); - - -#endif // REMOTE_WNET_PROTO_H diff --git a/src/remote/os/win32/xnet.cpp b/src/remote/os/win32/xnet.cpp index b1645657c0..9dc8e34557 100644 --- a/src/remote/os/win32/xnet.cpp +++ b/src/remote/os/win32/xnet.cpp @@ -306,7 +306,8 @@ rem_port* XNET_analyze(ClntAuthBlock* cBlock, REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_batch_send, 5), REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_batch_send, 6), REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_batch_send, 7), - REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_batch_send, 8) + REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_batch_send, 8), + REMOTE_PROTOCOL(PROTOCOL_VERSION18, ptype_batch_send, 9) }; fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); cnct->p_cnct_count = FB_NELEM(protocols_to_try); diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index f2f05067fb..ef466b8f34 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -889,6 +889,8 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) ULONG count = b->p_batch_messages; ULONG size = statement->rsr_batch_size; + if (!size) + statement->rsr_batch_size = size = FB_ALIGN(statement->rsr_format->fmt_length, FB_ALIGNMENT); if (xdrs->x_op == XDR_DECODE) { b->p_batch_data.cstr_length = (count ? count : 1) * size; @@ -996,10 +998,9 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) // Process status vectors ULONG pos = 0u; - LocalStatus to; DEB_RBATCH(fprintf(stderr, "BatRem: xdr sv %d\n", b->p_batch_vectors)); - for (unsigned i = 0; i < b->p_batch_vectors; ++i, ++pos) + for (unsigned i = 0; i < b->p_batch_vectors; ++pos) { DynamicStatusVector s; DynamicStatusVector* ptr = NULL; @@ -1011,6 +1012,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) if (pos == IBatchCompletionState::NO_MORE_ERRORS) return P_FALSE(xdrs, p); + LocalStatus to; statement->rsr_batch_ics->getStatus(&status_vector, &to, pos); if (status_vector.getState() & IStatus::STATE_ERRORS) continue; @@ -1027,17 +1029,19 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) if (xdrs->x_op == XDR_DECODE) { Firebird::Arg::StatusVector sv(ptr->value()); + LocalStatus to; sv.copyTo(&to); delete ptr; statement->rsr_batch_cs->regErrorAt(pos, &to); } + ++i; } // Process status-less errors pos = 0u; DEB_RBATCH(fprintf(stderr, "BatRem: xdr err %d\n", b->p_batch_errors)); - for (unsigned i = 0; i < b->p_batch_errors; ++i, ++pos) + for (unsigned i = 0; i < b->p_batch_errors; ++pos) { if (xdrs->x_op == XDR_ENCODE) { @@ -1046,6 +1050,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) if (pos == IBatchCompletionState::NO_MORE_ERRORS) return P_FALSE(xdrs, p); + LocalStatus to; statement->rsr_batch_ics->getStatus(&status_vector, &to, pos); if (!(status_vector.getState() & IStatus::STATE_ERRORS)) continue; @@ -1057,6 +1062,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) { statement->rsr_batch_cs->regErrorAt(pos, nullptr); } + ++i; } return P_TRUE(xdrs, p); diff --git a/src/remote/protocol.h b/src/remote/protocol.h index cdc3985e6f..992ff9bfb0 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -94,10 +94,15 @@ const USHORT PROTOCOL_VERSION16 = (FB_PROTOCOL_FLAG | 16); const USHORT PROTOCOL_STMT_TOUT = PROTOCOL_VERSION16; // Protocol 17: -// - supports op_batch_sync, op_info_batch, op_fetch_scroll +// - supports op_batch_sync, op_info_batch const USHORT PROTOCOL_VERSION17 = (FB_PROTOCOL_FLAG | 17); -const USHORT PROTOCOL_FETCH_SCROLL = PROTOCOL_VERSION17; + +// Protocol 18: +// - supports op_fetch_scroll + +const USHORT PROTOCOL_VERSION18 = (FB_PROTOCOL_FLAG | 18); +const USHORT PROTOCOL_FETCH_SCROLL = PROTOCOL_VERSION18; // Architecture types diff --git a/src/remote/remote.h b/src/remote/remote.h index 0fa6ae8e72..ae398431cb 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -714,7 +714,7 @@ public: }; // CryptKey implementation -class InternalCryptKey FB_FINAL : +class InternalCryptKey final : public Firebird::VersionedIface >, public Firebird::GlobalStorage { @@ -816,7 +816,7 @@ typedef Firebird::GetPlugins AuthClientPlugins; // Representation of authentication data, visible for plugin // Transfered in format, depending upon type of the packet (phase of handshake) -class RmtAuthBlock FB_FINAL : +class RmtAuthBlock final : public Firebird::VersionedIface > { public: @@ -840,7 +840,7 @@ private: }; -class ClntAuthBlock FB_FINAL : +class ClntAuthBlock final : public Firebird::RefCntIface > { private: @@ -857,7 +857,7 @@ private: Firebird::AutoPtr remAuthBlock; //Authentication block if present unsigned nextKey; // First key to be analyzed - class ClientCrypt FB_FINAL : + class ClientCrypt final : public Firebird::VersionedIface > { public: @@ -921,7 +921,7 @@ public: // Transfered from client data in format, suitable for plugins access typedef Firebird::GetPlugins AuthServerPlugins; -class SrvAuthBlock FB_FINAL : +class SrvAuthBlock final : public Firebird::VersionedIface >, public Firebird::GlobalStorage { @@ -1042,7 +1042,6 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted enum rem_port_t { INET, // Internet (TCP/IP) - PIPE, // Windows NT named pipe connection XNET // Windows NT shared memory connection } port_type; enum state_t { diff --git a/src/remote/remote_def.h b/src/remote/remote_def.h index 58daa1aa7a..39e34e0e59 100644 --- a/src/remote/remote_def.h +++ b/src/remote/remote_def.h @@ -81,11 +81,10 @@ const USHORT SRVR_server = 1; // server const USHORT SRVR_multi_client = 2; // multi-client server const USHORT SRVR_debug = 4; // debug run const USHORT SRVR_inet = 8; // Inet protocol -const USHORT SRVR_wnet = 16; // Wnet (named pipe) protocol (WinNT) -const USHORT SRVR_xnet = 32; // Xnet protocol (Win32) -const USHORT SRVR_non_service = 64; // not running as an NT service -const USHORT SRVR_high_priority = 128; // fork off server at high priority -const USHORT SRVR_thread_per_port = 256; // bind thread to a port -const USHORT SRVR_no_icon = 512; // tell the server not to show the icon +const USHORT SRVR_xnet = 16; // Xnet protocol (Win32) +const USHORT SRVR_non_service = 32; // not running as an NT service +const USHORT SRVR_high_priority = 64; // fork off server at high priority +const USHORT SRVR_thread_per_port = 128; // bind thread to a port +const USHORT SRVR_no_icon = 256; // tell the server not to show the icon #endif /* REMOTE_REMOTE_DEF_H */ diff --git a/src/remote/server/os/posix/inet_server.cpp b/src/remote/server/os/posix/inet_server.cpp index 2463360033..8de5a57506 100644 --- a/src/remote/server/os/posix/inet_server.cpp +++ b/src/remote/server/os/posix/inet_server.cpp @@ -41,6 +41,7 @@ #include "../common/config/config.h" #include "../common/os/fbsyslog.h" #include "../common/os/os_utils.h" +#include "../common/status.h" #include #ifdef HAVE_SYS_TYPES_H @@ -468,15 +469,12 @@ int CLIB_ROUTINE main( int argc, char** argv) fb_shutdown_callback(NULL, closePort, fb_shut_exit, port); - Firebird::LocalStatus localStatus; - Firebird::CheckStatusWrapper statusWrapper(&localStatus); - - if (!REPL_server(&statusWrapper, false, &serverClosing)) + Firebird::FbLocalStatus localStatus; + if (!REPL_server(&localStatus, false, &serverClosing)) { const char* errorMsg = "Replication server initialization error"; - gds__log_status(errorMsg, localStatus.getErrors()); + iscLogStatus(errorMsg, localStatus->getErrors()); Firebird::Syslog::Record(Firebird::Syslog::Error, errorMsg); - exit(STARTUP_ERROR); } SRVR_multi_thread(port, INET_SERVER_flag); diff --git a/src/remote/server/os/win32/property.cpp b/src/remote/server/os/win32/property.cpp index 94a3eab1d0..00ff4cf690 100644 --- a/src/remote/server/os/win32/property.cpp +++ b/src/remote/server/os/win32/property.cpp @@ -154,7 +154,7 @@ LRESULT CALLBACK GeneralPage(HWND hDlg, UINT unMsg, WPARAM wParam, LPARAM lParam lstrcpy(szText, FB_VERSION); SetDlgItemText(hDlg, IDC_STAT1, szText); - if (usServerFlags & (SRVR_inet | SRVR_wnet)) + if (usServerFlags & SRVR_inet) LoadString(hInstance, IDS_SERVERPROD_NAME, szText, sizeof(szText)); else LoadString(hInstance, IDS_LOCALPROD_NAME, szText, sizeof(szText)); @@ -241,14 +241,6 @@ static char* MakeVersionString(char* pchBuf, size_t nLen, USHORT usServerFlagMas if (p < end) *p++ = '\n'; } - if ((usServerFlagMask & SRVR_wnet) && end > p) - { - p += LoadString(hInstance, IDS_NP, p, end - p); - if (p < end) - *p++ = '\r'; - if (p < end) - *p++ = '\n'; - } if ((usServerFlagMask & SRVR_xnet) && end > p) { p += LoadString(hInstance, IDS_IPC, p, end - p); diff --git a/src/remote/server/os/win32/property.rc b/src/remote/server/os/win32/property.rc index 4223a9e1e3..ba69270590 100644 --- a/src/remote/server/os/win32/property.rc +++ b/src/remote/server/os/win32/property.rc @@ -45,7 +45,6 @@ STRINGTABLE BEGIN IDS_TCP, "TCP/IP Client Support" IDS_IPC, "Local Client Support" - IDS_NP, "NetBEUI Client Support" IDS_SUPER, "Multithreaded Server" IDS_SERVERPROD_NAME, "Firebird SQL Server for Windows" IDS_LOCALPROD_NAME, "Local Firebird Server" diff --git a/src/remote/server/os/win32/property.rh b/src/remote/server/os/win32/property.rh index 6d72d4ec36..f4cefc774c 100644 --- a/src/remote/server/os/win32/property.rh +++ b/src/remote/server/os/win32/property.rh @@ -48,7 +48,6 @@ produced by Borland Resource Workshop #define IDI_IBSVR 1007 #define IDS_TCP 11 -#define IDS_NP 12 #define IDS_IPC 13 #define IDS_SUPER 14 diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index bd753380c5..680693333f 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -103,7 +103,6 @@ #include "../remote/server/serve_proto.h" #include "../remote/server/ReplServer.h" #include "../remote/server/os/win32/window_proto.h" -#include "../remote/os/win32/wnet_proto.h" #include "../remote/server/os/win32/window.rh" #include "../remote/os/win32/xnet_proto.h" #include "../yvalve/gds_proto.h" @@ -118,7 +117,6 @@ static THREAD_ENTRY_DECLARE inet_connect_wait_thread(THREAD_ENTRY_PARAM); -static THREAD_ENTRY_DECLARE wnet_connect_wait_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE xnet_connect_wait_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE process_connection_thread(THREAD_ENTRY_PARAM); @@ -129,7 +127,6 @@ static int wait_threads(const int reason, const int mask, void* arg); static HINSTANCE hInst; static TEXT protocol_inet[128]; -static TEXT protocol_wnet[128]; static TEXT instance[MAXPATHLEN]; static USHORT server_flag = 0; static bool server_shutdown = false; @@ -224,7 +221,6 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs, SetProcessAffinityMask(GetCurrentProcess(), affinity); protocol_inet[0] = 0; - protocol_wnet[0] = 0; strcpy(instance, FB_DEFAULT_INSTANCE); @@ -295,8 +291,6 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs, port = NULL; } } - else if (server_flag & SRVR_wnet) - port = WNET_reconnect(connection_handle); else if (server_flag & SRVR_xnet) port = XNET_reconnect((ULONG_PTR) connection_handle); @@ -428,56 +422,6 @@ static THREAD_ENTRY_DECLARE inet_connect_wait_thread(THREAD_ENTRY_PARAM) } -static THREAD_ENTRY_DECLARE wnet_connect_wait_thread(THREAD_ENTRY_PARAM) -{ -/************************************** - * - * w n e t _ c o n n e c t _ w a i t _ t h r e a d - * - ************************************** - * - * Functional description - * - **************************************/ - ThreadCounter counter; - - while (!server_shutdown) - { - rem_port* port = NULL; - - try - { - port = WNET_connect(protocol_wnet, NULL, server_flag, NULL); - } - catch (const Exception& ex) - { - SimpleStatusVector<> status_vector; - ex.stuffException(status_vector); - - if (status_vector[1] == isc_net_server_shutdown) - break; - - iscLogException("WNET_connect", ex); - } - - if (port) - { - try - { - Thread::start(process_connection_thread, port, THREAD_medium); - } - catch (const Exception&) - { - gds__log("WNET: can't start worker thread, connection terminated"); - port->disconnect(NULL, NULL); - } - } - } - - return 0; -} - - static THREAD_ENTRY_DECLARE xnet_connect_wait_thread(THREAD_ENTRY_PARAM) { /************************************** @@ -557,7 +501,6 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) * **************************************/ ThreadCounter counter; - FbLocalStatus localStatus; if (server_flag & SRVR_inet) { @@ -571,18 +514,6 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) } } - if (server_flag & SRVR_wnet) - { - try - { - Thread::start(wnet_connect_wait_thread, 0, THREAD_medium); - } - catch (const Exception& ex) - { - iscLogException("WNET: can't start listener thread", ex); - } - } - if (server_flag & SRVR_xnet) { try @@ -595,7 +526,13 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) } } - REPL_server(&localStatus, false, &server_shutdown); + FbLocalStatus localStatus; + if (!REPL_server(&localStatus, false, &server_shutdown)) + { + const char* errorMsg = "Replication server initialization error"; + iscLogStatus(errorMsg, localStatus->getErrors()); + Syslog::Record(Syslog::Error, errorMsg); + } return 0; } @@ -651,13 +588,14 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) { TEXT buffer[32]; char* pp = buffer; - while (*p && *p != ' ' && (pp - buffer < sizeof(buffer) - 1)) + const char* ppend = buffer + sizeof(buffer) - 1; + while (*p && *p != ' ' && pp < ppend) { if (*p == '@') { p++; *pp++ = '\0'; - connection_handle = (HANDLE) _atoi64(buffer); + connection_handle = (HANDLE)_atoi64(buffer); pp = buffer; } else @@ -699,7 +637,7 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) *pserver_flag |= SRVR_no_icon; break; - case 'P': // Specify a port or named pipe other than the default + case 'P': // Specify a port other than the default while (*p && *p == ' ') p++; if (*p) @@ -708,23 +646,11 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) // in the future, hence I did generic code. char* pi = protocol_inet; const char* piend = protocol_inet + sizeof(protocol_inet) - 1; - char* pw = protocol_wnet; - const char* pwend = protocol_wnet + sizeof(protocol_wnet) - 1; *pi++ = '/'; - *pw++ = '\\'; - *pw++ = '\\'; - *pw++ = '.'; - *pw++ = '@'; - while (*p && *p != ' ') - { - if (pi < piend) - *pi++ = *p; - if (pw < pwend) - *pw++ = *p++; - } + while (*p && *p != ' ' && pi < piend) + *pi++ = *p++; *pi++ = '\0'; - *pw++ = '\0'; } break; @@ -766,10 +692,6 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) } break; - case 'W': - *pserver_flag |= SRVR_wnet; - break; - case 'X': *pserver_flag |= SRVR_xnet; break; @@ -791,9 +713,8 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) } } - if ((*pserver_flag & (SRVR_inet | SRVR_wnet | SRVR_xnet)) == 0) + if ((*pserver_flag & (SRVR_inet | SRVR_xnet)) == 0) { - *pserver_flag |= SRVR_wnet; *pserver_flag |= SRVR_inet; *pserver_flag |= SRVR_xnet; } diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 4f4f4d6885..7304b88dd7 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -907,7 +907,7 @@ private: GlobalPtr GlobalPortLock::mtx; #endif -class Callback FB_FINAL : public RefCntIface > +class Callback final : public RefCntIface > { public: explicit Callback(Rdb* aRdb, Rvnt* aEvent) @@ -1920,7 +1920,7 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) { if ((protocol->p_cnct_version == PROTOCOL_VERSION10 || (protocol->p_cnct_version >= PROTOCOL_VERSION11 && - protocol->p_cnct_version <= PROTOCOL_VERSION17)) && + protocol->p_cnct_version <= PROTOCOL_VERSION18)) && (protocol->p_cnct_architecture == arch_generic || protocol->p_cnct_architecture == ARCHITECTURE) && protocol->p_cnct_weight >= weight) @@ -2536,7 +2536,7 @@ static void aux_request( rem_port* port, /*P_REQ* request,*/ PACKET* send) return; } - // This buffer is used by INET and WNET transports + // This buffer is used by INET transport // to return the server identification string UCHAR buffer[BUFFER_TINY]; send->p_resp.p_resp_data.cstr_address = buffer; @@ -2937,7 +2937,7 @@ void rem_port::disconnect(PACKET* sendL, PACKET* receiveL) return; } - // For WNET and XNET we should send dummy op_disconnect packet + // For XNET we should send dummy op_disconnect packet // to wakeup async port handling events on client side. // For INET it's not necessary because INET client's async port // wakes up while server performs shutdown(socket) call on its async port. @@ -2946,7 +2946,7 @@ void rem_port::disconnect(PACKET* sendL, PACKET* receiveL) PACKET *packet = &rdb->rdb_packet; if (this->port_async) { - if ((this->port_type == rem_port::XNET) || (this->port_type == rem_port::PIPE)) + if (this->port_type == rem_port::XNET) { packet->p_operation = op_disconnect; this->port_async->send(packet); @@ -3833,7 +3833,8 @@ ISC_STATUS rem_port::execute_statement(P_OP op, P_SQLDATA* sqldata, PACKET* send if (statement->rsr_cursor || statement->rsr_batch) { - Arg::Gds(isc_dsql_cursor_open_err).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)).raise(); } InternalMessageBuffer iMsgBuffer(sqldata->p_sqldata_blr.cstr_length, @@ -3989,7 +3990,8 @@ ISC_STATUS rem_port::fetch(P_SQLDATA * sqldata, PACKET* sendL, bool scroll) sqldata->p_sqldata_blr.cstr_address, msg_length, NULL); if (!msgBuffer.metadata) - Arg::Gds(isc_dsql_cursor_open_err).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)).raise(); cursor->setDelayedOutputFormat(&status_vector, msgBuffer.metadata); check(&status_vector); diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index d7150af14e..b6ba23f236 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -396,22 +396,12 @@ int gsec(Firebird::UtilSvc* uSvc) const Firebird::string sqlRoleName(user_data->role.entered() ? user_data->role.get() : ""); Firebird::PathName serverName; - const bool useServices = !uSvc->isService(); - - switch (ISC_extract_host(databaseName, serverName, true)) - { - case ISC_PROTOCOL_TCPIP: + if (ISC_extract_host(databaseName, serverName, true) == ISC_PROTOCOL_TCPIP) serverName += ":"; - break; - case ISC_PROTOCOL_WLAN: - serverName = "\\\\" + serverName + "\\"; - break; - } + const bool useServices = !uSvc->isService(); if (!useServices) - { serverName = ""; - } Firebird::LocalStatus s; Firebird::CheckStatusWrapper statusWrapper(&s); diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index 17e842c735..cc54fe1d6f 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -452,10 +452,14 @@ int gstat(Firebird::UtilSvc* uSvc) const Switches::in_sw_tab_t* in_sw_tab = switches.findSwitch(str); if (!in_sw_tab) { - if (!str[1]) - str = "-*NONE*"; - dba_print(true, 20, SafeArg() << (str + 1)); // msg 20: unknown switch "%s" - print_help(); + if (!tddba->uSvc->isService()) // normally service manager doesn't pass illegal switches + { + if (!str[1]) + str = "-*NONE*"; + dba_print(true, 20, SafeArg() << (str + 1)); // msg 20: unknown switch "%s" + print_help(); + } + dba_error(1); // msg 1: found unknown switch break; // redundant } @@ -593,18 +597,13 @@ int gstat(Firebird::UtilSvc* uSvc) const Firebird::PathName connName = fileName; Firebird::PathName tempStr; -#ifdef WIN_NT - if (!ISC_analyze_pclan(fileName, tempStr)) -#endif + if (!ISC_analyze_tcp(fileName, tempStr)) { - if (!ISC_analyze_tcp(fileName, tempStr)) - { #ifndef NO_NFS - if (!ISC_analyze_nfs(fileName, tempStr)) + if (!ISC_analyze_nfs(fileName, tempStr)) #endif - { - fileName = connName; - } + { + fileName = connName; } } diff --git a/src/utilities/nbackup/nbackup.cpp b/src/utilities/nbackup/nbackup.cpp index f66e95b0d4..26a1542173 100644 --- a/src/utilities/nbackup/nbackup.cpp +++ b/src/utilities/nbackup/nbackup.cpp @@ -278,7 +278,7 @@ public: run_db_triggers(_run_db_triggers), direct_io(_direct_io), dbase(INVALID_HANDLE_VALUE), backup(INVALID_HANDLE_VALUE), decompress(_deco), childId(0), db_size_pages(0), - m_odsNumber(0), m_silent(false), m_printed(false) + m_odsNumber(0), m_silent(false), m_printed(false), m_flash_map(false) { // Recognition of local prefix allows to work with // database using TCP/IP loopback while reading file locally. @@ -344,6 +344,7 @@ private: USHORT m_odsNumber; bool m_silent; // are we already handling an exception? bool m_printed; // pr_error() was called to print status vector + bool m_flash_map; // clear mapping cache on attach // IO functions FB_SIZE_T read_file(FILE_HANDLE &file, void *buffer, FB_SIZE_T bufsize); @@ -1007,6 +1008,9 @@ void NBackup::attach_database() if (!run_db_triggers) dpb.insertByte(isc_dpb_no_db_triggers, 1); + if (m_flash_map) + dpb.insertByte(isc_dpb_clear_map, 1); + if (m_silent) { ISC_STATUS_ARRAY temp; @@ -1670,6 +1674,14 @@ void NBackup::restore_database(const BackupFiles& files, bool repl_seq, bool inc { close_database(); fixup_database(repl_seq, inc_rest); + + m_silent = true; + m_flash_map = true; + run_db_triggers = false; + + attach_database(); + detach_database(); + return; } if (!inc_rest || curLevel) diff --git a/src/utilities/ntrace/PluginLogWriter.h b/src/utilities/ntrace/PluginLogWriter.h index ea1be06349..da992ae584 100644 --- a/src/utilities/ntrace/PluginLogWriter.h +++ b/src/utilities/ntrace/PluginLogWriter.h @@ -53,7 +53,7 @@ struct PluginLogWriterHeader : public Firebird::MemoryHeader { }; -class PluginLogWriter FB_FINAL : +class PluginLogWriter final : public Firebird::RefCntIface >, public Firebird::IpcObject { diff --git a/src/utilities/ntrace/TracePluginImpl.h b/src/utilities/ntrace/TracePluginImpl.h index 95ebbfb2f3..2e0e337bb9 100644 --- a/src/utilities/ntrace/TracePluginImpl.h +++ b/src/utilities/ntrace/TracePluginImpl.h @@ -41,7 +41,7 @@ // Bring in off_t #include -class TracePluginImpl FB_FINAL : +class TracePluginImpl final : public Firebird::RefCntIface > { public: diff --git a/src/utilities/ntrace/traceplugin.cpp b/src/utilities/ntrace/traceplugin.cpp index 39cde97145..f77cd9fe0b 100644 --- a/src/utilities/ntrace/traceplugin.cpp +++ b/src/utilities/ntrace/traceplugin.cpp @@ -32,7 +32,7 @@ #include "TraceConfiguration.h" #include "TracePluginImpl.h" -class TraceFactoryImpl FB_FINAL : +class TraceFactoryImpl final : public Firebird::StdPlugin > { public: @@ -115,7 +115,7 @@ void registerTrace(Firebird::IPluginManager* iPlugin) } -extern "C" void FB_EXPORTED FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) { Firebird::CachedMasterInterface::set(master); registerTrace(Firebird::PluginManagerInterfacePtr()); diff --git a/src/yvalve/DistributedTransaction.cpp b/src/yvalve/DistributedTransaction.cpp index 4fb4ab06f2..b770051546 100644 --- a/src/yvalve/DistributedTransaction.cpp +++ b/src/yvalve/DistributedTransaction.cpp @@ -41,7 +41,7 @@ using namespace Why; namespace { -class DTransaction FB_FINAL : public RefCntIface > +class DTransaction final : public RefCntIface > { public: DTransaction() diff --git a/src/yvalve/MasterImplementation.cpp b/src/yvalve/MasterImplementation.cpp index e6e94b648f..db27aead27 100644 --- a/src/yvalve/MasterImplementation.cpp +++ b/src/yvalve/MasterImplementation.cpp @@ -56,7 +56,7 @@ namespace Why { // getStatus() // -class UserStatus FB_FINAL : public Firebird::DisposeIface > +class UserStatus final : public Firebird::DisposeIface > { }; @@ -145,7 +145,7 @@ FB_BOOLEAN MasterImplementation::getProcessExiting() return true; #ifdef WIN_NT - // Sometime, when user process exits not calling fb_shutdown and timer thread should + // Sometime, when user process exits not calling fb_shutdown and timer thread should // be terminated already, wait for its handle with zero timeout returns WAIT_TIMEOUT. // Usage of small non-zero timeout seems fixed such cases. diff --git a/src/yvalve/PluginManager.cpp b/src/yvalve/PluginManager.cpp index 2004c6087b..b462ab99bd 100644 --- a/src/yvalve/PluginManager.cpp +++ b/src/yvalve/PluginManager.cpp @@ -113,7 +113,7 @@ namespace bool flShutdown = false; - class ConfigParameterAccess FB_FINAL : + class ConfigParameterAccess final : public RefCntIface > { public: @@ -147,7 +147,7 @@ namespace const ConfigFile::Parameter* par; }; - class ConfigAccess FB_FINAL : + class ConfigAccess final : public RefCntIface > { public: @@ -432,7 +432,7 @@ namespace // Provides most of configuration services for plugins, // except per-database configuration in databases.conf - class ConfiguredPlugin FB_FINAL : + class ConfiguredPlugin final : public RefCntIface > { public: @@ -517,7 +517,7 @@ namespace }; // Provides per-database configuration from databases.conf. - class FactoryParameter FB_FINAL : + class FactoryParameter final : public RefCntIface > { public: @@ -770,7 +770,7 @@ namespace // Provides access to plugins of given type / name. - class PluginSet FB_FINAL : public RefCntIface > + class PluginSet final : public RefCntIface > { public: // IPluginSet implementation @@ -941,6 +941,9 @@ namespace current = rc; startModule(masterInterface); current = NULL; +#ifdef DARWIN // Plugin unload disabled in MacOS - GH-7112 + rc->addRef(); +#endif return rc; } diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index 79f2382e15..8ece68e0f5 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -204,7 +204,7 @@ private: typedef AtomicYPtr AtomicAttPtr; typedef AtomicYPtr AtomicTraPtr; -class YEvents FB_FINAL : +class YEvents final : public YHelper > { public: @@ -227,7 +227,7 @@ private: Firebird::AtomicCounter destroyed; }; -class YRequest FB_FINAL : +class YRequest final : public YHelper > { public: @@ -257,7 +257,7 @@ public: isc_req_handle* userHandle; }; -class YTransaction FB_FINAL : +class YTransaction final : public YHelper > { public: @@ -313,7 +313,7 @@ private: typedef Firebird::RefPtr NextTransaction; -class YBlob FB_FINAL : +class YBlob final : public YHelper > { public: @@ -341,7 +341,7 @@ public: AtomicTraPtr transaction; }; -class YResultSet FB_FINAL : +class YResultSet final : public YHelper > { public: @@ -376,7 +376,7 @@ public: YStatement* statement; }; -class YBatch FB_FINAL : +class YBatch final : public YHelper > { public: @@ -408,7 +408,7 @@ public: }; -class YReplicator FB_FINAL : +class YReplicator final : public YHelper > { public: @@ -443,7 +443,7 @@ private: bool input; }; -class YStatement FB_FINAL : +class YStatement final : public YHelper > { public: @@ -505,7 +505,7 @@ public: Firebird::Mutex enterMutex; }; -class YAttachment FB_FINAL : +class YAttachment final : public YHelper >, public EnterCount { @@ -593,7 +593,7 @@ public: Firebird::StatusHolder savedStatus; // Do not use raise() method of this class in yValve. }; -class YService FB_FINAL : +class YService final : public YHelper >, public EnterCount { @@ -626,7 +626,7 @@ private: bool utf8Connection; // Client talks to us using UTF8, else - system default charset }; -class Dispatcher FB_FINAL : +class Dispatcher final : public Firebird::StdPlugin > { public: @@ -655,7 +655,7 @@ private: Firebird::ICryptKeyCallback* cryptCallback; }; -class UtilInterface FB_FINAL : +class UtilInterface final : public Firebird::AutoIface > { // IUtil implementation diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 387b46c2d7..c0b4f3143d 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -318,6 +318,7 @@ const int op_subfunc_decl = 28; const int op_window_win = 29; const int op_erase = 30; // special due to optional blr_marks after blr_erase const int op_dcl_local_table = 31; +const int op_outer_map = 32; static const UCHAR // generic print formats @@ -406,7 +407,8 @@ static const UCHAR store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0}, marks[] = { op_byte, op_literal, op_line, op_verb, 0}, erase[] = { op_erase, 0}, - local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}; + local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}, + outer_map[] = { op_outer_map, 0 }; #include "../jrd/blp.h" @@ -3885,6 +3887,46 @@ static void blr_print_verb(gds_ctl* control, SSHORT level) break; } + case op_outer_map: + { + offset = blr_print_line(control, offset); + + static const char* subCodes[] = + { + nullptr, + "message", + "variable" + }; + + while ((blr_operator = control->ctl_blr_reader.getByte()) != blr_end) + { + blr_indent(control, level); + + if (blr_operator == 0 || blr_operator >= FB_NELEM(subCodes)) + blr_error(control, "*** invalid blr_outer_map sub code ***"); + + blr_format(control, "blr_outer_map_%s, ", subCodes[blr_operator]); + + switch (blr_operator) + { + case blr_outer_map_message: + case blr_outer_map_variable: + blr_print_word(control); + n = blr_print_word(control); + offset = blr_print_line(control, offset); + break; + + default: + fb_assert(false); + } + } + + // print blr_end + control->ctl_blr_reader.seekBackward(1); + blr_print_verb(control, level); + break; + } + default: fb_assert(false); break; @@ -3977,13 +4019,13 @@ static void sanitize(Firebird::string& locale) } -void FB_EXPORTED gds__default_printer(void* /*arg*/, SSHORT offset, const TEXT* line) +void API_ROUTINE_VARARG gds__default_printer(void* /*arg*/, SSHORT offset, const TEXT* line) { printf("%4d %s\n", offset, line); } -void FB_EXPORTED gds__trace_printer(void* /*arg*/, SSHORT offset, const TEXT* line) +void API_ROUTINE_VARARG gds__trace_printer(void* /*arg*/, SSHORT offset, const TEXT* line) { // Assume that line is not too long char buffer[PRETTY_BUFFER_SIZE + 10]; diff --git a/src/yvalve/gds_proto.h b/src/yvalve/gds_proto.h index 384cce4be1..8f1e91fe04 100644 --- a/src/yvalve/gds_proto.h +++ b/src/yvalve/gds_proto.h @@ -134,8 +134,10 @@ SINT64 API_ROUTINE isc_portable_integer(const UCHAR*, SSHORT); void gds__cleanup(); void gds__ulstr(char* buffer, FB_UINT64 value, const int minlen, const char filler); -void FB_EXPORTED gds__default_printer(void*, SSHORT, const TEXT*); -void FB_EXPORTED gds__trace_printer(void*, SSHORT, const TEXT*); +// These functions uses cdecl convention in Windows, so use API_ROUTINE_VARARG instead of API_ROUTINE (stdcall). +void API_ROUTINE_VARARG gds__default_printer(void*, SSHORT, const TEXT*); +void API_ROUTINE_VARARG gds__trace_printer(void*, SSHORT, const TEXT*); + #ifdef NOT_USED_OR_REPLACED void gds__print_pool(Firebird::MemoryPool*, const TEXT*, ...); #endif diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index 096c1b8f3c..c8947cbafc 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -874,7 +874,7 @@ unsigned UtilInterface::getClientVersion() } // End-user proxy for ClumpletReader & Writer -class XpbBuilder FB_FINAL : public DisposeIface > +class XpbBuilder final : public DisposeIface > { public: XpbBuilder(unsigned kind, const unsigned char* buf, unsigned len) @@ -1217,7 +1217,7 @@ IXpbBuilder* UtilInterface::getXpbBuilder(CheckStatusWrapper* status, } } -class DecFloat16 FB_FINAL : public AutoIface > +class DecFloat16 final : public AutoIface > { public: // IDecFloat16 implementation @@ -1278,7 +1278,7 @@ IDecFloat16* UtilInterface::getDecFloat16(CheckStatusWrapper* status) return &decFloat16; } -class DecFloat34 FB_FINAL : public AutoIface > +class DecFloat34 final : public AutoIface > { public: // IDecFloat34 implementation @@ -1339,7 +1339,7 @@ IDecFloat34* UtilInterface::getDecFloat34(CheckStatusWrapper* status) return &decFloat34; } -class IfaceInt128 FB_FINAL : public AutoIface > +class IfaceInt128 final : public AutoIface > { public: // IInt128 implementation diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index 03f49579ba..bded8718a7 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -128,7 +128,7 @@ static const struct { // Class-wrapper around external SQLDA. // Can be used as local variable, but do it with care -class SQLDAMetadata FB_FINAL : +class SQLDAMetadata final : public RefCntIface > { friend class SQLDAMetadataLauncher; @@ -1258,13 +1258,16 @@ namespace Why void checkCursorOpened() const { if (!statement || !statement->cursor) - Arg::Gds(isc_dsql_cursor_not_open).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-504) << + Arg::Gds(isc_dsql_cursor_err) << + Arg::Gds(isc_dsql_cursor_not_open)).raise(); } void checkCursorClosed() const { if (statement && statement->cursor) - Arg::Gds(isc_dsql_cursor_open_err).raise(); + (Arg::Gds(isc_sqlerr) << Arg::Num(-502) << + Arg::Gds(isc_dsql_cursor_open_err)).raise(); } IStatement* getInterface() @@ -2800,7 +2803,7 @@ int API_ROUTINE gds__enable_subsystem(TEXT* /*subsystem*/) namespace { - class WaitCallback FB_FINAL : + class WaitCallback final : public RefCntIface > { public: @@ -2864,7 +2867,7 @@ ISC_STATUS API_ROUTINE isc_wait_for_event(ISC_STATUS* userStatus, isc_db_handle* namespace { - class QueCallback FB_FINAL : public RefCntIface > + class QueCallback final : public RefCntIface > { public: QueCallback(FPTR_EVENT_CALLBACK aAst, void* aArg)