🚨 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-FORTRAN - FORTRAN with embedded Rdb/SQL to PostgreSQL or Oracle

Overview

VX/SQL-FORTRAN is Sector7's embedded-SQL preprocessor and runtime for FORTRAN applications built on Oracle Rdb and VAX FORTRAN embedded SQL. A drop-in replacement for the Rdb SQL$PRE precompiler, it reads your FORTRAN source, rewrites each EXEC SQL ... END-EXEC block into ordinary FORTRAN calls to the portable libvxsql runtime, and leaves the surrounding program structure and logic unchanged. It is part of the VX/SQL toolset and shares a single runtime with VX/SQL-COBOL and VX/SQL-MODULE.

The preprocessed program links against one database driver chosen at link time, so the same source runs against PostgreSQL, Oracle, SQL Server, MySQL or MariaDB, SQLite, or any ODBC data source, with PostgreSQL as the reference target. There is no separate precompiler to license and no application rewrite: existing host variables, cursors, dynamic SQL, and SQLCA error handling keep working exactly as they did under Rdb.

<!-- Webflow embed (min). Source: sql-fortran-page-technical/sqlfortran-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … --> <div class="s7-tech-embed" data-vxsqlfortrand-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="preprocessing">Embedded-SQL preprocessing model</h2><p> VX/SQL-FORTRAN is a single-pass source preprocessor. It scans a FORTRAN program for <code>EXEC SQL ... END-EXEC</code> blocks and rewrites each one in place, producing one preprocessed FORTRAN source file that no longer contains any embedded SQL. Everything else in the program is passed through untouched, so the output is still your program: same subroutines, same control flow, same variable declarations. </p><p> The rewritten SQL is not translated into another precompiler's dialect. Each block becomes a sequence of ordinary FORTRAN calls into the <code>libvxsql</code> C runtime, reached through <code>iso_c_binding</code> <code>bind(c)</code> interfaces. The database work happens at run time inside that runtime library, dispatched to whichever driver you link against, rather than at precompile time against a fixed vendor client. </p><h6>What the preprocessor produces</h6><div class="table-wrapper"><table><thead><tr><th>Output</th><th>Contents</th></tr></thead><tbody><tr><td>Preprocessed FORTRAN source (<code>-o</code>)</td><td>Your program with every <code>EXEC SQL</code> block replaced by <code>libvxsql</code> calls; the original SQL is retained inline as comments.</td></tr><tr><td>Symbol file (<code>-s</code>)</td><td>The host variables and statements the preprocessor recognised, for review and tooling.</td></tr><tr><td>Map file (<code>-m</code>, optional)</td><td>A machine-readable record of variable types and flags for downstream analysis.</td></tr></tbody></table></div><div class="callout"><strong>The source stays FORTRAN.</strong> There is no intermediate C module, no second compiler in the build, and no vendor precompiler license. The preprocessed file compiles with <code>gfortran</code> or <code>ifx</code> and links against the runtime. </div><h6>Data flow</h6><p>Preprocessing runs offline, once per source file; the runtime dispatch happens per call, inside the migrated program:</p><style>.s7-tech-embed .sqlf-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 .sqlf-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 .sqlf-flow-chain,.s7-tech-embed .sqlf-flow-drivers {display: grid;gap: 10px;}.s7-tech-embed .sqlf-flow-chain {grid-template-columns: repeat(4, minmax(0, 1fr));}.s7-tech-embed .sqlf-flow-drivers {grid-template-columns: repeat(4, minmax(0, 1fr));margin-top: 10px;}.s7-tech-embed .sqlf-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 .sqlf-flow-runtime {margin-top: 16px;padding-top: 16px;border-top: 1px solid var(--s7t-border);}.s7-tech-embed .sqlf-flow-runtime > .sqlf-flow-card {background: var(--s7t-note-bg);border: 1px solid var(--s7t-note-bd);border-top-width: 3px;}.s7-tech-embed .sqlf-flow-card strong,.s7-tech-embed .sqlf-flow-card span {display: block;}.s7-tech-embed .sqlf-flow-card strong {margin-bottom: 4px;color: var(--s7t-text);}.s7-tech-embed .sqlf-flow-card span {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}@media (max-width: 800px) {.s7-tech-embed .sqlf-flow-chain,.s7-tech-embed .sqlf-flow-drivers {grid-template-columns: 1fr;}}</style><div class="sqlf-flow" aria-label="VX/SQL-FORTRAN compile-time and runtime data flow"><div class="sqlf-flow-phase">Compile time</div><div class="sqlf-flow-chain"><div class="sqlf-flow-card"><strong>FORTRAN source</strong><span>Source containing <code>EXEC SQL ... END-EXEC</code></span></div><div class="sqlf-flow-card"><strong><code>vxsql</code> preprocessing</strong><span>Rewrites SQL blocks to S7 calls, retains the SQL as <code>!S7&gt;</code> comments, and emits one <code>.f90</code> file.</span></div><div class="sqlf-flow-card"><strong>FORTRAN compile</strong><span><code>gfortran</code> or <code>ifx</code> compiles the source with the SQLCA, SQLDA, and VX/SQL modules.</span></div><div class="sqlf-flow-card"><strong>Link executable</strong><span>Links <code>libvxsql-fortran</code> plus exactly one database driver.</span></div></div><div class="sqlf-flow-runtime"><div class="sqlf-flow-phase">Runtime</div><div class="sqlf-flow-card"><strong>S7 call dispatch</strong><span>Each generated call passes through the runtime to the driver selected at link time.</span></div><div class="sqlf-flow-drivers" aria-label="Database driver fan-out"><div class="sqlf-flow-card"><strong>PostgreSQL</strong><span><code>libpq</code></span></div><div class="sqlf-flow-card"><strong>Oracle</strong><span><code>OCI</code></span></div><div class="sqlf-flow-card"><strong>ODBC</strong><span>ODBC data sources</span></div><div class="sqlf-flow-card"><strong>SQLite</strong><span><code>sqlite3</code></span></div></div></div></div><h6>Traceability in the generated code</h6><p> The original SQL is never discarded. Each rewritten block is preceded by the source text as comments: the first line is marked <code>!S7&gt;</code> and continuations <code>!S7</code>, so a generated program is fully grep-searchable for the SQL it came from. The preprocessor also injects <code>use sqlca_module</code>, <code>use sqlda_module</code>, and <code>use vxsql_module</code>, and emits a <code>call sqlca_reset()</code> near the top of the main program before any database call runs. </p><h2 id="translation">The FORTRAN to target translation model</h2><p> Every recognised statement is lowered to the same shape: a batch of parameter-accumulator calls that push host variables and result targets onto a thread-local list, followed by one call that flushes the batch and runs the statement. FORTRAN has no variadic call convention, so this accumulator pattern is how a variable number of host variables is passed to the runtime. The examples below use a banking demo schema; the syntax is exactly what the preprocessor emits. </p><h6>Before: embedded SQL in FORTRAN</h6> <pre> <span class="kw">EXEC SQL</span> INCLUDE SQLCA <span class="kw">END-EXEC</span> ... <span class="kw">EXEC SQL</span> CONNECT TO :datasrc USER :dbuser <span class="kw">END-EXEC</span> ... <span class="kw">EXEC SQL</span> SELECT COUNT(*) INTO :row_ct FROM account_master <span class="kw">END-EXEC</span></pre> <h6>After: preprocessed FORTRAN</h6> <pre> <span class="kw">use</span> sqlca_module <span class="kw">use</span> vxsql_module <span class="kw">character</span>(len=4096, kind=c_char), target :: s7_sql_buf <span class="kw">call</span> sqlca_reset() <span class="cmt">!S7&gt; EXEC SQL CONNECT TO :datasrc USER :dbuser</span> <span class="cmt">!S7 END-EXEC</span> <span class="kw">if</span> (S7Connect(sqlca, &amp; c_loc(datasrc), 64_c_int, &amp; c_null_ptr, 0_c_int, &amp; <span class="cmt">! connection-id</span> c_null_ptr, 0_c_int, &amp; <span class="cmt">! dbname</span> c_loc(dbuser), 64_c_int, &amp; <span class="cmt">! username</span> c_null_ptr, 0_c_int) /= 0) <span class="kw">continue</span> <span class="cmt">! password</span> <span class="cmt">!S7&gt; EXEC SQL SELECT COUNT(*) INTO :row_ct FROM account_master</span> <span class="cmt">!S7 END-EXEC</span> <span class="kw">if</span> (S7StartSQL() /= 0) <span class="kw">continue</span> <span class="kw">if</span> (S7SetResultParams(23_c_int, 9_c_int, 0_c_int, 0_c_int32_t, c_loc(row_ct), c_null_ptr) /= 0) <span class="kw">continue</span> s7_sql_buf = <span class="str">'SELECT COUNT(*) FROM account_master'</span> // c_null_char <span class="kw">if</span> (S7ExecSelectIntoOne(sqlca, c_null_ptr, 0_c_int, &amp; c_loc(s7_sql_buf), 0_c_int, 1_c_int) /= 0) <span class="kw">continue</span> <span class="kw">if</span> (S7EndSQL() /= 0) <span class="kw">continue</span></pre> <p> The pattern is always <code>S7StartSQL</code>, then one accumulator call per host variable (<code>S7SetSQLParams</code> for inputs, <code>S7SetResultParams</code> for <code>INTO</code> targets), then the operation call, then <code>S7EndSQL</code>. The <code>INTO</code> list is stripped out of the SQL string and rebuilt as result parameters, so the text sent to the database is a clean query. </p><h6>Host variables become positional placeholders</h6><p> Host variables inside the SQL text are rewritten to positional placeholders (<code>$1</code>, <code>$2</code>, and so on), and their addresses are pushed as input parameters in order. A parameterised <code>INSERT</code> emits: </p> <pre> <span class="kw">if</span> (S7StartSQL() /= 0) <span class="kw">continue</span> <span class="kw">if</span> (S7SetSQLParams(16_c_int, 10_c_int, 0_c_int, 0_c_int32_t, c_loc(acct_code), c_null_ptr) /= 0) <span class="kw">continue</span> <span class="kw">if</span> (S7SetSQLParams(16_c_int, 50_c_int, 0_c_int, 0_c_int32_t, c_loc(acct_name), c_null_ptr) /= 0) <span class="kw">continue</span> <span class="kw">if</span> (S7SetSQLParams(23_c_int, 4_c_int, 0_c_int, 0_c_int32_t, c_loc(branch_id), c_null_ptr) /= 0) <span class="kw">continue</span> s7_sql_buf = <span class="str">'INSERT INTO account_master (acct_code, acct_name, branch_id) &amp;</span> <span class="str">&amp;VALUES ($1, $2, $3)'</span> // c_null_char <span class="kw">if</span> (S7ExecParams(sqlca, c_null_ptr, 0_c_int, c_loc(s7_sql_buf), 3_c_int) /= 0) <span class="kw">continue</span> <span class="kw">if</span> (S7EndSQL() /= 0) <span class="kw">continue</span></pre> <div class="callout"><strong>Parameterised, not string-built.</strong> Values are bound as parameters through the runtime rather than concatenated into the SQL text, so migrated code keeps the safety and query-plan reuse of proper bind variables. </div><h2 id="host-variables">Host-variable mapping</h2><p> The preprocessor recognises the standard FORTRAN scalar types as host variables and maps each to a runtime type identifier and a length. Both the modern <code>kind</code>-parameterised spellings and the classic <code>*n</code> spellings are accepted, so source written against either style migrates without edits. </p><div class="table-wrapper"><table><thead><tr><th>FORTRAN declaration</th><th>Also accepted</th><th>Runtime type</th></tr></thead><tbody><tr><td><code>integer(c_int16_t)</code></td><td><code>integer*2</code></td><td>signed binary (id 23)</td></tr><tr><td><code>integer(c_int)</code></td><td><code>integer*4</code></td><td>signed binary (id 23)</td></tr><tr><td><code>integer(c_int64_t)</code></td><td><code>integer*8</code></td><td>signed binary (id 23)</td></tr><tr><td><code>real(c_float)</code></td><td><code>real*4</code></td><td>float (id 26)</td></tr><tr><td><code>real(c_double)</code></td><td><code>real*8</code>, <code>double precision</code></td><td>double (id 27)</td></tr><tr><td><code>character(len=N)</code></td><td><code>character*N</code></td><td>alphanumeric (id 16)</td></tr></tbody></table></div><p> The type identifiers are shared with the COBOL front end so that one runtime serves both languages. The length passed to the runtime is a digit count rather than a byte count, to match the shared parameter convention: a 1-byte integer maps to 2, a 2-byte to 4, a 4-byte to 9, and an 8-byte to 18. </p><h6>NULL indicators</h6><p> NULL indicator variables are supported in both forms. The juxtaposed form <code>:value :indicator</code> and the keyword form <code>:value INDICATOR :indicator</code> are both recognised, and the indicator address is carried through the same accumulator call as the value. </p><h6>SQLCA and SQLDA</h6><p><code>EXEC SQL INCLUDE SQLCA END-EXEC</code> and <code>INCLUDE SQLDA</code> are replaced by <code>use sqlca_module</code> and <code>use sqlda_module</code>: the structures are provided by the runtime bindings rather than inlined into your source. The <code>sqlca_t</code> layout is wire-compatible with the C runtime's SQLCA and is 136 bytes. Unqualified references to <code>SQLCODE</code>, <code>SQLERRMC</code>, and <code>SQLERRD</code> in source are rewritten to the qualified module fields on emit: </p><div class="table-wrapper"><table><thead><tr><th>Source reference</th><th>Rewritten to</th><th>Meaning</th></tr></thead><tbody><tr><td><code>SQLCODE</code></td><td><code>sqlca%sqlcode</code></td><td>Result status of the last statement.</td></tr><tr><td><code>SQLERRMC</code></td><td><code>sqlca%sqlerrm%sqlerrmc</code></td><td>Error message text (70 characters).</td></tr><tr><td><code>SQLERRD(3)</code></td><td><code>sqlca%sqlerrd(3)</code></td><td>Rows affected by the last statement.</td></tr></tbody></table></div><p> The fields populated at run time 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>. <code>WHENEVER NOT FOUND</code> and <code>WHENEVER SQLERROR GO TO</code> are recognised and lowered to status tests against these fields. </p><h2 id="runtime-api">The libvxsql runtime API</h2><p> The preprocessed program talks to a single C ABI, exposed to FORTRAN as <code>bind(c)</code> interface blocks in <code>vxsql_module.f90</code>. Every entry point returns <code>integer(c_int)</code>: zero means success, non-zero means an error the caller handles through SQLCA. The routines fall into a few groups. </p><div class="table-wrapper"><table><thead><tr><th>Group</th><th>Routines</th><th>Purpose</th></tr></thead><tbody><tr><td>Connection</td><td><code>S7Connect</code>, <code>S7Disconnect</code>, <code>S7ConnectReset</code></td><td>Open, close, and reset a database connection.</td></tr><tr><td>Execute</td><td><code>S7Exec</code>, <code>S7ExecParams</code>, <code>S7ExecParamsMany</code>, <code>S7ExecSelectIntoOne</code>, <code>S7ExecImmediate</code></td><td>Static SQL, parameterised DML, array DML, single-row <code>SELECT INTO</code>, and <code>EXECUTE IMMEDIATE</code>.</td></tr><tr><td>Cursor</td><td><code>S7CursorDeclare</code>, <code>S7CursorOpen</code>, <code>S7CursorFetch</code>, <code>S7CursorFetchMany</code>, <code>S7CursorClose</code></td><td>Declare (optionally <code>WITH HOLD</code>), open, fetch (scalar or array), and close cursors.</td></tr><tr><td>Prepared</td><td><code>S7PrepareStatement</code>, <code>S7ExecPrepared</code>, <code>S7ExecPreparedInto</code>, <code>S7DeallocatePrepared</code></td><td>Dynamic SQL methods 2 and 3: prepare once, execute many.</td></tr><tr><td>Descriptor</td><td><code>S7DescriptorAllocate</code>, <code>S7DescribeOutput</code>, <code>S7DescriptorFetch</code>, <code>S7DescriptorGetItem</code>, <code>S7DescriptorCopyToSqlDa</code></td><td>Dynamic SQL method 4: SQLDA and DESCRIPTOR lifecycle and per-item access.</td></tr><tr><td>Parameter accumulators</td><td><code>S7StartSQL</code>, <code>S7EndSQL</code>, <code>S7SetSQLParams</code>, <code>S7SetResultParams</code></td><td>Open and close a parameter batch; push input parameters and result targets.</td></tr></tbody></table></div><h6>Accumulator signature</h6><p> Both accumulator calls take the same argument shape, so binding a value and binding a result target read the same way in generated code: </p> <pre>S7SetSQLParams (type_id, length, scale, flags, addr, ind_addr) S7SetResultParams(type_id, length, scale, flags, addr, ind_addr)</pre> <p><code>type_id</code> and <code>length</code> come from the host-variable mapping above, <code>addr</code> is the variable's address via <code>c_loc()</code>, and <code>ind_addr</code> is the NULL indicator address or <code>c_null_ptr</code>. The runtime holds these on a thread-local list until the operation call flushes them. </p><h2 id="statements">Statements and dictionary support</h2><p> The preprocessor recognises the full range of Rdb and VAX FORTRAN embedded-SQL statements, from static DML through all four dynamic-SQL methods. Blocks may span multiple FORTRAN lines with no continuation marker inside them: the scanner reads from <code>EXEC SQL</code> until it reaches <code>END-EXEC</code>. </p><div class="table-wrapper"><table><thead><tr><th>Category</th><th>Statements</th></tr></thead><tbody><tr><td>Session</td><td><code>CONNECT</code>, <code>DISCONNECT</code>, <code>SET TRANSACTION</code>, <code>COMMIT</code>, <code>ROLLBACK</code>, <code>SAVEPOINT</code></td></tr><tr><td>Static DML</td><td><code>SELECT INTO</code>, <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code></td></tr><tr><td>Cursors</td><td><code>DECLARE CURSOR</code>, <code>OPEN</code>, <code>FETCH</code>, <code>CLOSE</code>, scroll modes, <code>WITH HOLD</code></td></tr><tr><td>Dynamic SQL</td><td><code>EXECUTE IMMEDIATE</code>, <code>PREPARE</code>, <code>EXECUTE</code>, <code>EXECUTE ... INTO</code></td></tr><tr><td>Descriptor (method 4)</td><td><code>ALLOCATE DESCRIPTOR</code>, <code>DESCRIBE OUTPUT</code>, <code>GET</code> / <code>SET DESCRIPTOR</code>, <code>DEALLOCATE DESCRIPTOR</code></td></tr><tr><td>Control</td><td><code>WHENEVER</code>, portable <code>GET TYPE OF :hv INTO :result</code></td></tr></tbody></table></div><div class="callout"><strong>Rdb alias declarations.</strong> Rdb <code>DECLARE [EXTERNAL] &lt;name&gt; ALIAS ... FILENAME ...</code> attach declarations are parsed, so multi-database Rdb applications carry their alias structure into the migrated program. </div><h6>CDD and DEC record structures</h6><p> The FORTRAN front end understands OpenVMS Common Data Dictionary record definitions the same way the original tooling did. A <code>DICTIONARY '&lt;cdd-path&gt;'</code> directive is expanded into a DEC <code>STRUCTURE</code>, and <code>RECORD /type/ var</code> instances are registered so that dotted host variables such as <code>:rec.member</code> resolve to a real address the runtime can bind. This lets record-oriented Rdb FORTRAN, where host variables are members of a dictionary record, migrate without flattening the records by hand. </p><h2 id="cli">Command line and build pipeline</h2><p> The preprocessor is one binary, <code>vxsql</code>, shared across COBOL, FORTRAN, and Pascal. The FORTRAN front end is selected with <code>--language=fortran</code>, or detected from the file extension. </p><h6>Invocation</h6> <pre>vxsql -e --language=fortran --fortran-format=free \ -I &lt;incdir&gt; -i SRC.f90 -o OUT.f90 -m -s OUT.sym</pre> <div class="table-wrapper"><table><thead><tr><th>Flag</th><th>Long form</th><th>Meaning</th></tr></thead><tbody><tr><td><code>-e</code></td><td><code>--esql</code></td><td>Preprocess embedded SQL (required).</td></tr><tr><td><code>-l</code></td><td><code>--language</code></td><td><code>cobol</code>, <code>fortran</code>, or <code>pascal</code>; otherwise detected from the extension.</td></tr><tr><td></td><td><code>--fortran-format</code></td><td><code>auto</code>, <code>fixed</code>, or <code>free</code> (default <code>auto</code>).</td></tr><tr><td><code>-i</code> / <code>-o</code></td><td><code>--infile</code> / <code>--outfile</code></td><td>Input source and the single preprocessed output.</td></tr><tr><td><code>-s</code> / <code>-m</code></td><td><code>--symfile</code> / <code>--map</code></td><td>Symbol file and optional binary map file.</td></tr><tr><td><code>-I</code></td><td><code>--copypath</code></td><td>Include and COPY search path.</td></tr><tr><td></td><td><code>--target-db</code></td><td>Validate against the chosen driver (see below).</td></tr><tr><td></td><td><code>--vax-float</code></td><td>Treat <code>real*4</code> and <code>real*8</code> as VAX F, D, and G floating and route them through the numeric bridge.</td></tr><tr><td></td><td><code>--allow-passthru</code></td><td>Compile an unrecognised or DDL block as a runtime exec with a named warning instead of a hard error.</td></tr></tbody></table></div><p> Recognised FORTRAN extensions are <code>.f</code>, <code>.f77</code>, <code>.for</code>, <code>.ftn</code>, <code>.f90</code>, <code>.f95</code>, <code>.f03</code>, and <code>.f08</code>. Both free-form and fixed-form source are handled. </p><h6>Compile and link</h6> <pre>gfortran OUT.f90 -I runtime/libvxsql-fortran \ -L runtime/libvxsql-fortran -lvxsql-fortran \ -L runtime/libvxsql-pgsql -lVXSQL_PGSQL -lstdc++ -o prog</pre> <p> The preprocessed source compiles like any other FORTRAN, then links against <code>libvxsql-fortran</code> and exactly one database driver library. Choosing a different target database is a link-time change: swap the driver library, not the source. </p><h6>Target validation is fail-loud</h6><div class="callout warn"><strong>No silent downgrades.</strong> If a block uses a feature the chosen <code>--target-db</code> cannot honour, for example scroll-cursor fetch modes on a forward-only target or a <code>RETURNING</code> clause on a target that lacks it, the preprocessor stops with an error that names the construct and the source line rather than emitting code that would misbehave at run time. </div><h2 id="compatibility">Compatibility and status</h2><p> VX/SQL-FORTRAN targets the embedded SQL that real Rdb and VAX FORTRAN applications use. PostgreSQL is the reference target and the most fully exercised; other drivers are selected at link time and vary in breadth. The matrix below summarises feature status. </p><div class="table-wrapper"><table><thead><tr><th>Feature</th><th>Status</th></tr></thead><tbody><tr><td><code>CONNECT</code> / <code>DISCONNECT</code>, static <code>SELECT INTO</code> / <code>INSERT</code> / <code>UPDATE</code> / <code>DELETE</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Cursors: <code>DECLARE</code> / <code>OPEN</code> / <code>FETCH</code> / <code>CLOSE</code>, <code>WITH HOLD</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Scroll cursors (<code>NEXT</code>, <code>PRIOR</code>, <code>FIRST</code>, <code>LAST</code>, <code>ABSOLUTE</code>, <code>RELATIVE</code>)</td><td><span class="ac-live">live on PostgreSQL, SQLite, MySQL, SQL Server</span></td></tr><tr><td>NULL indicators (juxtaposed and <code>INDICATOR</code> keyword)</td><td><span class="ac-live">live</span></td></tr><tr><td><code>WHENEVER NOT FOUND</code> / <code>SQLERROR GO TO</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Dynamic SQL methods 1 to 3 (<code>EXECUTE IMMEDIATE</code>, <code>PREPARE</code>, <code>EXECUTE</code>, <code>EXECUTE ... INTO</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td>Dynamic SQL method 4 (SQL DESCRIPTOR, SQLDA, <code>DESCRIBE OUTPUT</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td>Array operations (<code>FETCH ... FOR n ROWS</code>, array <code>INSERT</code> / <code>UPDATE</code>)</td><td><span class="ac-live">live on PostgreSQL</span></td></tr><tr><td><code>SAVEPOINT</code> / <code>ROLLBACK TO</code>, <code>DEALLOCATE PREPARED</code>, two-phase commit</td><td><span class="ac-live">live on PostgreSQL</span></td></tr><tr><td><code>SET TRANSACTION</code> isolation directive</td><td><span class="ac-v1">parsed, applied where the driver supports it</span></td></tr><tr><td>CDD dictionary records and DEC structures</td><td><span class="ac-live">live</span></td></tr><tr><td>VAX F, D, and G floating conversion (<code>--vax-float</code>)</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h6>Target databases</h6><div class="kw-grid"><div>PostgreSQL</div><div>Oracle</div><div>SQL Server</div><div>MySQL</div><div>MariaDB</div><div>SQLite</div><div>ODBC</div><div>SQLRelay</div></div><h6>Platforms</h6><p> The preprocessor and runtime build and run on x86 OpenVMS, x86 and ARM Linux, and macOS, so the same source can be preprocessed on the source system or on the target during a staged migration. </p><div class="callout"><strong>One toolset across languages.</strong> VX/SQL-FORTRAN shares its runtime, driver set, and SQLCA behaviour with VX/SQL-COBOL and VX/SQL-MODULE, so a mixed-language Rdb estate migrates onto a single, consistent database layer. </div><h2 id="quickref">Quick reference</h2><h6>Invoke</h6> <pre>vxsql -e --language=fortran --fortran-format=free \ -I &lt;incdir&gt; -i SRC.f90 -o OUT.f90 -m -s OUT.sym</pre> <h6>Runtime modules</h6><div class="kw-grid"><div>sqlca_module</div><div>sqlda_module</div><div>vxsql_module</div></div><h6>Runtime routine groups</h6><div class="kw-grid"><div>S7Connect</div><div>S7Disconnect</div><div>S7Exec</div><div>S7ExecParams</div><div>S7ExecSelectIntoOne</div><div>S7ExecImmediate</div><div>S7CursorDeclare</div><div>S7CursorFetch</div><div>S7PrepareStatement</div><div>S7ExecPrepared</div><div>S7DescribeOutput</div><div>S7DescriptorFetch</div><div>S7StartSQL</div><div>S7EndSQL</div><div>S7SetSQLParams</div><div>S7SetResultParams</div></div><h6>Host-variable type IDs</h6><div class="kw-grid"><div>16 alphanumeric</div><div>23 signed binary</div><div>26 float</div><div>27 double</div></div><h6>Facts to remember</h6><div class="table-wrapper"><table><thead><tr><th>Item</th><th>Value</th></tr></thead><tbody><tr><td>Placeholders</td><td><code>$1</code>, <code>$2</code>, ... positional</td></tr><tr><td>SQLCA size</td><td>136 bytes; <code>SQLERRMC</code> 70 chars; <code>SQLSTATE</code> 5 chars</td></tr><tr><td>Rows affected</td><td><code>SQLERRD(3)</code></td></tr><tr><td>Emit markers</td><td><code>!S7&gt;</code> block start, <code>!S7</code> continuation</td></tr><tr><td>Reference target</td><td>PostgreSQL (libpq)</td></tr></tbody></table></div><h6>Recognised source extensions</h6><div class="kw-grid"><div>.f</div><div>.f77</div><div>.for</div><div>.ftn</div><div>.f90</div><div>.f95</div><div>.f03</div><div>.f08</div></div></div>

Frequently Asked Questions

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

What is VX/SQL-FORTRAN?

VX/SQL-FORTRAN is a single-pass embedded-SQL preprocessor for FORTRAN. It reads your source, rewrites each EXEC SQL ... END-EXEC block into ordinary FORTRAN calls to the portable libvxsql runtime, and emits one preprocessed FORTRAN source file.

The original SQL is preserved as !S7 comments in the output, so you still have a readable audit trail. It is a drop-in replacement for Oracle Rdb's SQL$PRE precompiler.

Why migrate from Oracle Rdb SQL$PRE to VX/SQL-FORTRAN?

Oracle Rdb and its SQL$PRE precompiler are tied to a platform and database that many estates are moving away from. VX/SQL-FORTRAN keeps your embedded SQL in FORTRAN while opening a path to modern platforms and open or commercial back ends.

You replace SQL$PRE with VX/SQL-FORTRAN in the build, link against libvxsql-fortran, and run the same application logic against PostgreSQL, Oracle, SQL Server, MySQL/MariaDB, SQLite, or ODBC.

Do I need to rewrite my FORTRAN application?

No. VX/SQL-FORTRAN is designed so your FORTRAN source structure and application logic stay intact. You keep EXEC SQL blocks, host variables, and the same program flow.

The preprocessor emits ordinary FORTRAN that compiles with gfortran or ifx. There is no application rewrite and no intermediate language files to maintain.

Which databases does VX/SQL-FORTRAN support?

The same preprocessed FORTRAN source can target PostgreSQL (the reference target), Oracle, SQL Server, MySQL/MariaDB, SQLite, or ODBC. You choose exactly one database driver at link time.

That means one codebase can be built for different back ends without rewriting embedded SQL, which simplifies testing and phased migration.

Which platforms does VX/SQL-FORTRAN run on?

VX/SQL-FORTRAN is available on x86 OpenVMS, x86 and ARM Linux, and macOS. The preprocessor and libvxsql runtime are built for these targets so Rdb-era FORTRAN can move with your platform strategy.

Build and link steps follow normal FORTRAN toolchain conventions on each platform, with libvxsql-fortran and your chosen database driver added at link time.

How does the build process change with VX/SQL-FORTRAN?

Replace the Rdb SQL$PRE step with the VX/SQL-FORTRAN preprocessor. It takes your FORTRAN source with embedded SQL and produces a single preprocessed .f (or equivalent) file ready for your FORTRAN compiler.

Compile that output with gfortran or ifx, then link against libvxsql-fortran plus one database driver selected at link time. No separate header or include pipeline is required beyond your normal FORTRAN build.

Does VX/SQL-FORTRAN support cursors, dynamic SQL, and error handling?

Yes. Host variables, cursors, dynamic SQL (methods 1 through 4), NULL indicators, WHENEVER directives, and SQLCA-based error handling all carry through the preprocessor into calls to libvxsql.

Your existing Rdb-style embedded SQL patterns continue to work after migration, so operational code around fetches, prepares, and error branches does not need to be redesigned.

How does VX/SQL-FORTRAN fit with VX/SQL-COBOL and VX/SQL-MODULE?

VX/SQL-FORTRAN is part of the VX/SQL toolset alongside VX/SQL-COBOL and VX/SQL-MODULE. All three share the same libvxsql runtime and the same set of link-time database drivers.

Mixed-language Rdb estates with FORTRAN, COBOL, and SQLMOD modules can migrate onto one database access layer, which reduces duplication and keeps SQL behavior consistent across languages.

Transform Your Legacy Software Today!

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