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></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></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> EXEC SQL CONNECT TO :datasrc USER :dbuser</span>
<span class="cmt">!S7 END-EXEC</span>
<span class="kw">if</span> (S7Connect(sqlca, &
c_loc(datasrc), 64_c_int, &
c_null_ptr, 0_c_int, & <span class="cmt">! connection-id</span>
c_null_ptr, 0_c_int, & <span class="cmt">! dbname</span>
c_loc(dbuser), 64_c_int, & <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> 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, &
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) &</span>
<span class="str">&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] <name> 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 '<cdd-path>'</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 <incdir> -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 <incdir> -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></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>