🚨 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-MODULE - SQLMOD to Embedded SQL

Overview

VX/SQL-MODULE regenerates Oracle Rdb SQL Module Language (.sqlmod) modules as C so they run against a modern database while preserving the exact procedure-call interface. Rdb's SQL$MOD model keeps the SQL in a module and lets the host program call its procedures as ordinary external routines; VX/SQL-MODULE keeps that extern "C" interface intact, so callers written in Fortran, COBOL, C or C++ keep the same calling sequence.

The generated modules link the same back-end-neutral VX/SQL runtime as embedded SQL, so a SQLMOD-based application can target PostgreSQL, Oracle, SQL Server, MySQL/MariaDB, SQLite or any ODBC source by configuration rather than by rebuilding. Modules are emitted either as embedded SQL for the ecpg toolchain or as pure C that calls the runtime directly.

<!-- Webflow embed (min). Source: sql-module-page-technical/sqlmod-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … --> <div class="s7-tech-embed" data-vxsqlmodd-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="module-model">The SQL module-language model</h2><p> SQL Module Language is one of the three ways Rdb applications embed SQL. Instead of scattering <code>EXEC SQL</code> through the host program, the SQL lives in a separate <code>.sqlmod</code> module: each database operation is a named <code>PROCEDURE</code> with a typed parameter list, and the host program calls those procedures as ordinary external routines. The module is the contract between application code and the database, and it is exactly this contract that VX/SQL-MODULE preserves. </p><div class="callout"><strong>Why the interface matters.</strong> Because the calling program only ever sees a set of external procedures with a fixed signature, a migrated module can change its database back end completely while the Fortran, COBOL, C or C++ caller keeps the same <code>CALL</code> sequence and is not rebuilt for the interface change. </div><h6>Module header directives</h6><p> A module opens with a header that names it, sets the SQL dialect and host language, and declares the database alias. VX/SQL-MODULE reads these to drive type mapping and naming in the generated code: </p> <pre><span class="kw">MODULE</span> ACCT_INQUIRY_MOD <span class="kw">DIALECT</span> SQL92 <span class="kw">LANGUAGE</span> C <span class="kw">AUTHORIZATION</span> BANK_USER <span class="kw">ALIAS</span> RDB$DBHANDLE <span class="kw">PARAMETER COLONS</span> <span class="kw">DECLARE ALIAS FOR FILENAME</span> banking_accounts <span class="kw">PROCEDURE</span> SET_TRANS (SQLCODE); <span class="kw">SET TRANSACTION READ WRITE</span>; </pre> <p><code>DIALECT</code> selects the SQL level (SQLV40, SQL89, SQL92 or MIA) and <code>LANGUAGE</code> selects the host-language conventions (Ada, BASIC, C, COBOL, Fortran, Pascal, PL/I or general). A line beginning with <code>--</code> is a comment. </p><h6>Procedures and reserved status parameters</h6><p> Each procedure declares a parameter list that may begin with one reserved status keyword, which becomes the way the caller reads the SQL result: </p><div class="kw-grid"><div>SQLCODE</div><div>SQLSTATE</div><div>SQLCA</div><div>SQLDA</div><div>SQLDA2</div></div><p> A leading <code>SQLCODE</code> becomes an <code>int *</code> out-parameter on the generated function; every other parameter is a host variable written as <code>:name TYPE</code> and is bound into the SQL statement. The rest of the procedure body is the SQL itself: DML, transaction control, cursors, compound blocks, or dynamic SQL. </p><h2 id="translation-pipeline">The SQLMOD to embedded SQL translation pipeline</h2><p> VX/SQL-MODULE is a self-contained C++ translator (<code>vxsqlmod</code>), not a runtime service. It reads one <code>.sqlmod</code> file and writes a C header plus one implementation file. It is a classic front-end / back-end compiler: a lexer and parser build a module AST, and an emitter lowers that AST to C, applying Rdb-to-target dialect rewrites along the way. </p><div class="table-wrapper"><table><thead><tr><th>Stage</th><th>Component</th><th>Role</th></tr></thead><tbody><tr><td>Lex</td><td>Lexer</td><td>Tokenises the <code>.sqlmod</code> source and classifies keywords.</td></tr><tr><td>Parse</td><td>Parser + expression handler</td><td>Builds the <code>Module</code> AST: header, procedures, parameter lists, SQL statements, and module-scope declarations.</td></tr><tr><td>Resolve</td><td>CDD runtime bridge</td><td>Loads <code>libvxcdd</code> at run time and resolves Domain-typed and record parameters to real C typedefs through the CDD dictionary.</td></tr><tr><td>Emit</td><td>C / ESQL emitter</td><td>Emits the <code>.h</code>, the <code>.esql</code> or <code>.psql</code> body, and the <code>.pgh</code> sidecar; contains all Rdb-to-Postgres dialect rewrites.</td></tr><tr><td>Emit</td><td>Compound emitter</td><td>Lowers <code>BEGIN [ATOMIC] ... END</code> compound statements and <code>FOR ... DO</code> loops.</td></tr><tr><td>Diagnose</td><td>Diagnostics + CLI driver</td><td>Reports parse and translation errors with <code>file:line:col</code> context and owns the exit-code contract.</td></tr></tbody></table></div><h6>Compile-time flow</h6><p> Translation runs offline, once per estate. One module in produces a header, an implementation body, and (in embedded-SQL mode) a parameter-header sidecar:</p><style>.s7-tech-embed .sqlm-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 .sqlm-flow-phase,.s7-tech-embed .sqlm-flow-label {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 .sqlm-flow-phase {margin: 0 0 10px;}.s7-tech-embed .sqlm-flow-chain,.s7-tech-embed .sqlm-flow-branches {display: grid;gap: 10px;}.s7-tech-embed .sqlm-flow-chain {grid-template-columns: repeat(4, minmax(0, 1fr));}.s7-tech-embed .sqlm-flow-branches {grid-template-columns: repeat(3, minmax(0, 1fr));margin-top: 10px;}.s7-tech-embed .sqlm-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 .sqlm-flow-side-output {border-top-color: var(--s7t-note-bd);background: var(--s7t-note-bg);}.s7-tech-embed .sqlm-flow-card .sqlm-flow-label,.s7-tech-embed .sqlm-flow-card strong,.s7-tech-embed .sqlm-flow-card span {display: block;}.s7-tech-embed .sqlm-flow-card .sqlm-flow-label {margin-bottom: 10px;}.s7-tech-embed .sqlm-flow-card strong {margin-bottom: 4px;color: var(--s7t-text);}.s7-tech-embed .sqlm-flow-card span:not(.sqlm-flow-label) {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}@media (max-width: 800px) {.s7-tech-embed .sqlm-flow-chain,.s7-tech-embed .sqlm-flow-branches {grid-template-columns: 1fr;}}</style><div class="sqlm-flow" aria-label="VX/SQL-MODULE compile-time flow"><div class="sqlm-flow-phase">Compile time</div><div class="sqlm-flow-chain"><div class="sqlm-flow-card"><strong><code>module.sqlmod</code></strong><span>Input SQL module source</span></div><div class="sqlm-flow-card"><strong>Lex</strong><span>Tokenises source and classifies keywords.</span></div><div class="sqlm-flow-card"><strong>Parse and resolve</strong><span>Builds the AST and resolves CDD Domain and record types.</span></div><div class="sqlm-flow-card"><strong>Emit</strong><span>Lowers the AST into the selected C output path.</span></div></div><div class="sqlm-flow-branches" aria-label="Generated output branches"><div class="sqlm-flow-card sqlm-flow-side-output"><span class="sqlm-flow-label">Shared output</span><strong><code>module.h</code></strong><span>Header containing <code>extern "C"</code> prototypes</span></div><div class="sqlm-flow-card"><span class="sqlm-flow-label">Embedded SQL branch</span><strong><code>module.esql</code></strong><span><code>EXEC SQL</code> C passes through <code>ecpg</code>, then <code>cc</code>. This mode also emits <code>module.pgh</code>.</span></div><div class="sqlm-flow-card"><span class="sqlm-flow-label">Direct C branch</span><strong><code>module.psql</code></strong><span>Direct runtime calls pass straight to <code>cc</code>.</span></div></div></div><h6>Run-time flow</h6><p> At run time the generated module is just linked C. The host program calls a module procedure, the procedure binds its host variables, executes against the configured back end through the VX/SQL runtime, and maps the result status back to an Rdb <code>SQLCODE</code> before returning:</p><div class="sqlm-flow" aria-label="VX/SQL-MODULE runtime flow"><div class="sqlm-flow-phase">Runtime</div><div class="sqlm-flow-chain"><div class="sqlm-flow-card"><strong>Host program</strong><span>Calls <code>module_proc(&amp;sqlcode, args...)</code>.</span></div><div class="sqlm-flow-card"><strong>Generated C</strong><span>Binds the procedure's host variables.</span></div><div class="sqlm-flow-card"><strong>VX/SQL runtime</strong><span>Executes through the configured database back end.</span></div><div class="sqlm-flow-card sqlm-flow-side-output"><strong>Return to caller</strong><span>Maps result status to Rdb <code>SQLCODE</code> and returns it to the host program.</span></div></div></div><div class="callout"><strong>The translator does not link a database.</strong> <code>vxsqlmod</code> itself links no database client; only the <em>generated</em> code links the VX/SQL runtime. The back end is chosen when the application runs, not when the module is translated. </div><h2 id="output-flavours">Two output flavours: direct API and embedded SQL</h2><p> VX/SQL-MODULE offers both lowering strategies a migration might want, selected by a single flag. Both are generated from the same <code>.sqlmod</code> source and link the same runtime, so one flavour can validate the other. </p><div class="table-wrapper"><table><thead><tr><th>Flavour</th><th>Flag</th><th>Body file</th><th>Toolchain</th></tr></thead><tbody><tr><td>Embedded SQL (default)</td><td><code>-m esql</code></td><td><code>.esql</code> (+ <code>.pgh</code>)</td><td>C containing <code>EXEC SQL</code>, processed by a second <code>ecpg</code> pass, then compiled.</td></tr><tr><td>Direct API</td><td><code>-m psql</code></td><td><code>.psql</code></td><td>Pure C that calls the VX/SQL runtime directly; compiled in one step with a normal C compiler and no embedded-SQL pre-compiler.</td></tr></tbody></table></div><div class="callout"><strong>Reaching Oracle.</strong> Oracle is reached through the runtime's Oracle OCI driver. VX/SQL-MODULE does not depend on Oracle Pro*C, so the Pro*C pre-compiler toolchain is not required. </div><h6>Direct-API output (<code>-m psql</code>)</h6><p> Given a simple insert procedure declared in the module:</p> <pre><span class="kw">PROCEDURE</span> INSERT_ACCOUNT (SQLCODE, :P_ACCOUNT_ID CHAR(5), :P_LAST_NAME CHAR(14), :P_FIRST_NAME CHAR(10), :P_BALANCE INTEGER); <span class="kw">INSERT INTO</span> ACCOUNTS (ACCOUNT_ID, LAST_NAME, FIRST_NAME, BALANCE) <span class="kw">VALUES</span> (:P_ACCOUNT_ID, :P_LAST_NAME, :P_FIRST_NAME, :P_BALANCE); </pre> <p> the direct-API emitter produces pure C. The <code>SQLCODE</code> status becomes an <code>int *</code> out-parameter, each host variable becomes a pointer parameter, Rdb <code>:name</code> placeholders are rewritten to positional <code>$1, $2, ...</code>, and the statement runs through the runtime's <code>S7ESQL*</code> calls: </p> <pre>int INSERT_ACCOUNT( int *_sqlcode, char *_P_ACCOUNT_ID, char *_P_LAST_NAME, char *_P_FIRST_NAME, int *_P_BALANCE) { char P_ACCOUNT_ID[5], P_LAST_NAME[14], P_FIRST_NAME[10]; int P_BALANCE; memcpy(P_ACCOUNT_ID, _P_ACCOUNT_ID, sizeof(P_ACCOUNT_ID)); <span class="cmt">/* ... copy remaining host vars ... */</span> P_BALANCE = *_P_BALANCE; struct sqlca_t sqlca = sqlmod_sqlca_init; S7ESQLStartSQL(); S7ESQLSetSQLParams(SQLMOD_HVAR_ALPHANUMERIC, (int)sizeof(P_ACCOUNT_ID), 0, 0u, (void*)P_ACCOUNT_ID, NULL); <span class="cmt">/* ... bind remaining params ... */</span> S7ESQLSetSQLParams(SQLMOD_HVAR_SIGNED_BINARY, (int)sizeof(P_BALANCE), 0, 0u, (void*)&amp;P_BALANCE, NULL); S7ESQLExecParams(&amp;sqlca, NULL, 0, (char*)"INSERT INTO ACCOUNTS (ACCOUNT_ID, LAST_NAME, FIRST_NAME, BALANCE) VALUES ($1,$2,$3,$4)", 4); S7ESQLEndSQL(); *_sqlcode = vxrt_postgres_to_rdb(sqlca.sqlcode); return *_sqlcode; } </pre> <h6>Embedded-SQL output (<code>-m esql</code>)</h6><p> The same procedure lowered as embedded SQL keeps the statement readable and preserves the <code>:name</code> host-variable references, wrapped in an <code>EXEC SQL</code> declare section for <code>ecpg</code>:</p> <pre>int INSERT_ACCOUNT(int *_sqlcode, char *_P_ACCOUNT_ID, <span class="cmt">/* ... */</span>) { EXEC SQL BEGIN DECLARE SECTION; char P_ACCOUNT_ID[5]; <span class="cmt">/* ... remaining host vars ... */</span> int P_BALANCE; EXEC SQL END DECLARE SECTION; memcpy(P_ACCOUNT_ID, _P_ACCOUNT_ID, sizeof(P_ACCOUNT_ID)); <span class="cmt">/* ... copy remaining host vars ... */</span> EXEC SQL INSERT INTO ACCOUNTS (ACCOUNT_ID, LAST_NAME, FIRST_NAME, BALANCE) VALUES (:P_ACCOUNT_ID, :P_LAST_NAME, :P_FIRST_NAME, :P_BALANCE); *_sqlcode = vxrt_postgres_to_rdb(sqlca.sqlcode); return *_sqlcode; } </pre> <p> Both flavours end each procedure with the identical <code>vxrt_postgres_to_rdb(sqlca.sqlcode)</code> status-mapping tail. That shared tail is what "same runtime, one flavour validates the other" means in practice: the two outputs are behaviourally equivalent by construction. </p><h2 id="parameter-mapping">Procedure and host-parameter mapping</h2><p> The core of the translation is turning a SQLMOD procedure signature into a C function signature that callers can link unchanged, and binding each host variable to the SQL statement with the right type handling. VX/SQL-MODULE maps the full range of Rdb SQLMOD parameter types, not just the common scalars. </p><h6>Host-variable and column data types</h6><div class="table-wrapper"><table><thead><tr><th>Group</th><th>Types handled</th></tr></thead><tbody><tr><td>Numeric</td><td><code>TINYINT</code>, <code>SMALLINT</code>, <code>INTEGER</code>, <code>BIGINT</code>, <code>REAL</code>, <code>FLOAT</code>, <code>DOUBLE PRECISION</code>, <code>DECIMAL</code>, <code>NUMERIC</code></td></tr><tr><td>Character</td><td><code>CHAR</code>, <code>VARCHAR</code>, <code>LONG VARCHAR</code>, <code>NCHAR</code>, <code>NCHAR VARYING</code></td></tr><tr><td>Date / time</td><td><code>DATE ANSI</code>, <code>DATE VMS</code>, <code>DATE</code>, <code>TIME</code>, <code>TIMESTAMP</code>, <code>INTERVAL</code></td></tr><tr><td>Large / structured</td><td><code>LIST OF BYTE VARYING</code>, <code>Domain</code> (resolved through CDD)</td></tr></tbody></table></div><p> Types that do not exist natively on the target are mapped to the nearest standard form: for example Rdb <code>TINYINT</code> is promoted to <code>SMALLINT</code> where the back end has no one-byte integer, and fields whose type varies by context are carried as raw bytes so no data is lost. </p><h6>CDD record parameters</h6><p> A <code>RECORD ... END RECORD</code> parameter, optionally sourced from a CDD dictionary path, becomes a C struct and is bound field by field, so a <code>SELECT ... INTO :record</code> populates every column. Domain-typed parameters resolve through CDD to real C typedefs, and VMS CDD data-type definitions can be extracted into C headers so the generated code matches the canonical record layout. Without CDD resolution, Domains fall back to an opaque pointer and individual fields are not reachable, so record-based procedures are translated with CDD enabled. </p><div class="callout"><strong>VMS quadword dates.</strong> Rdb <code>DATE VMS</code> parameters keep their VMS quadword form in the application while the runtime converts them to and from SQL <code>TIMESTAMP</code>. Dates are stored as normal timestamps in the target database, so other tools can read them, while the application continues to send and receive VMS quadwords with no code change. </div><h6>Indicators, cursors and transactions</h6><p> The translator handles indicator variables, including the Rdb <code>INDICATOR ARRAY OF n</code> record-indicator form; multi-statement procedures; static cursors (<code>DECLARE</code> / <code>OPEN</code> / <code>FETCH ... INTO</code> / <code>CLOSE</code>, including <code>WHERE CURRENT OF</code>); <code>SET TRANSACTION</code>, <code>COMMIT</code> and <code>ROLLBACK</code>; compound <code>BEGIN [ATOMIC] ... END</code> blocks and <code>FOR ... DO</code> loops; and full dynamic SQL (<code>PREPARE</code>, <code>DESCRIBE</code>, and <code>OPEN</code> / <code>FETCH ... USING DESCRIPTOR</code>). </p><div class="callout"><strong>Rdb error codes preserved.</strong> VX/SQL-MODULE provides an Rdb-compatible SQLCA and maps Rdb <code>SQLCODE</code> and <code>SQLSTATE</code>, so procedures that inspect <code>sqlca.sqlcode</code> behave as they did under Rdb. </div><h2 id="runtime-databases">Runtime and supported databases</h2><p> The generated modules call the same pluggable VX/SQL runtime that VX/SQL embedded SQL uses. That runtime provides the <code>S7ESQL*</code> statement API for direct-API output and a set of shared <code>vxrt_*</code> helpers used by both flavours, and it is where the target database is actually selected. </p><h6>Runtime entry points</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>S7ESQLStartSQL</code> / <code>S7ESQLEndSQL</code></td><td>Open and close a statement scope.</td></tr><tr><td><code>S7ESQLSetSQLParams</code></td><td>Bind an input host variable by type code, size and indicator.</td></tr><tr><td><code>S7ESQLExecParams</code></td><td>Execute a positional-parameter statement.</td></tr><tr><td><code>S7ESQLSetResultParams</code> / <code>S7ESQLExecSelectIntoOne</code></td><td>Register output host variables and run a single-row <code>SELECT ... INTO</code>.</td></tr><tr><td><code>S7ESQLCursorDeclare</code> / <code>S7ESQLCursorOpen</code> / <code>S7ESQLCursorFetchOne</code> / <code>S7ESQLCursorClose</code></td><td>Static cursor lifecycle.</td></tr><tr><td><code>S7ESQLPrepareStatement</code></td><td>Dynamic-SQL prepare.</td></tr><tr><td><code>vxrt_postgres_to_rdb</code></td><td>Map a back-end status to the Rdb <code>SQLCODE</code> the caller expects.</td></tr><tr><td><code>vxrt_db_open</code> / <code>vxrt_db_connect</code> / <code>vxrt_db_disconnect</code></td><td>Connection lifecycle for <code>ATTACH</code>, <code>CONNECT</code> and <code>DISCONNECT</code>.</td></tr><tr><td><code>vxrt_chrcpy</code> / <code>vxrt_trim_rhs</code></td><td>Language-aware character copy and fixed-width <code>CHAR</code> trimming.</td></tr></tbody></table></div><h6>Supported databases</h6><p> Because the back end is chosen at run time by configuration, the same generated modules move between databases without being rebuilt: </p><div class="table-wrapper"><table><thead><tr><th>Target</th><th>Access</th><th>Status</th></tr></thead><tbody><tr><td>PostgreSQL</td><td>Native driver</td><td><span class="ac-live">reference, most thoroughly tested</span></td></tr><tr><td>MySQL / MariaDB</td><td>Native driver</td><td><span class="ac-live">validated to PostgreSQL parity on the regression suite</span></td></tr><tr><td>Microsoft SQL Server</td><td>ODBC (unixODBC / FreeTDS)</td><td><span class="ac-live">live</span></td></tr><tr><td>Any ODBC data source</td><td>ODBC</td><td><span class="ac-live">live</span></td></tr><tr><td>Oracle</td><td>Oracle OCI client</td><td><span class="ac-live">live</span></td></tr><tr><td>SQLite</td><td>Embedded</td><td><span class="ac-live">useful for offline development and CI</span></td></tr></tbody></table></div><h6>Performance and validation</h6><ul><li>Bulk and array fetch cut network round-trips on high-volume cursors, with native server-side cursors on the PostgreSQL reference driver.</li><li>An Rdb-compatible SQLCA and Rdb <code>SQLCODE</code> / <code>SQLSTATE</code> mapping keep status-inspection logic working unchanged.</li><li>An automated unit-test harness is generated for each SQLMOD procedure, for side-by-side validation of the original module against the migrated one.</li></ul><h6>Offloading database workloads from OpenVMS</h6><p> VX/SQL-MODULE lets an OpenVMS application keep its SQLMOD calls while the database itself runs on a modern back end, on OpenVMS x86, Linux, or elsewhere. Database work moves off Rdb, which reduces load on legacy systems and extends their useful life, without changing the application's calling sequence. With Oracle Rdb reaching end of life in December 2027 and no port to x86 OpenVMS, this gives SQLMOD-based applications a supported path onto modern platforms and open back ends. </p><div class="s7-tech-embed__video" style="position:relative;padding-bottom:56.25%;height:0;margin-top:24px;"><iframe src="https://www.youtube.com/embed/yhHIyVRYkKY" title="Sector7 Automated OpenVMS Rdb to Oracle/Postgres Database Transition" style="position:absolute;inset:0;width:100%;height:100%" loading="lazy" frameborder="0" allowfullscreen></iframe></div><h2 id="compatibility">Compatibility and status</h2><p> VX/SQL-MODULE is developed in phases, and each capability is tracked against golden fixtures and customer regression corpora that must pass at 100 percent. The table below summarises what the translator handles today. </p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Status</th></tr></thead><tbody><tr><td>Module header and procedure prototypes</td><td><span class="ac-live">live</span></td></tr><tr><td>Single-statement procedures: <code>SELECT INTO</code>, <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code>, transaction control, indicators</td><td><span class="ac-live">live</span></td></tr><tr><td>Static cursors: <code>DECLARE</code>, <code>OPEN [USING]</code>, <code>FETCH ... INTO</code>, <code>CLOSE</code>, <code>WHERE CURRENT OF</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Baseline alignment: <code>RESERVING</code> strip, <code>ATTACH</code> to connect, prepare re-guard, lazy DB open, close guard</td><td><span class="ac-live">live</span></td></tr><tr><td>Full dynamic SQL: <code>PREPARE</code>, <code>DESCRIBE</code>, <code>OPEN</code> / <code>FETCH ... USING DESCRIPTOR</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Compound <code>BEGIN [ATOMIC] ... END</code> and <code>FOR ... DO</code> loops</td><td><span class="ac-live">live</span></td></tr><tr><td>Scrollable, holdable and <code>LIST</code> cursors</td><td><span class="ac-v1">v1 soon</span></td></tr><tr><td>Extended SQLDA type codes (<code>REAL</code>, <code>DOUBLE</code>, <code>DECIMAL</code>, <code>TIMESTAMP</code>, <code>INTERVAL</code>, <code>LIST</code>)</td><td><span class="ac-v2">v2</span></td></tr></tbody></table></div><h6>Rdb-to-target dialect rewrites</h6><p> The emitter applies a fixed set of source-to-source rewrites so Rdb-dialect SQL becomes acceptable to the target. These include null and conditional functions (<code>NVL</code>, <code>DECODE</code>), date handling (<code>SYSDATE</code>, <code>EXTRACT</code>, <code>DATE VMS</code> / <code>ANSI</code> qualifier removal), the Oracle outer-join <code>(+)</code> operator, concatenation and casting, <code>STARTING WITH</code>, sequence <code>NEXTVAL</code>, <code>DELETE t</code> to <code>DELETE FROM t</code>, <code>RESERVING ... FOR SHARED</code> stripping, cursor mode and <code>FOR UPDATE OF</code> handling, and schema qualifier rewriting. Long statements are re-wrapped at clause boundaries. Functions with no direct target equivalent are backed by installable SQL shim functions supplied with the toolset. </p><div class="callout"><strong>Command line.</strong> The translator binary is <code>vxsqlmod</code>. Embedded SQL (<code>-m esql</code>) is the default output; direct-API C (<code>-m psql</code>) is selected explicitly. This is distinct from the <code>vxsql</code> embedded-SQL preprocessor used by the COBOL and Fortran members of the VX/SQL toolset. </div><h2 id="quickref">Quick reference</h2><h6>Command line</h6> <pre>usage: vxsqlmod [-o OUTDIR] [-b BASENAME] [-m psql|esql] [--cdd PATH] [--cdd-types FILE] [--cdd-probe NAME] [--trace] INPUT.sqlmod <span class="cmt"># Embedded SQL (default), with CDD type resolution:</span> vxsqlmod -m esql --cdd /path/cdd.db -o build/ acct_inquiry.sqlmod <span class="cmt"># -> build/acct_inquiry.h + .esql + .pgh (then: ecpg -> cc)</span> <span class="cmt"># Direct-API C (no pre-compiler):</span> vxsqlmod -m psql -o build/ acct_inquiry.sqlmod <span class="cmt"># -> build/acct_inquiry.h + .psql (then: cc)</span> </pre> <h6>Flags</h6><div class="table-wrapper"><table><thead><tr><th>Flag</th><th>Meaning</th></tr></thead><tbody><tr><td><code>-m esql|psql</code></td><td>Output flavour. Default <code>esql</code>.</td></tr><tr><td><code>-o OUTDIR</code></td><td>Output directory (default: current directory).</td></tr><tr><td><code>-b BASENAME</code></td><td>Override output basename (default: stem of the input).</td></tr><tr><td><code>--cdd PATH</code></td><td>Open the CDD dictionary at <code>PATH</code> and resolve Domain parameters to real C typedefs.</td></tr><tr><td><code>--cdd-types FILE</code></td><td>Load a static CDD typedef registry, consulted before <code>--cdd</code>.</td></tr><tr><td><code>--cdd-probe NAME</code></td><td>Diagnostic: extract and print one CDD record definition.</td></tr><tr><td><code>--trace</code></td><td>Emit optional trace hooks around each statement.</td></tr></tbody></table></div><h6>Exit codes</h6><div class="table-wrapper"><table><thead><tr><th>Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>0</code></td><td>Translation succeeded.</td></tr><tr><td><code>1</code></td><td>Parse or translation error.</td></tr><tr><td><code>2</code></td><td>Bad command line (no input, unknown flag).</td></tr><tr><td><code>3</code></td><td>CDD load or open failure.</td></tr><tr><td><code>4</code></td><td>CDD extract failed for the probed record.</td></tr></tbody></table></div><h6>SQL statement verbs handled</h6><div class="kw-grid"><div>SELECT INTO</div><div>INSERT</div><div>UPDATE</div><div>DELETE</div><div>SET TRANSACTION</div><div>COMMIT</div><div>ROLLBACK</div><div>SAVEPOINT</div><div>OPEN CURSOR</div><div>FETCH</div><div>CLOSE CURSOR</div><div>COMPOUND</div><div>ATTACH</div><div>CONNECT</div><div>DISCONNECT</div><div>SET CONNECT</div><div>PREPARE</div><div>DESCRIBE</div><div>EXECUTE</div><div>EXECUTE IMMEDIATE</div><div>CALL</div><div>DDL</div></div><h6>Host-variable type codes emitted</h6><div class="kw-grid"><div>SQLMOD_HVAR_ALPHANUMERIC</div><div>SQLMOD_HVAR_SIGNED_BINARY</div><div>SQLMOD_HVAR_FLOAT</div><div>SQLMOD_HVAR_SIGNED_PD</div></div><h6>Related tools</h6><ul><li><a href="/technical/vx-sql-cobol-with-embedded-sql-to-pro-c-or-ecpg">VX/SQL-COBOL</a> and <a href="/technical/vx-sql-fortran-with-embedded-sql-to-pro-c">VX/SQL-FORTRAN</a> handle embedded SQL in COBOL and Fortran callers on the same VX/SQL runtime.</li><li><a href="/technical/vx-rdml-convert-rdml-to-embedded-sql">VX/RDML</a> converts Rdb RDML to embedded SQL, often part of the same Rdb estate as SQLMOD.</li><li><a href="/technical/vx-cdd-vms-cdd">VX/CDD</a> supplies the dictionary extraction that Domain and record-parameter resolution depends on.</li><li><a href="/technical/api-sql-database-interface">SQL$ (API)</a> is the database-interface API layer these generated modules build on.</li></ul></div>

Frequently Asked Questions

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

Which databases can VX/SQL-MODULE target?

PostgreSQL (the reference, most tested driver), MySQL/MariaDB, Microsoft SQL Server (via ODBC), any ODBC data source, Oracle (via the OCI client) and SQLite. The database is chosen at run time by configuration, so the same generated modules move between back ends without rebuilding.

What is the difference between the direct-API and embedded-SQL output flavours?

Direct API (-m psql) emits pure C that calls the VX/SQL runtime directly and compiles in one step with no embedded-SQL pre-compiler. Embedded SQL (-m esql) emits C containing EXEC SQL, processed by a second ecpg pass, when you want the generated code to stay as readable embedded SQL. Both come from the same .sqlmod source and link the same runtime, so each can validate the other. Embedded SQL is the default output.

Does VX/SQL-MODULE require Oracle Pro*C to reach Oracle?

No. Oracle is reached through the runtime's Oracle OCI driver. The tool does not depend on Oracle Pro*C, so you do not need the Pro*C pre-compiler toolchain.

Do my Fortran, COBOL, C or C++ programs that call the SQLMOD need to change?

No. The generated modules preserve the exact SQLMOD procedure-call interface (an extern "C" ABI), so existing callers keep the same calling sequence and are not rebuilt for the interface change.

How are CDD record parameters handled?

A RECORD ... END RECORD parameter, optionally sourced from a CDD dictionary path, becomes a C struct and is bound field by field, so SELECT ... INTO :record populates every column. Domains resolve through CDD to C typedefs, and VMS CDD data-type definitions can be extracted into C headers.

What happens to VMS quadword dates when the data lives in PostgreSQL or Oracle?

VMS quadword date and time parameters are converted to and from SQL TIMESTAMP automatically. Dates are stored as normal timestamps in the target database, so other tools can read them, while your application keeps sending and receiving VMS quadwords with no application change.

Will migrated procedures still see the same Rdb error codes?

Yes. VX/SQL-MODULE provides an Rdb-compatible SQLCA and maps Rdb SQLCODE and SQLSTATE, so procedures that inspect sqlca.sqlcode behave as they did under Rdb.

Why should we migrate off Rdb SQLMOD now?

Oracle Rdb reaches end of life in December 2027 and is not being ported to x86 OpenVMS. VX/SQL-MODULE gives SQLMOD-based applications a supported path onto modern platforms and open back ends, which reduces licensing cost, while preserving the procedure interface so the application itself is largely unchanged.

Transform Your Legacy Software Today!

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