GixSQL is an ESQL preprocessor and a set of runtime libraries that allow GnuCOBOL or sufficiently compatible enough COBOL implementations to access PostgreSQL, ODBC, MySQL, Oracle and SQLite databases.
For the latest news and current developments see here on the development blog.
It started as a (private) fork of ocesql but has been almost completely rewritten: while the semantics related to the GnuCOBOL interface are similar and several support functions have been retained, the parser, scanner and library frameworks have been developed in C++ and have a different organisation, with dynamically loadable modules as "database drivers".
The core of GixSQL has been incorporated into a more generic "preprocessing library" (libgixpp) which is used in Gix-IDE to parse COBOL files and derive some metadata used for navigation and debugging.
GixSQL is available both as an integrated module in Gix-IDE and as an external set of tools and libraries. Since version 1.0.4 GixSQL is standard C++ and no longer depends on Qt.
Since version 1.0.7 GixSQL comes also in a standalone install package, so it can also be used without installing the full-blown IDE.
Since version 1.0.13 GixSQL has been moved to its own repository.
GixSQL consists of a preprocessor (a standalone executable or a library) and a set of runtime libraries. libgixsql.dll/.so is the main library and it is the one that will be linked to your COBOL modules. The other libraries (e.g. libgisql-odbc.dll/.so) are dynamically loaded at runtime depending on the DB you have chosen in your configuration (see below). It is possible to develop additional libraries for specific DBMSs not covered in the standard installation, if desired.
Note: the "connection string" format has changed in version 1.0.8 and is incompatible with the old one. Given the amount of fixes and improvements, it is strongly recommended to upgrade to the latest version anyway.
There is no "bind" procedure in GixSQL, you will have to open a connection to a database manually in COBOL. This can be done in a number of ways, the following example uses a syntax similar to that used in Micro Focus COBOL:
ACCEPT DATASRC FROM ENVIRONMENT-VALUE.
ACCEPT DBAUTH FROM ENVIRONMENT-VALUE.
EXEC SQL
CONNECT TO :DATASRC USER :DBAUTH
END-EXEC.
In this case the two values are retrieved from the environment variables DBNAME
and DBAUTH
and passed to the CONNECT function.
DATASRC
is an alphanumeric "connection string"-style field, whose default format (see below) is basically
<dbtype>://<host>[:port][/dbname][?[opt1=val1]&...]
e.g. (if using PostgreSQL)
pgsql://localhost:5432/testdb?default_schema=myschema
In this case the username and password are provided in the second parameter (DBAUTH
in this case) and follow the format (yes, that's a dot):
username.password
You can also use other formats for your connection statements, such as
CONNECT TO :DATASOURCE USER :USERNAME USING :PASSWORD
or
CONNECT :USERNAME IDENTIFIED BY :PASSWORD USING :DATASOURCE
All the identifiers for data sources, usernames and passwords can be either COBOL variables (prefixed by a semicolon) or string literals.
Since version 1.0.16dev1 there are three supported formats for "connection strings":
-
GixSQL "standard", e.g.:
pgsql://test.test@localhost:5432/testdb1
pgsql://localhost:5432/testdb1
-
GixSQL without explicit DB driver
localhost:5432/testdb1
test.test@localhost:5432/testdb1
-
OCESQL-compatible
testdb1@localhost:5432
For cases 2) and 3), the DB/driver type is inferred by:
- setting a compile-time constant (compile-time means "when GixSQL is compiled"):
- manually in default_driver.h (e.g.
#define GIXSQL_DEFAULT_DRIVER "pgsql"
) - by using the
--with-default-driver=pgsql|odbc|mysql|none
when running the configure script (Linux/MinGW only)
- manually in default_driver.h (e.g.
- If this constant is missing or empty (default), the content of the environment variable
GIXSQL_DEFAULT_DRIVER
is used. If no default driver is available (either set by the user or provided by the compile-time configuration), and none has been specified in the user-supplied connection string, the library will return an error.
Starting with version 1.0.8 of GixSQL/Gix-IDE, it is possible to open and manage multiple connections:
CONNECT TO :db_data_source AS :db_conn_id USER :username.:opt_password [ USING password ]
or
CONNECT :username IDENTIFIED BY :password [ AT :db_conn_id ] USING :db_data_source
where db_conn_id is an identifier for your connection.
You can then use this identifier in your SQL statements, for example
EXEC SQL
CONNECT TO :DATASRC-1 AS CONN1 USER :DBUSR-1
END-EXEC.
EXEC SQL
CONNECT TO :DATASRC-2 AS CONN2 USER :DBUSR-2
END-EXEC.
EXEC SQL AT CONN1 DROP TABLE IF EXISTS TAB1 END-EXEC.
EXEC SQL AT CONN2 DROP TABLE IF EXISTS TAB2 END-EXEC.
Currently, the time BEGIN DECLARE SECTION
/END DECLARE SECTIONS
statements are processed but ignored.
You can use any COBOL field in SQL statements, e.g.:
WORKING-STORAGE SECTION.
01 T1 PIC 9(4) VALUE 0.
...
EXEC SQL AT CONN1
SELECT SUM(FLD1) INTO :T1 FROM TAB1
END-EXEC.
As a special case, if you need to declare and use field to be associated with variable length database fields (i.e. VARBINARY or VARCHAR), you can do this in one of the following ways:
-
Use the
SQL TYPE IS
clause:01 VBFLD SQL TYPE IS VARBINARY(100).
This will treat the underlying (generated) field as a
VARCHAR
, applying the appropriate padding rules.You can also generate what is commonly known as a "variable-length group". If you declare a variable as:
01 VBFLD PIC X(100) VARYING.
The following fields will be generated:
01 VBFLD. 49 VBFLD-LEN PIC 9(8) COMP-5. 49 VBFLD-ARR PIC X(100).
As it is standard practice in ESQL COBOL, "level 49" fields are used to store a
VARCHAR
/VARBINARY
field, handling separately its length in the first child data-item and the actual data in the second.The default suffixes
-LEN
and-ARR
can be customized by using the-Y
/--varying
option ingixpp
.Another
gixpp
option (-P
/--picx-as arg
) allows to choose the default handling for standardPIC X
fields (asCHAR
, with padding or asVARCHAR
, auto-trimmed when written to the database). Please note that this does not apply to variable-length groups, whose actual length is always determined by the length indicator field.(Note: you can select 2 or 4 bytes for the data item length indicator, the standard being 4. This option can currently be changed only at compile time of GixSQL, by defining the
USE_VARLEN_16
constant). -
Manually define "level 49" fields as in case 1, this is the case with some legacy code. As mentioned above, make sure that the length (2 or 4 bytes) matches your installation of GixSQL.
-
Use the
EXEC SQL VAR
syntax for a given field:WORKING-STORAGE SECTION. 01 VARD PIC X(120). .... EXEC SQL VAR VARD IS VARCHAR(120) END-EXEC.
Note: You can also use the EXEC SQL VAR
syntax to declare other SQL-typed fields, such as
EXEC SQL VAR NUM3 IS FLOAT END-EXEC.
EXEC SQL VAR NUM4 IS FLOAT(6,2) END-EXEC.
As of version 1.0.10, the supported SQL types are FLOAT
, REAL
, INTEGER
, DECIMAL
.
VARCHAR2
is supported at a syntactic level but for now is treated as a standard VARCHAR
.
Since version 1.0.10 GixSQL supports prepared statements:
WORKING-STORAGE SECTION.
...
01 DYNSTMT1 SQL TYPE IS VARCHAR(100).
PROCEDURE DIVISION.
...
MOVE 'INSERT INTO TAB1(F1, F2) VALUES(?, ?)' TO DYNSTMT1.
EXEC SQL
PREPARE SQLSTMT1 FROM :DYNSTMT1
END-EXEC.
MOVE 1 TO T1.
MOVE 2 TO T2.
EXEC SQL EXECUTE SQLSTMT1 USING :T1, :T2 END-EXEC.
EXECUTE IMMEDIATE
is also supported (as with the case above, you can use literals or host references):
EXEC SQL EXECUTE IMMEDIATE
'UPDATE TAB1 SET FLD1=FLD1+100, FLD2=FLD2+300'
END-EXEC.
Starting from version 1.0.8 it is possible to pass options to the backend "drivers", i.e., the submodules of GixSQL that interface with a specific DBMS. Only a few options are supported at the moment, but the number is likely to increase in future releases:
client_encoding
: sets the default text encoding for client connections (supported in MySQL and PostgreSQL)autocommit
: sets autocommit on or off (default: off, supported in MySQL and PostgreSQL)default_schema
: selects the default schema (supported in PostgreSQL, maps to the search_path)
Driver options are passed in the connection string, e.g.
pgsql://localhost/mydb?autocommit=on&client_encoding=UTF8&default_schema=myschema
For "boolean" options you can use either on
/off
or 1
/0
to enable or disable them.
You can set client encoding and autocommit options for a given connection in two different ways.
-
using a parameter in the datasource, as shown above, e.g.
pgsql://localhost:5432/testdb?default_schema=myschema&client_encoding=LATIN1
-
using an environment variable, e.g.
export GIXSQL_CLIENT_ENCODING=UTF8 export GIXSQL_AUTOCOMMIT=on
Notes:
- A setting specified in the datasource definition (the first method) takes precedence over a setting specified in an environment variable
- The defaults are:
- client encoding: UTF8
- autocommit: off
- Client encoding identifiers are driver-specific, i.e. you should use:
export GIXSQL_CLIENT_ENCODING=utf8mb4
for MySQLexport GIXSQL_CLIENT_ENCODING=UTF8
for PostgreSQL
Starting with version 1.0.16, GixSQL supports an improved logging engine, based on spdlog. Logging options can be controlled by using two environment variables:
-
GIXSQL_LOG_LEVEL Sets the debug level. It can be
off
,critical
,error
(default),warn
,info
,debug
ortrace
. Be aware that thetrace
option: 1) exposes a lot of internal information, including possibly sensitive data. 2) causes a slowdown of about 30%. -
GIXSQL_LOG_FILE Specifies the file to which the debug output (if any) is written. Defaults to "gixsql.log"
Pre-v1.0.18 the two environment variables were named GIXSQL_DEBUG_LOG_LEVEL
and GIXSQL_DEBUG_LOG_FILE
. The default log level was off
.
Pre-v1.0.16: you can use the environment variables GIXSQL_DEBUG_LOG-ON=1
(which defaults to 0=OFF) and GIXSQL_DEBUG_LOG
(defaults to "gixsql.log" in your temp directory). This mechanism has been removed in later versions.
You can find a sample project collection for GixSQL (TEST001.gix) in the folder %USERPROFILE%\Documents\Gix\Examples
($HOME/Documents/gix/examples
on GNU/Linux) that should have been created when you installed Gix-IDE.
Under the project directory (%USERPROFILE%\Documents\Gix\Examples\TEST001
or $HOME/Documents/gix/examples/TEST001
on GNU/Linux) there is a SQL file with a DDL query and some data you can use to run the example project.
If you want to manually precompile COBOL programs for ESQL, you can use the preprocessor binary (gixpp or gixpp.exe) you will find in the bin folder in Gix-IDE's installation directory. If you are running it from the console, make sure you have the same bin directory in your PATH
/LD_LIBRARY_PATH
since it contains some libraries that are needed by gixpp. These are the available command line options
gixpp - the ESQL preprocessor for Gix-IDE/GixSQL
Version: 1.0.18
libgixpp version: 1.0.18
Options:
-h, --help displays help on commandline options
-V, --version displays version information
-I, --copypath arg COPY file path list
-i, --infile arg input file
-o, --outfile arg output file
-s, --symfile arg output symbol file
-e, --esql preprocess for ESQL
-p, --esql-preprocess-copy ESQL: preprocess all included COPY files
-E, --esql-copy-exts arg ESQL: copy files extension list (comma-separated)
-z, --param-style arg (=d) ESQL: generated parameters style (=a|d|c
-S, --esql-static-calls ESQL: emit static calls
-g, --debug-info generate debug info
-c, --consolidate consolidate source to single-file
-k, --keep keep temporary files
-v, --verbose verbose
-d, --verbose-debug verbose (debug)
-m, --map emit map file
-C, --cobol85 emit COBOL85-compliant code
-Y, --varying arg length/data suffixes for varlen fields (=LEN,ARR)
-P, --picx-as arg (=char) text field options (=char|charf|varchar)
--no-rec-code arg custom code for "no record" condition(=nnn)
Alternatively, you can use gixsql, which is a wrapper around the gixsql binary.
When you want to build and link the resulting COBOL program from the console, remember to also add the <gix-install-dir>/share/gixsql/copy
directory to the COPY path list (it contains SQLCA) and to include libgixsql (and the appropriate path, depending on your architecture) to the compiler's command line.
First of all, you will (rather obviously) need a database server: this can be PostgreSQL, MySQL, or any other database that has an ODBC driver (DB2 works too). The DDL for this example targets PostgreSQL and may need to be modified to work with your DBMS of choice.
The files needed to run this example can be found in the "examples/test001" subfolder within the "Gix" folder that the installer created in your "Documents" folder. On Windows this should be:
C:\Users\%USERNAME%\Documents\gix\test001
On GNU/Linux (this may vary depending on your distribution) it should be
$HOME/Documents/gix/test001
Create an empty database or a schema, make sure you can access it with a given username and password, then use the DDL file test001.sql
to create the test table we will use (named emptable
).
Make sure that the preprocessor (gixpp) is in your path, then preprocess the COBOL source file:
gixpp -e -S -I. -ext ".,*.cpy,*.CPY" -i TEST001.cbl -o TEST001.cbsql
gixsql TEST001.cbl TEST001.cbsql -S -I. -ext ".,*.cpy,*.CPY"
where:
-e
: preprocess for ESQL (mandatory for preprocessing)-S
: use static calls when emitting GixSQL library calls (this is the mode to be normally used)-I.
: use the current directory for included COPY files (SQLCA is included from GixSQL's own directory)-ext ".,.cpy,.CPY"
: search for COPY files with one of these extensions (comma-separated list)-i
and-o
: input and output file paths
Another interesting option is --picx-as
: this indicates how standard PIC(X)
fields should be treated when sent to the DBMS. There are three possible options:
char
: treatPIC(X)
fields as standardCHAR
fields (preserving trailing spaces)charf
: (synonymous forchar
)varchar
: treatPIC(X)
fields asVARCHAR
fields (remove trailing spaces)
Please note that this does NOT affect variable-length groups, whose data part (by default the sub-field having an -ARR
suffix) is always output with the length specified in the corresponding length indicator field.
If all goes well, you can compile the preprocessed file TEST001.cbsql
:
cobc -x TEST001.cbsql -L <GIXSQL_LIB_DIR> -llibgixsql
The location of the actual path for GIXSQL_LIB_DIR
depends on several elements:
- The installation path for GixIDE (if you have installed the full-blown IDE) or GixSQL (if you have installed one of the GixSQL-only packages)
- The architecture your version of Gix-IDE/GixSQL was compiled for or is running on (x86 or x64)
- On Windows only: the flavour of the GnuCOBOL compiler you are running (MSVC-based or MinGW-based)
For instance, if we are on Windows x64, using GixSQL as shipped with Gix-IDE and an x64 version of GnuCOBOL, MSVC-based, the actual command will be:
cobc -x TEST001.cbsql -L "C:\Program Files\Gix-IDE\lib\x64\msvc" -llibgixsql
On Linux the packages are installed in /opt, so it would be (the "lib" prefix in the library name is not needed):
cobc -x TEST001.cbsql -L /opt/gix-ide/lib/x64/gcc -lgixsql
Now we can run the test program, we just need to set some environment variables (on Linux just use the appropriate paths and syntax):
-
Add the path for libgixsql (and the driver library, in this case libgixsql-pgsql.dll) to the path
set PATH=%PATH%;C:\Program Files\Gix-IDE\lib\x64\msvc
or (on Linux)
export PATH=$PATH:/opt/gix-ide/lib/x64/gcc
-
Set the two environment variables needed by the COBOL code itself (they can be named whatever you like, of course):
set DBNAME=pgsql://192.168.1.1:5432/testdb set DBAUTH=test.test
The first variable uses the standard format for a GixSQL connection string used in the EXEC SQL CONNECT TO
command: host:port/dbname
.
set DBNAME=MYODBCCONN
The second variable contains username and password, separated by a dot.
Run the program (TEST001.exe
on Windows) and you should get this output:
C:\Users\Test\Documents\Gix\Examples\test001>TEST001.exe
DB : 192.168.1.1:5432/testdb
USER: test.test
***************************************
open 00000000
open
fetch 00000000
department: [DEP1]
last name : [Doe ]
first name: [John ]
street : [123, Nowhere Lane ]
city : [Noplace ]
state : [NA]
zip code : [00100]
payrate : [000000000000200]
commission 000
Do you want to see the next record? (y/n)
Keep pressing 'y' to advance in the loop and display all the three records in the sample table.
GixSQL requires a C++17-compatible compiler, with support of the filesystem
namespace. This usually means GCC 8+ and Visual Studio 2017 onwards (minimum 15.7) or, better, Visual Studio 2019/2022. Building GixSQL with older compilers may require adding LIBS=-lstdc++fs
to your configure line (old gcc/lang) or LIBS=-lc++fs
(old LLVM).
After cloning or downloading the repository you will find a solution file (gixsql.sln
). You can use Visual Studio 2019 to build it, but first you probably need to check the include and library definitions for the PostgreSQL and MySQL client libraries (32/and or 64 bit). The preprocessor (gixpp
) does not have any specific dependency, while the main runtime library (libgixsql
) - starting from version 1.0.18 - depends on spdlog and fmt.
The solution file is already set up to build with libraries from vcpkg, so you can simply do
vcpkg install libpq:x64-windows libmariadb:x64-windows fmt:x64-windows-static-md spdlog:x64-windows-static-md
for x64, or in case you want to to build a 32-bit version:
vcpkg install libpq:x86-windows libmariadb:x86-windows fmt:x86-windows-static-md spdlog:x86-windows-static-md
.
All commands and packages refer to Ubuntu 20.04, You might need to adjust them depending on your distribution or environment.
You will need the development packages for some of the DBMS client libraries, e.g.:
apt install libmariadb-dev libpq-dev unixodbc-dev flex
(it is still possible to use libmysqlclient-dev, should you prefer it).
The client support code for Oracle and SQLite is integrated in GixSQL, so no additional package is needed.
Starting with version 1.0.16, you will also need the development packages for spdlog and fmt, if not already installed:
apt install libspdlog-dev libfmt-dev
You will also need a modern enough version of bison (3.7+). If you do not already have it installed and you are using Ubuntu 20.04, you can download it from Debian's repositories and install it over the current one (or you can download and compile it from here.
wget http://ftp.debian.org/debian/pool/main/b/bison/bison_3.7.5+dfsg-1_amd64.deb
and
sudo dpkg -i bison_3.7.5+dfsg-1_amd64.deb
Download the .tar.gz.package from the Releases page, e.g.
gixsql-1.0.18-xxxx.tar.gz
Untar the package (the package name and verision may obviously vary):
tar xzvf gixsql-1.0.18b-922.tar.gz
cd to the directory created by the tar command and run configure (in this case we will install to /opt/gixsql)
cd gixsql-1.0.18-922
./configure --prefix=/opt/gixsql
By default configure tries to build all the drivers. If you nly need one, you can disable the others. For instance, to build only the PostgreSQL driver:
./configure --prefix=/opt/install --disable-mysql --disable-odbc --disable-sqlite ---disable-oracle
If all goes well you can just do:
make
It should compile all the libraries, then the preprocessing library and the preprocessor. You can install with:
sudo make install
Starting with version 1.0.16, the configure
script can also be used to build with MSYS2 (MinGW32/64). You will need to install the following packages with pacman
:
pacman -S mingw-w64-x64-pkg-config autoconf make automake libtool bison flex mingw-w64-x64-gcc mingw-w64-x64-postgresql mingw-w64-x64-libmariadbclient mingw-w64-x64-unixodbc mingw-w64-x64-spdlog
Reasonably up-to-date installs of MinGW already have a correct version of bison
.
When you create a project in Gix-IDE, you are asked whether you want to enable it for ESQL preprocessing. This is not an absolute requirement. At any point you can set the project property "Preprocess for ESQL" (under "General") to "Yes". There are several properties you can configure here that affect code generation by the preprocessor:
- Preprocess COPY files: if set to "Yes" all copy files (not only those in EXEC SQL INCLUDE sections) will be parsed by the ESQL preprocessor. This is useful when you have copy files containing code that includes EXEC SQL statements.
- Use anonymous parameters: if set to "Yes", parameters in SQL statements will be represented as
?
, otherwise a numeric indicator (i.e$1
) will be used. - Emit static calls: if set to "Yes", the calls to the gixsql library functions will be emitted as static. This should be normally set to "Yes".
When you run your code from the IDE, the path for the runtime libraries needed for the DBMS you chose are automatically added to your PATH. Obviously, when you are running outside the IDE, you will have to do this manually: the runtime libraries and their dependencies reside in {gix-install-dir}\lib\{platform}\{architecture}
(platform can be either x64 or x86, architecture can either be msvc or gcc, depending on the compiler type you are using).
At the moment Gix-IDE always uses its embedded version of GixSQL. In the future this will be extended to allow for other preprocessors.
When an error occurs, in the runtime libraries or in the DBMS, GixSQL does its best to return standard-compliant SQLSTATE
and SQLCODE
error codes and messages (the "no record found" condition is a special case, see below). There are a few instances where an operation fails due to "internal" issues (logic errors, consistency checks, unsupported features, possible driver bugs, etc.). In these case GixSQL will use a custom error code and message for SQLCODE
and SQLERRM
. The table below details the internal error codes that are currently use and a brief explanation for each of them (error messages in SQLERRM
may be slightly different due to space limitation in the field).
When one of these errors occur and there isn't a self-evident explanation (e.g. your program did not properly initialize a data field used for a prepared statement) you can use the logging system (see above) to try and diagnose the problem.
ID | Number | Description |
---|---|---|
DBERR_NO_ERROR | 0 | No error occurred |
DBERR_CONNECTION_FAILED | -100 | Connection to the database has failed |
DBERR_BEGIN_TX_FAILED | -101 | A transaction could not be started |
DBERR_END_TX_FAILED | -102 | A transaction could not be ended |
DBERR_CONN_NOT_FOUND | -103 | Connection ID not found |
DBERR_CONN_RESET_FAILED | -104 | Connection close failed |
DBERR_EMPTY_QUERY | -105 | Empty query |
DBERR_SQL_ERROR | -106 | Generic SQL/driver error |
DBERR_TOO_MANY_ARGUMENTS | -107 | Too many arguments for a given function |
DBERR_TOO_FEW_ARGUMENTS | -108 | Too few arguments for a given function |
DBERR_NO_PARAMETERS | -109 | Parameters were expected but not supplied |
DBERR_CURSOR_EXISTS | -110 | The cursor already exists |
DBERR_NO_SUCH_CURSOR | -111 | There is no such cursor |
DBERR_CLOSE_CURSOR_FAILED | -112 | Cursor could not be closed |
DBERR_DISCONNECT_FAILED | -113 | Could not disconnect from the DB |
DBERR_OUT_OF_MEMORY | -114 | Out of memory |
DBERR_DECLARE_CURSOR_FAILED | -115 | Cursor declaration failed |
DBERR_OPEN_CURSOR_FAILED | -116 | Cursor could not be opened |
DBERR_FETCH_ROW_FAILED | -117 | Could not fetch a row from the cursor |
DBERR_INVALID_COLUMN_DATA | -118 | Column data is not valid |
DBERR_CURSOR_CLOSED | -119 | Cursor is closed |
DBERR_MOVE_TO_FIRST_FAILED | -120 | Cannot move to first row in a resultset |
DBERR_FIELD_COUNT_MISMATCH | -121 | Result field count does not match with the one in the query |
DBERR_NO_DATA | -122 | No data rows when data rows were expected |
DBERR_TOO_MUCH_DATA | -123 | Received more data rows than expected |
DBERR_PREPARE_FAILED | -124 | Prepare statement failed |
DBERR_CONN_INIT_ERROR | -201 | Connection initialization error |
DBERR_CONN_INVALID_DBTYPE | -202 | Invalid DB type |
Each DBMS uses a different set of placeholders for parameters in SQL statements. For instance:
- PostgreSQL uses a
$
prefix followed by a numeric index (e.g.$1
,$2
, etc.) - Oracle uses a colon (
:
) followed by an alphanumeric identifier (e.g.:param1
,:client_no
,:1
) - ODBC only accepts "anonymous" parameters whose placeholder is a question mark (
?
) - SQLite can use any of the above
GixSQL will generate the appropriate parameter placeholders for each case (using the -z
parameter in gixpp):
-z d
: parameter placeholders are generated with a dollar prefix, followed by a numerical index (e.g.$1
,$2
, etc.). This is the default.-z a
: parameter placeholders are generated as anonymous (?
)-z c
: parameter placeholders are generated with a colon prefix, followed by a numerical index (e.g.:1
,:2
, etc.)
This obviously does not apply to prepared statements, whose text is compiled (or generated) in the program code itself. This means that a statement like SELECT mycol FROM mytab WHERE mykey = ?
will succeed on ODBC (if syntactically and semantically correct, of course) and fail on PostgreSQL, because the latter will not recognize the ?
placeholder.
GixSQL can optionally convert all the parameter placeholders in a prepared statements to the ones used by the database driver. This feature can be activated in one of two ways:
- Pass the
fixup_params
options in a connection string when connecting (e.g.pgsql://localhost/mydb?fixup_params=on
) - set the
GIXSQL_FIXUP_PARAMS
toon
or1
The return values for SQLCODE
, as it is common knowledge, are not standard. Many DBMSs, when no data is found (e.g. when trying to fetch after the last row in a cursor has been reached, or when a SELECT
statements returns no results) supply 100
as a return code when flagging this condition. While this behaviour is widespread, it is far from being a standard. Oracle, for instance, uses 1403
. GixSQL, in this condition, returns a (more standardized) 02000
for SQLSTATE
and, until version 1.0.18, used to return a fixed value of 100
in SQLCODE
. From version 1.0.18 onwards, this value can be customized, to conform to the standards used by the combination of your COBOL code and DBMS of choice. A custom code for the "no record found" condition can (actually should) be set in two different places:
- While preprocessing, by using the
--no-rec-code
option and adding a numeric value (e.g.--no-rec-code 1403
). This instructs the preprocessor to correctly handle theNOT FOUND
clauses inEXEC SQL WHENEVER
statements. - At runtime, by setting the
GIXSQL_NOREC_CODE
environment variable (e.g.export GIXSQL_NOREC_CODE=1403
(Linux) orset GIXSQL_NOREC_CODE=1403
(Windows)
Starting from version 1.0.19 the autocommit feature has been re-implemented. It has been there since the first releases, but due to several reasons (essentially lack of usage and bad assumptions) it was left behind and its functionality was, in its previous incarnation, rather dubious.
The new implementation sees this feature being moved mainly from the main library (libgixsql) to the database driver libraries (e.g. libgixsql-pgsql), with only a few parameters passed around. The rationale behind this move is that different DBMSs behave in different ways, and trying to keep a unified approach would only lead to confusion (and bugs, a lot of bugs). What follows is the level of support featured by the various DB drivers.
Autocommit will basically work in three different modes:
- Native: the connection will start in the default mode used by the database
- On: a
COMMIT
will be executed after each successful statement - Off: the connection will start by opening a transaction. A new connection will be started after each
COMMIT
andROLLBACK
Driver notes:
- MySQL and ODBC (through their own drivers) directly support autocommit to be turned on or off, so the handling of this feature will be passed entirely to the native driver (e.g. libmysqlclient).
- Oracle has no concept of autocommit, just as PostgreSQL, but the first one always work in transaction mode, while transactions must be explicitly started in PostgreSQL. While autocommit (or "transaction mode" in the case of PostgreSQL) have been - and will further be - tested, these differences (and the ones above) can probably lead to different behaviours when switching a single codebase that makes heavy use of the autocommit feature to a different DB, if precautions are not taken and tests performed.
Starting from version 1.0.19 the updatable cursor feature has been re-implemented. It has been there since the first releases, but due to several reasons (essentially lack of usage and bad assumptions) it was left behind and its functionality was, in its previous incarnation, rather dubious.
The new implementation sees this feature being moved mainly from the main library (libgixsql) to the database driver libraries (e.g. libgixsql-pgsql), with only a few parameters passed around. The rationale behind this move is that different DBMSs behave in different ways, and trying to keep a unified approach would only lead to confusion (and bugs, a lot of bugs). What follows is the level of support featured by the various DB drivers.
Updatable cursors allow the update/deletion of a single cursor row after a fetch, using the clause UPDATE...WHERE CURRENT OF mycursor
after a cursor has been opened FOR UPDATE
.
Driver notes:
- Oracle directly supports updatable cursors, so the handling of this feature will be passed entirely to the native driver.
- PostgreSQL directly supports updatable cursors when native cursors are enabled - this is the default - in the GixSQL driver (libgixsql-pgsql); in this case the handling of this feature will be passed entirely to the native driver. If cursors are emulated in PostgreSQL, updatable cursors are not available.
- MySQL and SQLite have updatable cursor emulation: the table on which the cursor is opened must have a unique key that will be used for the update. This has been proven to work, but it is obviously (a lot) slower than native support.
- ODBC will defer updatable cursor handling to its own drivers: it depends on them whether updatable cursors will be available or not. As a side note the ODBC driver for MySQL (MySQL Connector/ODBC) uses the same technique illustrated above to emulate updatable cursors.
Starting from version 1.0.20a, the standard COBOL NULL indicators are supported for all drivers. You will need to declare a specific variable as a NULL indicator (type must be PIC S9(4) COMP
:
01 COM-NULL-IND PIC S9(4) COMP.
Then you case youse it like this:
EXEC SQL
SELECT PAYRATE, COM
INTO :PAYRATE, :COM:COM-NULL-IND
FROM EMPTABLE
WHERE LNAME = 'XYZ1' AND
FNAME = 'ABC1' AND
COM IS NULL
END-EXEC.
or this:
EXEC SQL
INSERT INTO EMPTABLE
(LNAME, FNAME, PAYRATE, COM)
VALUES
('XYZ1', 'ABC1', 94.00, :COM:COM-NULL-IND)
END-EXEC.
A standard value of -1
in the indicator value is retrieved on output (INSERT...
) or set on input (SELECT...
, FETCH...
) to indicate NULL fields. There is currently no support for other values of the indicator (e.g. to indicate truncation).
The PostgreSQL drivers implements two ways of dealing with cursors: "native" (the default one) and "emulated". Native cursors use the SQL language features provided by PostgreSQL (e.g. DECLARE crsr CURSOR FOR...
, FETCH NEXT
, etc.) to manage cursor operations. Emulated cursors, on the other hand, execute the SELECT
statement provided in the cursor definition and handle the resultset internally. Native cursors are generally to be preferred: they are a bit (just a bit) faster and support dynamic (updatable) behaviour. On the negative side, they must be executed inside a transaction, so you will need to add at least a START TRANSACTION
/COMMIT
pair of statements to your programs, if they are not already there.
The PostgreSQL driver has specific options that can be added to the connection string:
default_schema
: used to set the default schema(s), maps directly to PostgreSQL'ssearch_path
decode_binary
: binary (bytea
) fields are decoded when their data is read into a COBOL field. The default ison
. If, for any reason, you want to preserve the original encoding, set it tooff
in the connection string. Since encoded binary data takes more space, in this case you should make sure that your data fields are large enough to accommodate the data being read.
Starting from version v1.0.20b the PostgreSQL driver supports SSL connections. You can configure the SSL connection proerties by using the SSL-related connection parameters listed here.
A lot of features in the ODBC driver depend in turn on the underlying driver (MySQL ODBC Connector, psqlODBC, etc.) and on its settings. Tests have usually been conducted using the latest versions of the available ODBC drivers (e.g. 13.x for PostgreSQL).
For instance: when using the PostgreSQL driver (psqlODBC) you should set the "rollback on error" option to "nop", to let GixSQL handle database errors. Otherwise the ODBC driver will always roll back the transaction in case of errors and not allow your program to handle errors, roll back to savepoints, etc.
The Oracle driver currently supports connecting only with a service name (e.g. oracle://<oracle host>/<service name>/
), no SID. Other connection options and parameters will be added in the future.
The connection string for SQLite databases directly encodes the filename, e.g.:
sqlite:///home/user/mydb.db
sqlite://c:/Users/myuser/mydb.db
As usual with SQLite, if the SQLite file does not exist, it will be created. Currently no options are available for the SQLite driver.
GixSQL includes a self-contained test runner with an extendable test suite. The test runner is written in C# and runs under .Net 6.0, both under Windows and Linux. Each test case in the test suite is written to validate the correct implementation of a particular feature, at a syntactic or functional level.
For more information on the test suite and on how to run it, look here