🚨 Oracle not porting Rdb to x86 and EOL on Itanium 12/2027 🚨
Technical Capabilities
Having trouble finding what you need?
Get in touch with us, so we can answer your specific questions directly.
Get In Touch

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_&lt;DB&gt;</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_&lt;DB&gt;</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>*&gt; 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">*&gt; S7 EXEC SQL INCLUDE SQLCA END-EXEC.</span> COPY "SQLCA.cpy". 01 SQ0001. 02 FILLER PIC X(0090) VALUE "INSERT INTO funds_transfer (" &amp; "acct_code , beneficiary , status_code , amount) VALUES ( " &amp; "$1 , $2 , $3 , $4 )". 02 FILLER PIC X(1) VALUE X"00". ... <span class="cmt">*&gt; 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">*&gt; 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">*&gt; 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">*&gt; 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">*&gt; 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">*&gt; nParams</span> <span class="kw">BY VALUE</span> 1 <span class="cmt">*&gt; nResParams</span> <span class="kw">BY REFERENCE</span> S7-CSTR-0001 <span class="cmt">*&gt; "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_&lt;DB&gt;</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_&lt;DB&gt;</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> &lt;type&gt; &lt;len&gt; &lt;scale&gt; &lt;flags&gt; <span class="kw">BY REFERENCE</span> &lt;hostvar&gt; &lt;indicator&gt; <span class="kw">END-CALL</span> <span class="cmt">*&gt; ... one per input param ...</span> <span class="kw">CALL</span> "S7SetResultParams" <span class="kw">USING BY VALUE</span> &lt;type&gt; &lt;len&gt; &lt;scale&gt; &lt;flags&gt; <span class="kw">BY REFERENCE</span> &lt;target&gt; &lt;indicator&gt; <span class="kw">END-CALL</span> <span class="kw">CALL</span> "S7ExecParams" <span class="kw">USING BY REFERENCE</span> SQLCA &lt;conn&gt; &lt;tl&gt; &lt;sqlliteral&gt; <span class="kw">BY VALUE</span> &lt;nParams&gt; <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 &gt;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>

Frequently Asked Questions

Curious about how Sector7 can facilitate your application migration? Explore our FAQs for expert insights.

What does VX/SQL-COBOL actually produce from my COBOL source?

The vxsql preprocessor rewrites each EXEC SQL block into ordinary COBOL CALL "S7..." statements and writes a binary .cbsql.map sidecar that captures host-variable types and null indicators. The result is still COBOL: it is compiled by your native COBOL compiler and linked against the VX/SQL runtime. It is not converted into C or C++.

Which databases can VX/SQL-COBOL target, and do I rebuild to switch?

PostgreSQL is the reference and most tested driver, alongside MySQL/MariaDB, Microsoft SQL Server (via ODBC or FreeTDS), Oracle (via the OCI client), SQLite, and any ODBC data source. Each build links one runtime library, so within a driver family a program repoints by changing its connection string. Moving to a different database engine means relinking against that engine's runtime library.

Why can't I just use ECPG or Oracle Pro*C on my COBOL?

Both the PostgreSQL ECPG preprocessor and Oracle's Pro*C process embedded SQL in C and C++ only. Neither handles embedded SQL in COBOL. That is the gap VX/SQL-COBOL fills: it is the COBOL-aware embedded-SQL preprocessor those tools do not provide.

Do I have to change my COBOL source, EXEC SQL, or SQLCODE checks?

No. Existing EXEC SQL blocks, CDD includes, and SQLCODE branches keep working. VX/SQL-COBOL is a drop-in replacement for Rdb's SQL$PRE, so the migration does not require a hand rewrite of the COBOL source.

How does VX/SQL-COBOL preserve Rdb error-handling behaviour?

It provides a 136-byte Rdb-compatible SQLCA carrying Rdb SQLCODE and SQLSTATE values: 0 for success, 100 for no data, and Rdb negatives such as -803, -811, and -913. Code that branches on SQLCODE behaves as it did under Rdb. For example, a PostgreSQL duplicate key (SQLSTATE 23505) is reported to COBOL as Rdb SQLCODE -803.

What happens to PACKED DECIMAL and VMS QUADWORD dates?

PACKED DECIMAL and COMP-3 are converted to ANSI NUMERIC, and VMS QUADWORD date/time to SQL TIMESTAMP, transparently on input and output. There is no conversion code to add in the COBOL.

How are INCLUDE ... FROM DICTIONARY (CDD) record includes handled?

They 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 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 in the same workflow.

Which platforms and COBOL compilers are supported?

OpenVMS x86-64 (V9.2) with native VSI COBOL, Linux with Fujitsu NetCOBOL or GnuCOBOL, and macOS for development and CI. On every platform the vxsql preprocessor and the runtime are the same, so one code base covers all three.

Transform Your Legacy Software Today!

Get In Touch
Unlock the potential of your legacy software with our expert migration services.