VX/SQL-COBOL - COBOL with embedded Rdb/SQL to PostgreSQL or Oracle
Overview
VX/SQL-COBOL is Sector7's embedded-SQL preprocessor and runtime for COBOL applications that use Oracle Rdb SQL. A drop-in replacement for Rdb's SQL$PRE, it reads your COBOL source, rewrites each EXEC SQL block into ordinary COBOL CALL "S7..." statements, and links a small runtime that talks to a modern database. The result is still COBOL: it compiles with your native COBOL compiler and runs against PostgreSQL, Oracle, SQL Server, MySQL/MariaDB, SQLite or any ODBC source on x86 OpenVMS, Linux, and macOS, without rewriting the application logic.
This matters because the PostgreSQL ECPG preprocessor and Oracle's Pro*C both process embedded SQL in C and C++ only: neither understands embedded SQL in COBOL. With Oracle Rdb reaching end of life on x86 OpenVMS and the classic COBOL client not being ported, VX/SQL-COBOL is the COBOL-aware preprocessor that closes the gap, preserving the Rdb SQLCA, SQLCODE values, cursor semantics, and CDD record includes that real Rdb programs depend on.
<!-- Webflow embed (min). Source: sql-cobol-page-technical/sqlcobol-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … -->
<div class="s7-tech-embed" data-vxsqlcobold-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="architecture">Architecture and components</h2><p>
VX/SQL-COBOL is a preprocessor plus a linked runtime library, not a daemon or a
transpiler to C. The <code>vxsql</code> preprocessor rewrites every <code>EXEC SQL</code>
block into ordinary COBOL <code>CALL "S7..."</code> statements and emits a binary
<code>.cbsql.map</code> sidecar holding host-variable types, lengths and null-indicator
metadata. Your native COBOL compiler builds the result, which links against one runtime
library per target database. The same <code>vxsql</code> binary also serves FORTRAN and
Pascal front ends; this page documents the COBOL front end.
</p><div class="callout"><strong>Not a C converter.</strong> Unlike ECPG or Oracle Pro*C, VX/SQL-COBOL keeps your
program in COBOL. The generated output contains <code>CALL "S7Connect"</code>,
<code>CALL "S7ExecParams"</code> and similar statements, never emitted C source. That is
precisely the gap it fills: ECPG and Pro*C only preprocess embedded SQL in C and C++.
</div><h6>End-to-end data flow</h6><style>.s7-tech-embed .sqlc-flow {margin: 1.5rem 0;padding: 16px;border: 1px solid var(--s7t-border);border-radius: 10px;background: rgba(255, 255, 255, 0.01);}.s7-tech-embed .sqlc-flow-phase {margin: 0 0 10px;font-family: var(--s7t-mono);color: var(--s7t-accent);font-size: 0.76em;font-weight: 700;letter-spacing: 0.08em;text-transform: uppercase;}.s7-tech-embed .sqlc-flow-chain,.s7-tech-embed .sqlc-flow-drivers {display: grid;gap: 10px;}.s7-tech-embed .sqlc-flow-chain {grid-template-columns: repeat(4, minmax(0, 1fr));}.s7-tech-embed .sqlc-flow-drivers {grid-template-columns: repeat(3, minmax(0, 1fr));margin-top: 10px;}.s7-tech-embed .sqlc-flow-card {min-width: 0;padding: 15px;border-top: 3px solid var(--s7t-accent);border-radius: 6px;background: var(--s7t-surface2);color: var(--s7t-text);}.s7-tech-embed .sqlc-flow-runtime {margin-top: 16px;padding-top: 16px;border-top: 1px solid var(--s7t-border);}.s7-tech-embed .sqlc-flow-runtime > .sqlc-flow-card {background: var(--s7t-note-bg);border: 1px solid var(--s7t-note-bd);border-top-width: 3px;}.s7-tech-embed .sqlc-flow-card strong,.s7-tech-embed .sqlc-flow-card span {display: block;}.s7-tech-embed .sqlc-flow-card strong {margin-bottom: 4px;color: var(--s7t-text);}.s7-tech-embed .sqlc-flow-card span {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}@media (max-width: 800px) {.s7-tech-embed .sqlc-flow-chain,.s7-tech-embed .sqlc-flow-drivers {grid-template-columns: 1fr;}}</style><div class="sqlc-flow" aria-label="VX/SQL-COBOL compile-time and runtime data flow"><div class="sqlc-flow-phase">Compile time</div><div class="sqlc-flow-chain"><div class="sqlc-flow-card"><strong>COBOL source</strong><span>Source containing <code>EXEC SQL</code> blocks</span></div><div class="sqlc-flow-card"><strong><code>vxsql</code> preprocessor</strong><span>Rewrites SQL blocks to <code>CALL "S7..."</code> and emits the <code>.cbsql.map</code> sidecar.</span></div><div class="sqlc-flow-card"><strong>Native COBOL compiler</strong><span><code>cobc</code>, NetCOBOL, or VSI COBOL compiles the rewritten source.</span></div><div class="sqlc-flow-card"><strong>Linked executable</strong><span>Links exactly one <code>libVXSQL_<DB></code> runtime library.</span></div></div><div class="sqlc-flow-runtime"><div class="sqlc-flow-phase">Runtime</div><div class="sqlc-flow-card"><strong>S7 call dispatch</strong><span>The executable routes each generated call through the database driver bound into its linked runtime library.</span></div><div class="sqlc-flow-drivers" aria-label="Database driver fan-out"><div class="sqlc-flow-card"><strong>PostgreSQL</strong><span><code>libpq</code></span></div><div class="sqlc-flow-card"><strong>MySQL / MariaDB</strong><span><code>mysqlclient</code></span></div><div class="sqlc-flow-card"><strong>SQLite</strong><span><code>sqlite3</code></span></div><div class="sqlc-flow-card"><strong>Oracle</strong><span><code>oci</code></span></div><div class="sqlc-flow-card"><strong>ODBC</strong><span><code>odbc</code></span></div><div class="sqlc-flow-card"><strong>SQL Server</strong><span><code>ct-lib</code></span></div></div></div></div><h6>Preprocessor pipeline</h6><p>
The preprocessor is a chain of transformation steps: it consolidates <code>COPY</code>
includes into one stream, parses each <code>EXEC SQL ... END-EXEC</code> block with a
hand-written recursive-descent COBOL parser, then emits the rewritten COBOL and the map
sidecar. <code>INCLUDE ... FROM DICTIONARY</code> (CDD) record includes are expanded into
native COBOL declarations along the way.
</p><div class="table-wrapper"><table><thead><tr><th>Component</th><th>Role</th><th>Status</th></tr></thead><tbody><tr><td>Preprocessor CLI (<code>vxsql</code>)</td><td>Argument parsing, drives the transformation pipeline.</td><td><span class="ac-live">live</span></td></tr><tr><td>Source consolidation</td><td>Inlines <code>COPY</code> includes into a single source stream.</td><td><span class="ac-live">live</span></td></tr><tr><td>ESQL parse step</td><td>Finds <code>EXEC SQL ... END-EXEC</code>, parses the SQL and host variables.</td><td><span class="ac-live">live</span></td></tr><tr><td>COBOL emit step</td><td>Rewrites each block to <code>CALL "S7..."</code> and writes the map.</td><td><span class="ac-live">live</span></td></tr><tr><td>CDD reader / emit</td><td>Expands <code>INCLUDE ... FROM DICTIONARY</code> via the CDD$ compiler-protocol interface.</td><td><span class="ac-live">live</span></td></tr><tr><td>Type bridge</td><td>COMP-3 / packed / VMS quadword conversion to and from SQL <code>NUMERIC</code> / <code>TIMESTAMP</code>.</td><td><span class="ac-live">live</span></td></tr><tr><td>Map writer / reader</td><td>Binary <code>.cbsql.map</code> sidecar of host-variable metadata.</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h6>Runtime libraries (one per database)</h6><p>
The runtime core (SQL dispatch, SQLCA population, connection and cursor managers, and the
parameter accumulator) is folded into each driver library. Every driver implements the
same internal database interface, so the COBOL side is identical regardless of the target.
</p><div class="table-wrapper"><table><thead><tr><th>Runtime library</th><th>Backend client</th><th>Status</th></tr></thead><tbody><tr><td><code>libVXSQL_PGSQL</code></td><td>libpq (PostgreSQL)</td><td><span class="ac-live">reference / fully tested</span></td></tr><tr><td><code>libVXSQL_MYSQL</code></td><td>mysqlclient (MySQL / MariaDB)</td><td><span class="ac-live">live</span></td></tr><tr><td><code>libVXSQL_SQLITE</code></td><td>sqlite3</td><td><span class="ac-live">live</span></td></tr><tr><td><code>libVXSQL_ODBC</code></td><td>unixODBC</td><td><span class="ac-live">live</span></td></tr><tr><td><code>libVXSQL_SQLSERVER</code></td><td>FreeTDS / ct-lib / ODBC</td><td><span class="ac-live">live</span></td></tr><tr><td><code>libVXSQL_ORACLE</code></td><td>OCI / Instant Client</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><div class="callout"><strong>Database is a build-and-deploy choice.</strong> A built executable links exactly
one <code>libVXSQL_<DB></code>. Within that driver family, an already-built program can
be repointed by changing its connection string. Moving to a different database engine
means relinking against a different runtime library, so pick the target as part of the
build for that deployment.
</div><h2 id="transformation">The EXEC SQL to native COBOL rewrite</h2><p>
The core of VX/SQL-COBOL is a mechanical, reviewable rewrite. Each <code>EXEC SQL</code>
block is replaced by a group of COBOL <code>CALL</code> statements against the runtime,
with the original statement preserved as a <code>*> S7</code> comment so migrated
programs can be searched for what came from preprocessing. <code>EXEC SQL INCLUDE SQLCA</code>
becomes <code>COPY "SQLCA.cpy"</code>. The rewritten SQL text is emitted as a COBOL literal
with <code>$1</code>, <code>$2</code> placeholders for the bound host variables.
</p><h6>Input: embedded SQL in COBOL</h6>
<pre> 01 HV-ACCT PIC X(10) VALUE "FT0004 ".
01 HV-STATUS PIC S9(4) COMP-5 VALUE -1.
01 HV-AMT PIC S9(9)V99 COMP-3 VALUE -7654.32.
...
EXEC SQL INCLUDE SQLCA END-EXEC.
...
EXEC SQL
CONNECT TO :DATASRC USER :DBUSR
END-EXEC.
...
EXEC SQL
INSERT INTO funds_transfer
(acct_code, beneficiary, status_code, amount)
VALUES
(:HV-ACCT, :HV-NAME, :HV-STATUS, :HV-AMT)
RETURNING id INTO :NEW-ID
END-EXEC.</pre>
<h6>Output: generated native COBOL</h6><p>
The generated program stays COBOL throughout. The excerpt below shows the
<code>COPY "SQLCA.cpy"</code> substitution, the pre-declared literal holding the rewritten
SQL, and the <code>S7StartSQL</code> / <code>S7SetSQLParams</code> / <code>S7Exec...</code> /
<code>S7EndSQL</code> accumulator sequence:
</p>
<pre> <span class="cmt">*> S7 EXEC SQL INCLUDE SQLCA END-EXEC.</span>
COPY "SQLCA.cpy".
01 SQ0001.
02 FILLER PIC X(0090) VALUE "INSERT INTO funds_transfer ("
& "acct_code , beneficiary , status_code , amount) VALUES ( "
& "$1 , $2 , $3 , $4 )".
02 FILLER PIC X(1) VALUE X"00".
...
<span class="cmt">*> S7 EXEC SQL CONNECT TO :DATASRC USER :DBUSR END-EXEC.</span>
<span class="kw">CALL</span> "S7Connect" <span class="kw">USING</span>
<span class="kw">BY REFERENCE</span> SQLCA
<span class="kw">BY REFERENCE</span> DATASRC <span class="kw">BY VALUE</span> 256
...
<span class="kw">BY REFERENCE</span> DBUSR <span class="kw">BY VALUE</span> 64
<span class="kw">END-CALL</span>.
...
<span class="kw">CALL</span> "S7StartSQL" <span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7SetResultParams" <span class="kw">USING</span>
<span class="kw">BY VALUE</span> 23 <span class="cmt">*> COBOL_TYPE_SIGNED_BINARY (NEW-ID, COMP-5)</span>
<span class="kw">BY VALUE</span> 9 <span class="kw">BY VALUE</span> 0 <span class="kw">BY VALUE</span> 0
<span class="kw">BY REFERENCE</span> NEW-ID <span class="kw">BY REFERENCE</span> S7-NO-INDICATOR
<span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7SetSQLParams" <span class="kw">USING</span>
<span class="kw">BY VALUE</span> 16 <span class="cmt">*> COBOL_TYPE_ALPHANUMERIC (HV-ACCT, PIC X)</span>
<span class="kw">BY VALUE</span> 10 <span class="kw">BY VALUE</span> 0 <span class="kw">BY VALUE</span> 0
<span class="kw">BY REFERENCE</span> HV-ACCT <span class="kw">BY REFERENCE</span> S7-NO-INDICATOR
<span class="kw">END-CALL</span>
...
<span class="kw">CALL</span> "S7SetSQLParams" <span class="kw">USING</span>
<span class="kw">BY VALUE</span> 9 <span class="cmt">*> COBOL_TYPE_SIGNED_NUMBER_PD (HV-AMT, COMP-3)</span>
<span class="kw">BY VALUE</span> 11 <span class="kw">BY VALUE</span> -2 <span class="cmt">*> scale</span> <span class="kw">BY VALUE</span> 0
<span class="kw">BY REFERENCE</span> HV-AMT <span class="kw">BY REFERENCE</span> S7-NO-INDICATOR
<span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7ExecInsertReturning" <span class="kw">USING</span>
<span class="kw">BY REFERENCE</span> SQLCA ... <span class="kw">BY REFERENCE</span> SQ0001
<span class="kw">BY VALUE</span> 4 <span class="cmt">*> nParams</span>
<span class="kw">BY VALUE</span> 1 <span class="cmt">*> nResParams</span>
<span class="kw">BY REFERENCE</span> S7-CSTR-0001 <span class="cmt">*> "id" RETURNING column</span>
<span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7EndSQL" <span class="kw">END-CALL</span>.</pre>
<h6>Why the accumulator pattern</h6><p>
COBOL has no variadic <code>CALL</code> convention, so parameters cannot be passed as a
varargs list. Instead the preprocessor emits one <code>S7SetSQLParams</code> call per input
host variable and one <code>S7SetResultParams</code> per output or <code>INTO</code> target,
each pushing <code>(type, length, scale, flags, address, indicator address)</code> onto a
thread-local list. The operation call then flushes that list to the driver.
<code>S7StartSQL</code> resets the accumulator and <code>S7EndSQL</code> clears it. The four
leading <code>BY VALUE</code> integers on each set call are the COBOL variable type code,
length, scale, and flags.
</p><h2 id="runtime-api">The S7 runtime call surface</h2><p>
Every runtime routine is exported with C linkage under an <code>S7</code> prefix and lives
inside each <code>libVXSQL_<DB></code> library. The preprocessor targets this stable call
interface, so the same generated COBOL runs against any driver. The full embedded-SQL
surface is supported: multiple named connections, static DML and singleton queries,
cursors including <code>WITH HOLD</code> and <code>WHERE CURRENT OF</code>, dynamic SQL
(<code>PREPARE</code> / <code>EXECUTE</code>, Method 2 and Method 4 with an SQLDA),
transaction control, and <code>WHENEVER</code> error handling.
</p><h6>Core routines</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Embedded SQL it implements</th><th>Status</th></tr></thead><tbody><tr><td><code>S7Connect</code> / <code>S7ConnectReset</code> / <code>S7Disconnect</code></td><td><code>CONNECT TO</code>, reset, and <code>DISCONNECT</code> for named or default connections.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7Exec</code> / <code>S7ExecParams</code> / <code>S7ExecParamsMany</code></td><td>Static SQL, parameterised DML/DDL, and array/bulk DML.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7ExecSelectIntoOne</code> / <code>S7ExecInsertReturning</code></td><td>Singleton <code>SELECT ... INTO</code> and <code>INSERT ... RETURNING ... INTO</code>.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7ExecImmediate</code></td><td>Dynamic <code>EXECUTE IMMEDIATE</code>.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7CursorDeclare</code> / <code>S7CursorOpen</code> / <code>S7CursorFetch</code> / <code>S7CursorFetchMany</code> / <code>S7CursorClose</code></td><td><code>DECLARE</code> (including <code>WITH HOLD</code>), <code>OPEN</code>, single and array <code>FETCH</code>, and <code>CLOSE</code>.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7PrepareStatement</code> / <code>S7ExecPrepared</code> / <code>S7ExecPreparedInto</code> / <code>S7DeallocatePrepared</code></td><td><code>PREPARE</code>, <code>EXECUTE</code> (Method 2), <code>EXECUTE ... INTO</code>, and <code>DEALLOCATE PREPARE</code>.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7Descriptor*</code> family + <code>S7DescribeOutput</code></td><td><code>ALLOCATE</code> / <code>SET</code> / <code>GET DESCRIPTOR</code> and <code>DESCRIBE</code> for Method 4 dynamic SQL with an SQLDA.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7StartSQL</code> / <code>S7SetSQLParams</code> / <code>S7SetResultParams</code> / <code>S7EndSQL</code></td><td>The parameter accumulator used by every parameterised statement (see the rewrite section).</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7CddApply</code></td><td>Applies a CDD record definition to the generated program.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>S7SetContext</code></td><td>Rdb <code>USING CONTEXT :ctx</code>: records and traces the DECdtm transaction id.</td><td><span class="ac-v1">partial: v1 traces the transaction id, no two-phase commit</span></td></tr></tbody></table></div><div class="callout warn"><strong>Honest scope on <code>USING CONTEXT</code>.</strong> <code>S7SetContext</code> records
and traces the 16-byte DECdtm transaction id, but the statement executes on the process's
own connection. Distributed-transaction coordination is expected to come from a single
database instance or an ACMS-style consolidation, not from a two-phase commit across
engines. Treat <code>USING CONTEXT</code> as accepted and traced, not as a full
distributed-transaction implementation.
</div><h6>Runtime environment variables</h6><p>Runtime behaviour and tracing are tuned with <code>S7_</code> environment variables:</p><div class="kw-grid"><div>S7_AUTOCOMMIT</div><div>S7_CLIENT_ENCODING</div><div>S7_FIXUP_PARAMS</div><div>S7_NOREC_CODE</div><div>S7_USRPWD_SEP</div><div>S7_VARYING_LEN_SZ_SHORT</div><div>S7_VXSQL_PROVIDER</div><div>S7_LOG_FILE</div><div>S7_LOG_LEVEL</div><div>S7_LOG_TRUNCATE</div><div>S7_LOG_ROTATE*</div><div>S7_VXSQL_TRACE_*</div></div><h2 id="datatypes">Host variables, data types, and the SQLCA</h2><p>
Every host variable in a rewritten statement carries a COBOL type code that tells the
runtime how to read or write its in-memory bytes. Packed decimal (COMP-3), binary
(COMP/COMP-5), display numerics, and VMS quadword date/time are all converted transparently
on input and output, so there is no conversion code to add in the COBOL.
</p><h6>COBOL variable type codes</h6><p>These are the leading <code>BY VALUE</code> integers on each accumulator call:</p><div class="table-wrapper"><table><thead><tr><th>Code</th><th>Type</th><th>COBOL usage</th></tr></thead><tbody><tr><td>1</td><td><code>UNSIGNED_NUMBER</code></td><td>unsigned display numeric</td></tr><tr><td>2 - 5</td><td><code>SIGNED_NUMBER_TS/TC/LS/LC</code></td><td>signed display, trailing/leading, separate/combined sign</td></tr><tr><td>8</td><td><code>UNSIGNED_NUMBER_PD</code></td><td>unsigned packed decimal (COMP-3)</td></tr><tr><td>9</td><td><code>SIGNED_NUMBER_PD</code></td><td>signed packed decimal (COMP-3)</td></tr><tr><td>16</td><td><code>ALPHANUMERIC</code></td><td><code>PIC X</code></td></tr><tr><td>22 / 23</td><td><code>UNSIGNED_BINARY</code> / <code>SIGNED_BINARY</code></td><td>COMP / COMP-5</td></tr><tr><td>24 / 28</td><td><code>JAPANESE</code> / <code>NATIONAL</code></td><td><code>PIC N</code> (DBCS) / national</td></tr><tr><td>25</td><td><code>GROUP</code></td><td>group item</td></tr><tr><td>26 / 27</td><td><code>FLOAT</code> / <code>DOUBLE</code></td><td>COMP-1 / COMP-2</td></tr></tbody></table></div><h6>Data-type conversion</h6><p>
A runtime numeric bridge converts COBOL in-memory layouts to and from the SQL
<code>NUMERIC</code> and <code>TIMESTAMP</code> types the database expects:
</p><div class="table-wrapper"><table><thead><tr><th>COBOL layout</th><th>Converted to and from</th></tr></thead><tbody><tr><td>COMP-3 packed decimal, packed integer</td><td>SQL <code>NUMERIC</code></td></tr><tr><td>Display numeric (trailing / leading, separate / combined sign)</td><td>SQL <code>NUMERIC</code></td></tr><tr><td>VMS 64-bit quadword date/time</td><td>SQL <code>TIMESTAMP</code>, <code>DATE</code>, and <code>TIME</code></td></tr><tr><td>VAX F/D/G floating (with <code>--vax-float</code>)</td><td>SQL <code>NUMERIC</code> via the same bridge</td></tr></tbody></table></div><p>
The VMS quadword epoch (base modified Julian day 0 = 1858-11-17) is handled by the bridge,
so legacy VMS timestamps land as correct SQL values without application changes.
</p><h6>Rdb-compatible SQLCA</h6><p><code>EXEC SQL INCLUDE SQLCA</code> is replaced by <code>COPY "SQLCA.cpy"</code>, a 136-byte,
fixed-layout communications area matching the Rdb SQLCA. Every <code>S7</code> call
populates it. The fields actively populated today are <code>SQLCAID</code>,
<code>SQLABC</code>, <code>SQLCODE</code>, <code>SQLSTATE</code>, <code>SQLERRMC</code>,
<code>SQLERRML</code>, and <code>SQLERRD(3)</code> (rows affected after
INSERT/UPDATE/DELETE/SELECT INTO, wired for the PostgreSQL driver).
</p><div class="callout"><strong>Known SQLCA gap.</strong> <code>SQLWARN[0..7]</code> and <code>SQLERRP</code> are
not populated today, and the other <code>SQLERRD</code> slots default to -1. Do not rely
on <code>SQLWARN</code> warning flags in migrated code.
</div><h2 id="rdb-fidelity">Rdb fidelity</h2><p>
A syntactic port is not enough: real Rdb programs depend on Rdb behaviour. VX/SQL-COBOL
preserves the pieces that legacy code branches on, so error handling, dictionary includes,
and Rdb-dialect constructs keep working against a modern database.
</p><h6>Rdb SQLCODE mapping</h6><p>
The runtime translates backend-neutral SQLSTATE values into the Oracle Rdb
<code>SQLCODE</code> values that real Rdb programs test. Code such as
<code>IF SQLCODE = -803</code> therefore keeps working after migration. For example, a
PostgreSQL duplicate-key error (SQLSTATE <code>23505</code>) is reported to COBOL as Rdb
<code>SQLCODE -803</code>.
</p><div class="table-wrapper"><table><thead><tr><th>SQLSTATE (backend-neutral)</th><th>Rdb SQLCODE</th><th>Meaning</th></tr></thead><tbody><tr><td><code>00000</code></td><td><code>0</code></td><td>success</td></tr><tr><td>no-data classes</td><td><code>100</code> (configurable)</td><td>no data / not found</td></tr><tr><td><code>22002</code></td><td><code>-305</code></td><td>null value, no indicator</td></tr><tr><td><code>21000</code>, <code>2202E</code></td><td><code>-811</code></td><td>singleton SELECT returned more than one row</td></tr><tr><td><code>23505</code></td><td><code>-803</code></td><td>duplicate key</td></tr><tr><td><code>40P01</code>, <code>40001</code></td><td><code>-913</code></td><td>deadlock</td></tr><tr><td><code>55P03</code></td><td><code>-1003</code></td><td>lock conflict</td></tr><tr><td><code>23000</code>, <code>23502</code>, <code>23503</code>, <code>23514</code></td><td><code>-1001</code></td><td>constraint / integrity failure</td></tr><tr><td><code>42P01</code></td><td><code>-1041</code></td><td>table or view does not exist</td></tr><tr><td><code>24000</code> / <code>34000</code></td><td><code>-502</code> / <code>-501</code></td><td>invalid cursor state / name</td></tr><tr><td><code>08*</code>, <code>57*</code>, <code>58*</code></td><td><code>-1020</code></td><td>connection / operator intervention</td></tr></tbody></table></div><p>
The no-record code defaults to <code>100</code> and is configurable with the
<code>--no-rec-code</code> flag or the <code>S7_NOREC_CODE</code> environment variable.
SQLSTATE values are also mirrored into the Rdb message vector.
</p><h6>Rdb message vector</h6><p><code>--rdb-msgvec</code> makes <code>INCLUDE SQLCA</code> also copy the Rdb message-vector
copybook, into which the runtime mirrors SQLSTATE. <code>--rdb-msgvec-style</code> chooses
the spelling: <code>folded</code> names for stock compilers (the default) or a
DEC-compatibility variant for the gnuCOBOL DEC dialect.
</p><h6>CDD / Repository includes</h6><p><code>INCLUDE ... FROM DICTIONARY</code> record includes are expanded into native COBOL
record declarations through the genuine CDD$ compiler-protocol interface, using Sector7's
portable dictionary library on any platform or VSI CDD/Repository (CDDSHR) on OpenVMS, with
the same code path either way. Because CDD is not being ported to x86 OpenVMS, VSI's VDD is
a drop-in replacement that fits the same workflow.
</p><div class="callout"><strong>Rdb dialect and semantics.</strong> Rdb-dialect rewrites (for example
<code>NVL</code> to <code>COALESCE</code> and <code>RDB$</code> identifier handling) are
applied for the target database, and Rdb constructs such as <code>DECLARE ALIAS</code>,
multi-schema connections, and external-file connection semantics are exercised by the
regression suite.
</div><h2 id="databases">Databases, preprocessor options, and platforms</h2><h6>Supported databases</h6><div class="table-wrapper"><table><thead><tr><th>Database</th><th>Notes</th></tr></thead><tbody><tr><td>PostgreSQL</td><td>The reference driver, most thoroughly tested: native server-side cursors, <code>WITH HOLD</code>, rows-affected in the SQLCA, schema introspection.</td></tr><tr><td>MySQL / MariaDB</td><td>Validated to PostgreSQL parity on the regression suite.</td></tr><tr><td>Microsoft SQL Server</td><td>Via FreeTDS / ct-lib or ODBC.</td></tr><tr><td>Oracle</td><td>Via the Oracle OCI client.</td></tr><tr><td>SQLite</td><td>Useful for offline development and CI.</td></tr><tr><td>Any ODBC data source</td><td>Through unixODBC.</td></tr></tbody></table></div><h6>Preprocessor options</h6><p>Common <code>vxsql</code> flags for the COBOL front end:</p><div class="table-wrapper"><table><thead><tr><th>Flag</th><th>Purpose</th></tr></thead><tbody><tr><td><code>-e, --esql</code></td><td>Preprocess for embedded SQL.</td></tr><tr><td><code>-i / -o / -s</code></td><td>Input source, generated output, and symbol file.</td></tr><tr><td><code>-m, --map</code></td><td>Emit the binary <code>.cbsql.map</code> sidecar.</td></tr><tr><td><code>-I, --copypath</code> / <code>-p</code></td><td>COPY search path; optionally preprocess included COPY files.</td></tr><tr><td><code>--cobol-compiler</code></td><td>Compiler profile: <code>gnucobol</code>, <code>netcobol</code>, <code>openvms</code>, or <code>vsi</code>.</td></tr><tr><td><code>--cobol-format</code></td><td>Source form: <code>auto</code>, <code>fixed</code>, or <code>free</code>.</td></tr><tr><td><code>-P, --picx-as</code></td><td>How <code>PIC X</code> maps: <code>char</code>, <code>charf</code>, or <code>varchar</code>.</td></tr><tr><td><code>--binary-byteorder</code></td><td>Byte order of COMP/COMP-4/BINARY host vars (COMP-5 is always native).</td></tr><tr><td><code>--vax-float</code></td><td>Treat <code>REAL*4/8</code> host vars as VAX F/D/G floating.</td></tr><tr><td><code>--no-rec-code</code></td><td>Custom SQLCODE for the no-record condition.</td></tr><tr><td><code>--rdb-msgvec</code> / <code>--rdb-msgvec-style</code></td><td>Emit the Rdb message vector and choose its name spelling.</td></tr><tr><td><code>--target-db</code></td><td>Validate generated ESQL against a target driver at preprocess time.</td></tr><tr><td><code>--allow-passthru</code></td><td>Compile unrecognised or DDL <code>EXEC SQL</code> as a runtime exec with a named warning instead of a hard error.</td></tr></tbody></table></div><div class="callout warn"><strong>Difference from <code>SQL$PRE</code>.</strong> vxsql has no target-database parser
at preprocess time, so SQL it does not recognise is validated at first execution rather
than at preprocess time. The default is a hard error on unrecognised or DDL SQL;
<code>--allow-passthru</code> downgrades that to a named warning and passes the statement
through to run-time execution.
</div><h6>Platform support</h6><div class="table-wrapper"><table><thead><tr><th>Platform</th><th>Toolchain</th><th>Status</th></tr></thead><tbody><tr><td>OpenVMS x86-64 (V9.2)</td><td>VSI C/C++ built preprocessor and runtime; native VSI COBOL</td><td><span class="ac-live">live</span></td></tr><tr><td>Linux</td><td>Fujitsu NetCOBOL or GnuCOBOL</td><td><span class="ac-live">live</span></td></tr><tr><td>macOS</td><td>Development and CI</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h2 id="compatibility">Compatibility and status</h2><p>
The table below records what is live today, what is partial, and what is out of scope, so a
migration can be planned against the real surface rather than a wish list.
</p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Status</th></tr></thead><tbody><tr><td><code>EXEC SQL</code> to native COBOL <code>CALL "S7..."</code> rewrite</td><td><span class="ac-live">live</span></td></tr><tr><td>Static DML/DQL, <code>SELECT ... INTO</code>, <code>INSERT ... RETURNING</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Cursors including <code>WITH HOLD</code> and array fetch</td><td><span class="ac-live">live</span></td></tr><tr><td>Dynamic SQL: <code>PREPARE</code> / <code>EXECUTE</code> (Method 2)</td><td><span class="ac-live">live</span></td></tr><tr><td><code>ALLOCATE</code> / <code>DESCRIBE DESCRIPTOR</code> with SQLDA (Method 4)</td><td><span class="ac-live">live</span></td></tr><tr><td>COMP-3 / packed / display numeric conversion</td><td><span class="ac-live">live</span></td></tr><tr><td>VMS quadword date/time to and from <code>TIMESTAMP</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Rdb SQLCODE / SQLSTATE compatibility map</td><td><span class="ac-live">live</span></td></tr><tr><td>Rdb message vector, <code>DECLARE ALIAS</code>, external-file connect</td><td><span class="ac-live">live</span></td></tr><tr><td>CDD <code>INCLUDE ... FROM DICTIONARY</code> (CDD$ protocol)</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SQLERRD(3)</code> rows affected</td><td><span class="ac-v1">live for PostgreSQL; -1 default elsewhere</span></td></tr><tr><td>PostgreSQL, MySQL, SQLite, ODBC, SQL Server, Oracle drivers</td><td><span class="ac-live">live</span></td></tr><tr><td>Rdb <code>USING CONTEXT</code> distributed transaction</td><td><span class="ac-v1">v1: transaction id traced, no two-phase commit</span></td></tr><tr><td><code>SQLWARN[0..7]</code>, <code>SQLERRP</code>, other <code>SQLERRD</code> slots</td><td><span class="ac-no">not populated</span></td></tr><tr><td>Cursor <code>FIRST/LAST/ABSOLUTE/RELATIVE</code> at source level</td><td><span class="ac-no">gap (parser)</span></td></tr><tr><td>Preprocess-time SQL validation against the schema (like <code>SQL$PRE</code>)</td><td><span class="ac-no">out of scope</span></td></tr></tbody></table></div><h6>Divergences from Rdb <code>SQL$PRE</code></h6><div class="table-wrapper"><table><thead><tr><th>Divergence</th><th>Reason</th></tr></thead><tbody><tr><td>No preprocess-time SQL validation against the schema</td><td>vxsql has no target-DB parser at preprocess time; unparsed SQL is validated at first execution.</td></tr><tr><td>Emits native COBOL <code>CALL "S7..."</code>, not Rdb object code</td><td>Portability: one preprocessor output compiled by any native COBOL compiler, one runtime library per database.</td></tr><tr><td>Database bound at link time, not by an Rdb attach</td><td>Each <code>libVXSQL_<DB></code> is self-contained; a cross-engine change is a relink.</td></tr><tr><td><code>USING CONTEXT</code> traces the DECdtm id but does no two-phase commit</td><td>Coordination is assumed to come from a single database instance or ACMS consolidation.</td></tr><tr><td>Only forward (<code>NEXT</code>) cursor positioning is surfaced at source level</td><td>The COBOL grammar does not yet expose <code>FIRST/LAST/ABSOLUTE/RELATIVE</code>.</td></tr></tbody></table></div><h2 id="quickref">Quick reference</h2><h6>Preprocess one program (PostgreSQL target)</h6>
<pre>vxsql -e --cobol-compiler=gnucobol -i PROG.sco -o PROG.cob -m -s PROG.sym</pre>
<p>
This emits the rewritten COBOL in <code>PROG.cob</code> plus the binary
<code>PROG.cbsql.map</code> host-variable sidecar. <code>INCLUDE SQLCA</code> becomes
<code>COPY "SQLCA.cpy"</code>.
</p><h6>S7 call skeleton for a parameterised statement</h6>
<pre><span class="kw">CALL</span> "S7StartSQL" <span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7SetSQLParams" <span class="kw">USING BY VALUE</span> <type> <len> <scale> <flags>
<span class="kw">BY REFERENCE</span> <hostvar> <indicator> <span class="kw">END-CALL</span>
<span class="cmt">*> ... one per input param ...</span>
<span class="kw">CALL</span> "S7SetResultParams" <span class="kw">USING BY VALUE</span> <type> <len> <scale> <flags>
<span class="kw">BY REFERENCE</span> <target> <indicator> <span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7ExecParams" <span class="kw">USING BY REFERENCE</span> SQLCA <conn> <tl> <sqlliteral>
<span class="kw">BY VALUE</span> <nParams> <span class="kw">END-CALL</span>
<span class="kw">CALL</span> "S7EndSQL" <span class="kw">END-CALL</span></pre>
<h6>Type codes</h6><div class="kw-grid"><div>16 = PIC X</div><div>9 = signed COMP-3</div><div>8 = unsigned COMP-3</div><div>23 = signed COMP-5</div><div>22 = unsigned binary</div><div>26 = COMP-1</div><div>27 = COMP-2</div><div>25 = group item</div></div><h6>Rdb SQLCODEs you can still branch on</h6><div class="kw-grid"><div>0 = ok</div><div>100 = no data</div><div>-803 = duplicate key</div><div>-811 = singleton >1 row</div><div>-913 = deadlock</div><div>-1001 = constraint</div><div>-1003 = lock</div><div>-1041 = no such table</div></div><h6>Change database</h6><p>
Relink the executable against a different runtime library
(<code>libVXSQL_PGSQL</code>, <code>libVXSQL_ORACLE</code>, <code>libVXSQL_SQLSERVER</code>,
and so on). Within a linked driver family, an already-built program is repointed by its
connection string.
</p><h6>Related tools</h6><ul><li><a href="/technical/vx-sqlmod-for-postgres-ecpg-oracle-pro-c">VX/SQL-MODULE</a> - Oracle Rdb SQL Module Language (SQLMOD) migration.</li><li><a href="/technical/vx-sql-fortran-with-embedded-sql-to-pro-c">VX/SQL-FORTRAN</a> - embedded SQL for FORTRAN.</li><li><a href="/technical/vx-rdml-convert-rdml-to-embedded-sql">VX/RDML</a> - convert Rdb RDML to embedded SQL.</li><li><a href="/technical/vx-cdd-vms-cdd">VX/CDD</a> - the dictionary behind <code>INCLUDE ... FROM DICTIONARY</code>.</li><li><a href="/technical/vx-cobol-vms-cobol-to-ansi-cobol">VX/COBOL</a> - OpenVMS COBOL to ANSI COBOL.</li><li><a href="/technical/gnucobol-fdec-dec-vsi-cobol-extensions">GnuCOBOL -fdec</a> - DEC/VSI COBOL extensions on Linux.</li></ul></div>