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