🚨 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/SOR - OpenVMS SORT MERGE

Overview

VX/SOR is a complete reimplementation of the OpenVMS SORT/MERGE utility on Linux. It ships two cooperating parts: the callable SOR$ Sort/Merge run-time library that OpenVMS BASIC, COBOL, FORTRAN and C programs call directly, and the SORT and MERGE DCL verbs that parse the VMS command line and drive that same library. Existing calls to SOR$ routines and existing DCL sort scripts move across without changing sort logic.

Under the hood VX/SOR is a classic external merge sort: records accumulate into an in-memory block, each full block is sorted and written to a SORTWORK swap file, then the swap files are combined with a fast buffered k-way merge. The /KEY, /SPECIFICATION, STABLE and NODUPLICATES behaviour matches VMS, and the qualifiers the engine acts on produce identical results to OpenVMS.

<!-- Webflow embed (min). Source: sor-page-technical/sor-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … --> <div class="s7-tech-embed" data-vxsord-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="architecture">What VX/SOR is</h2><p> VX/SOR is two cooperating things inside the VX/RT runtime. Together they replace the OpenVMS SORT/MERGE utility for migrated applications on Linux, at both the API and the command-line level. </p><div class="table-wrapper"><table><thead><tr><th>Component</th><th>Role</th><th>Status</th></tr></thead><tbody><tr><td><code>SOR$</code> callable library</td><td>The Sort/Merge run-time library compiled into <code>libvxrt.a</code>. One public entry point per module. This is the engine every other part sits on, and the API that OpenVMS BASIC, COBOL, FORTRAN and C programs call directly.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SORT</code> / <code>MERGE</code> DCL verbs</td><td>Thin command front-ends that parse the VMS SORT/MERGE command line and drive the <code>SOR$</code> API. They are callers of the library, not a separate engine.</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h6>The sort engine</h6><p> The engine is a classic external merge sort. Records are accumulated into an in-memory logical block; each full block is sorted and written to a <code>SORTWORK</code> swap file; the swap files are then combined with a fast buffered k-way heap merge (a legacy N-way merge remains as a fallback). This is the same shape as the OpenVMS Sort/Merge engine, so result ordering, duplicate handling and stability all match. </p><style>.s7-tech-embed .sor-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 .sor-flow-inputs,.s7-tech-embed .sor-flow-choices {display: grid;grid-template-columns: repeat(2, minmax(0, 1fr));gap: 10px;}.s7-tech-embed .sor-flow-node {min-width: 0;padding: 14px 16px;border-radius: 8px;background: var(--s7t-surface);color: var(--s7t-text);}.s7-tech-embed .sor-flow-node strong,.s7-tech-embed .sor-flow-node span {display: block;}.s7-tech-embed .sor-flow-node strong {margin-bottom: 4px;}.s7-tech-embed .sor-flow-node span {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}.s7-tech-embed .sor-flow-stage {border-top: 3px solid var(--s7t-accent);background: var(--s7t-surface2);}.s7-tech-embed .sor-flow-output {border: 1px solid var(--s7t-note-bd);background: var(--s7t-note-bg);}.s7-tech-embed .sor-flow-label {margin: 8px 0;color: var(--s7t-accent);font-family: var(--s7t-mono);font-size: 0.76em;font-weight: 700;letter-spacing: 0.08em;text-align: center;text-transform: uppercase;}.s7-tech-embed .sor-flow-split {display: grid;grid-template-columns: minmax(0, 1.5fr) minmax(180px, 0.7fr);gap: 10px;}.s7-tech-embed .sor-flow-side {border: 1px dashed var(--s7t-border);}.s7-tech-embed .sor-flow-sequence {display: grid;grid-template-columns: repeat(4, minmax(0, 1fr));gap: 10px;}.s7-tech-embed .sor-flow-note {margin-top: 10px;color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}@media (max-width: 800px) {.s7-tech-embed .sor-flow-inputs,.s7-tech-embed .sor-flow-choices,.s7-tech-embed .sor-flow-split,.s7-tech-embed .sor-flow-sequence {grid-template-columns: 1fr;}}</style><div class="sor-flow" aria-label="VX/SOR external merge sort topology"><div class="sor-flow-inputs"><div class="sor-flow-node"><strong>SORT or MERGE</strong><span>VX/DCL command front end</span></div><div class="sor-flow-node"><strong>Direct <code>SOR$</code> calls</strong><span>Migrated application API</span></div></div><div class="sor-flow-label">Both inputs converge</div><div class="sor-flow-node sor-flow-stage"><strong><code>SOR$</code> library in <code>libvxrt.a</code></strong><span>Accumulates records into an in-memory logical block</span></div><div class="sor-flow-label">When a logical block is full</div><div class="sor-flow-split"><div class="sor-flow-node sor-flow-stage"><strong>Sort the block</strong><span>Write the sorted run to work storage</span></div><div class="sor-flow-node sor-flow-side"><strong><code>SORTWORK0..9</code></strong><span>Side output: swap files for sorted runs</span></div></div><div class="sor-flow-label">After all blocks are written</div><div class="sor-flow-node sor-flow-stage"><strong>k-way heap merge</strong><span>Combines every sorted run in order</span></div><div class="sor-flow-label">Produces</div><div class="sor-flow-node sor-flow-output"><strong>Output file</strong><span>Final ordered records with duplicate and stability rules applied</span></div></div><p> Because both the DCL verbs and direct <code>SOR$</code> callers reach the same library, a migrated estate can keep its DCL sort scripts and its compiled programs that call the sort API, with no rewrite of either. </p><h2 id="callable-api">The SOR$ callable API</h2><p> The callable interface is ten public entry points. Each routine is defined once as the real C++ function and wired to its VMS-style aliases, so existing object references resolve and existing source compiles unchanged. Applications that call these routines migrate to Linux without changing their sort logic. </p><h6>Routine table</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th><th>Status</th></tr></thead><tbody><tr><td><code>SOR$INIT_SORT</code></td><td>Prepare a sort, treating key offsets as 1-based, then forward to BEGIN_SORT. This is the only functional difference from BEGIN_SORT.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$BEGIN_SORT</code></td><td>Initialise the context: build the key table, validate options, allocate the logical-block, duplicate and merge buffers, and generate SORTWORK names.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$PASS_FILES</code></td><td>Register one input file (wildcards expanded through the VMS parse/search path) and optionally the output file. Up to 10 input files per sort.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$RELEASE_REC</code></td><td>Push one record into the sort. RECORD process type only.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$SORT_MERGE</code></td><td>Run the back end: file-driven read, sort the trailing block, k-way merge, and optional direct write to the output file.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$RETURN_REC</code></td><td>Return the next sorted record; end-of-file status is returned once the stream is drained.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$END_SORT</code></td><td>Release the context, unlink the SORTWORK files, and free the specification tree.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$SPEC_FILE</code></td><td>Parse a VMS Sort specification file or string (FIELD, KEY, CONDITION, INCLUDE, OMIT, DATA, COLLATING_SEQUENCE) into a specification node tree.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SOR$STAT</code></td><td>Return one sort statistic selected by a code. A subset of codes is wired; unrecognised codes return zero with a success status.</td><td><span class="ac-v1">partial: subset of codes</span></td></tr><tr><td><code>SOR$DTYPE</code></td><td>Register a user compare routine for one of ten user-defined key data types.</td><td><span class="ac-v1">live: see alias note</span></td></tr></tbody></table></div><h6>Name forms and aliasing</h6><p>Every routine is exposed under several names, so callers from any supported language link against it:</p><div class="kw-grid"><div>SOR_D_xxx (C++ symbol)</div><div>SOR$xxx (upper-case VMS)</div><div>sor$xxx (lower-case VMS)</div><div>F4_SOR_D_xxx (FORTRAN thunk)</div></div><p><code>SOR_D_xxx</code> is the canonical internal C++ symbol. <code>SOR$xxx</code> and <code>sor$xxx</code> are the VMS-style upper and lower-case aliases. <code>F4_SOR_D_xxx</code> is a FORTRAN thunk that unpacks trailing string-length arguments, provided for the descriptor-taking routines (PASS_FILES, RELEASE_REC, RETURN_REC and SPEC_FILE). </p><div class="callout warn"><strong>Alias note for SOR$DTYPE.</strong> The user-compare registration routine is currently exported under the upper-case alias <code>SYR$DTYPE</code> rather than <code>SOR$DTYPE</code>. Until this is corrected, call <code>SYR$DTYPE</code> or the lower-case <code>sor$dtype</code>; the routine itself works normally. </div><h6>Record-interface example</h6><p> The RECORD interface releases records one at a time and pulls sorted records back until end-of-file. This example sorts on a 10-byte text key at 1-based position 1, the account number in a banking record: </p> <pre>#include "sor$routines.h" #include "ssdef.h" U32 ctx = 0; <span class="cmt">/* 0 => allocate a context slot */</span> U16 lrl = 80; <span class="cmt">/* longest record */</span> U32 opt = SOR$M_STABLE; <span class="cmt">/* stable sort */</span> U16 keybuf[1 + 4] = { 1, <span class="cmt">/* one key descriptor */</span> DSC$K_DTYPE_T, <span class="cmt">/* key_type = text */</span> 0, <span class="cmt">/* key_order = ascending */</span> 1, <span class="cmt">/* key_offset = 1-based (INIT_SORT) */</span> 10 <span class="cmt">/* key_length = 10-byte account number */</span> }; <span class="cmt">/* 1. initialise (INIT_SORT: offsets are 1-based) */</span> U32 st = <span class="kw">SOR$INIT_SORT</span>(keybuf, &amp;lrl, &amp;opt, 0, 0, 0, 0, 0, &amp;ctx); if (!(st &amp; 1)) return st; <span class="cmt">/* 2. release records (RECORD process only) */</span> struct dsc$descriptor rec_d; <span class="cmt">/* ... fill rec_d for each ACCOUNTS record ... */</span> st = <span class="kw">SOR$RELEASE_REC</span>(&amp;rec_d, &amp;ctx); <span class="cmt">/* repeat per input record */</span> <span class="cmt">/* 3. run the sort/merge */</span> st = <span class="kw">SOR$SORT_MERGE</span>(&amp;ctx); if (!(st &amp; 1)) { SOR$END_SORT(&amp;ctx); return st; } <span class="cmt">/* 4. pull sorted records back until end-of-file */</span> char out[80]; U16 olen; struct dsc$descriptor out_d; <span class="cmt">/* points at out[] */</span> while ((st = <span class="kw">SOR$RETURN_REC</span>(&amp;out_d, &amp;olen, &amp;ctx)) == SS$_NORMAL) { <span class="cmt">/* ... consume out[0..olen) ... */</span> } <span class="cmt">/* st == SS$_ENDOFFILE here */</span> <span class="cmt">/* 5. always tear down */</span> <span class="kw">SOR$END_SORT</span>(&amp;ctx);</pre> <p> The file-driven alternative is what the DCL verbs use: call <code>SOR$PASS_FILES</code> once per input file, then <code>SOR$BEGIN_SORT</code>, then <code>SOR$SORT_MERGE</code> (which reads the inputs and writes the output file directly), then <code>SOR$END_SORT</code>, with no release or return loop. </p><h6>Pluggable sort engine</h6><p> Each entry point first checks for a replacement engine. On the first call it loads a customer-supplied shared library named by the <code>SORLIB</code> logical or environment variable and looks up the matching lower-case <code>sor$xxx</code> symbol. If it is present, the call is forwarded to that engine and the in-tree engine is bypassed, so a site can drop in an alternative sort implementation without relinking the runtime. </p><h2 id="data-model">Key and record definitions</h2><p> A developer driving the callable API needs to understand three things: how keys are described, what options control the sort, and the limits the context enforces. All three match the OpenVMS Sort/Merge data model. </p><h6>Key buffer</h6><p> Keys are described by a compact unsigned 16-bit array. The first element is the number of key descriptors; each descriptor is then four elements: data type, order, offset and length. </p> <pre>key_buffer[0] = N, the number of key descriptors key_buffer[1 + 4*i + 0] = key_type (a DSC$K_DTYPE_* code) key_buffer[1 + 4*i + 1] = key_order (0 = ascending, 1 = descending) key_buffer[1 + 4*i + 2] = key_offset (0-based for BEGIN_SORT, 1-based for INIT_SORT) key_buffer[1 + 4*i + 3] = key_length</pre> <p><code>SOR$INIT_SORT</code> treats offsets as 1-based and decrements each one before forwarding to BEGIN_SORT, so an offset of 0 there is a fatal bad-key error. If no key is supplied at all, BEGIN_SORT synthesises a single ascending text key spanning the whole record. A context can hold up to 50 keys. </p><h6>Per-record header</h6><p> Inside the work buffer each record is prefixed with a packed header. The sequence number is what makes STABLE sorts stable: records that compare equal on all keys break the tie on insertion order. </p> <pre>typedef struct { U32 recnum; <span class="cmt">// duplicate flag = recnum |= 0x80000000</span> U32 seqnum; <span class="cmt">// preserves insertion order for STABLE</span> U16 ctx_num; U16 reclen; off_t offset_into_swap_file; char data[0]; } sort_header_t;</pre> <h6>Context limits</h6><div class="table-wrapper"><table><thead><tr><th>Constant</th><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>MAX_CONTEXT</code></td><td>64</td><td>Concurrent sort contexts</td></tr><tr><td><code>MAX_INPUT_FILES</code></td><td>10</td><td>Input files per sort</td></tr><tr><td><code>MAX_SORTWORK</code></td><td>11</td><td>SORTWORK swap-file slots</td></tr><tr><td><code>MAX_COND</code></td><td>100</td><td>Conditions per specification node</td></tr><tr><td>keys per context</td><td>50</td><td>Maximum key descriptors</td></tr><tr><td>default page / swap I/O</td><td>4 MiB</td><td>In-memory block and swap-file I/O unit</td></tr></tbody></table></div><p> A context is referenced by a 32-bit handle. Passing a context value of 0 on the first call allocates a free slot from the 64 available. </p><h6>Options and process types</h6><p> The options bitmask controls sort behaviour. Only three bits are legal; any other bit is rejected. The default when no options are supplied is STABLE. </p><div class="table-wrapper"><table><thead><tr><th>Option</th><th>Meaning</th></tr></thead><tbody><tr><td><code>STABLE</code></td><td>Preserve insertion order for records with equal keys (default).</td></tr><tr><td><code>NOSIGNAL</code></td><td>Accepted for compatibility; the engine returns conditions as values and never signals.</td></tr><tr><td><code>NODUPS</code></td><td>Drop records that are equal on all keys.</td></tr></tbody></table></div><p> Setting STABLE and NODUPS together is rejected as an exclusive-option error. The process type can be RECORD (the default), TAG, INDEX or ADDRESS: all four are accepted by BEGIN_SORT, but <code>SOR$RELEASE_REC</code> handles RECORD only, so TAG, INDEX and ADDRESS are reached through the file interface (<code>SOR$PASS_FILES</code>). </p><h6>Statistics</h6><p><code>SOR$STAT</code> returns one statistic selected by a code. The counters below are wired; the remaining OpenVMS codes (nodes, initial runs, merge order, work allocation, merge buffer count) return zero with a success status, so a missing statistic is a zero rather than an error. </p><div class="table-wrapper"><table><thead><tr><th>Statistic</th><th>Returned value</th></tr></thead><tbody><tr><td><code>SOR$K_IDENT</code></td><td>Implementation identifier</td></tr><tr><td><code>SOR$K_REC_INP</code></td><td>Records released to the sort</td></tr><tr><td><code>SOR$K_REC_SOR</code></td><td>Records actually sorted</td></tr><tr><td><code>SOR$K_REC_OUT</code></td><td>Records returned</td></tr><tr><td><code>SOR$K_LRL_INP</code></td><td>Largest input record length</td></tr><tr><td><code>SOR$K_LRL_OUT</code></td><td>Largest output record length</td></tr><tr><td><code>SOR$K_MRG_PASSES</code></td><td>Number of merge passes</td></tr></tbody></table></div><h2 id="dcl">SORT and MERGE from the command line</h2><p> VX/SOR defines <code>SORT</code> and <code>MERGE</code> as VX/DCL verbs that replicate the VMS command syntax. They parse the qualifiers, expand VMS wildcards on the input files, map the output specification, and then drive the same <code>SOR$</code> library described above. <code>MERGE</code> is identical to <code>SORT</code> except that it opens the output to append or create rather than supersede, and it expects inputs that are already sorted on the same keys. </p><h6>Command syntax</h6> <pre>$ <span class="kw">SORT</span>/KEY=(POSITION:1,SIZE:10) [/DESCENDING] [/STABLE] [/NODUPLICATES] [/STATISTICS] [/SPECIFICATION=RULES.SPC] ACCOUNTS.DAT SORTED_ACCOUNTS.DAT $ <span class="kw">MERGE</span>/KEY=(POSITION:1,SIZE:10) BRANCH_EAST.DAT,BRANCH_WEST.DAT ACCOUNTS.DAT</pre> <h6>Key sub-keywords</h6><p> The <code>/KEY</code> clause is where most of the sort is defined. The keywords below change the key descriptor and behave exactly as on OpenVMS: </p><div class="table-wrapper"><table><thead><tr><th>Keyword</th><th>Effect</th></tr></thead><tbody><tr><td><code>POSITION</code></td><td>1-based key start position; a value below 1 is a key-position error.</td></tr><tr><td><code>SIZE</code></td><td>Key length in bytes.</td></tr><tr><td><code>NUMBER</code></td><td>Key ordinal.</td></tr><tr><td><code>ASCENDING</code> / <code>DESCENDING</code></td><td>Sets the sort direction.</td></tr><tr><td><code>CHARACTER</code></td><td>Text key (the default data type).</td></tr><tr><td><code>D_FLOATING</code>, <code>F_FLOATING</code>, <code>G_FLOATING</code>, <code>H_FLOATING</code></td><td>The VAX floating-point key data types.</td></tr><tr><td><code>PACKED_DECIMAL</code></td><td>Packed decimal key data type.</td></tr></tbody></table></div><p> For source compatibility, VX/SOR also parses and consumes a further set of <code>/KEY</code> sub-keywords that have no effect on the sort, so existing command lines carry across without edits. These accepted-for-compatibility keywords are: </p><div class="kw-grid"><div>BINARY</div><div>DECIMAL</div><div>ZONED</div><div>SIGNED</div><div>UNSIGNED</div><div>LEADING_SIGN</div><div>TRAILING_SIGN</div><div>OVERPUNCHED_SIGN</div><div>SEPARATE_SIGN</div><div>ONYOMI</div><div>KUNYOMI</div><div>BUSHU</div><div>SOKAKU</div><div>KOKUGO</div><div>KANA8BIT</div><div>JISCODE</div></div><h6>Command-level qualifiers</h6><div class="table-wrapper"><table><thead><tr><th>Qualifier</th><th>Behaviour</th><th>Status</th></tr></thead><tbody><tr><td><code>/KEY=(...)</code></td><td>Builds the key table and drives BEGIN_SORT.</td><td><span class="ac-live">acted on</span></td></tr><tr><td><code>/STABLE</code></td><td>Preserves insertion order for equal keys.</td><td><span class="ac-live">acted on</span></td></tr><tr><td><code>/[NO]DUPLICATES</code></td><td>The negated form drops records equal on all keys.</td><td><span class="ac-live">acted on</span></td></tr><tr><td><code>/STATISTICS</code></td><td>Prints records read, sorted and output plus elapsed and CPU time.</td><td><span class="ac-live">acted on</span></td></tr><tr><td><code>/SEQUENTIAL</code></td><td>Sets the output file organisation to sequential.</td><td><span class="ac-live">acted on</span></td></tr><tr><td><code>/SPECIFICATION=file</code></td><td>Parses a specification file that drives fields, conditions and include/omit rules.</td><td><span class="ac-live">acted on</span></td></tr><tr><td><code>/PROCESS=(...)</code></td><td>Parsed into RECORD/TAG/ADDRESS/INDEX but not forwarded to the engine.</td><td><span class="ac-v1">accepted for compatibility</span></td></tr><tr><td><code>/FORMAT=(...)</code></td><td>FIXED/VARIABLE/CONTROLLED and the size keywords parse but do not change the sort.</td><td><span class="ac-v1">accepted for compatibility</span></td></tr></tbody></table></div><h6>Specification files</h6><p> A specification file lets you drive the sort declaratively instead of on the command line. <code>/SPECIFICATION</code> reads FIELD, CONDITION and INCLUDE/OMIT directives (handled by <code>SOR$SPEC_FILE</code>), so you can define fields and record-selection conditions in one place and reuse them across jobs. </p><h6>Statistics output</h6><p>The <code>/STATISTICS</code> qualifier prints a banner after the run:</p> <pre> VX/SOR Sort and Merge Utility v&lt;Version_Id&gt; Records read: &lt;n&gt; Records sorted: &lt;n&gt; Records output: &lt;n&gt; Elapsed time (Secs) &lt;n&gt; Elapsed CPU (Secs) &lt;f&gt;</pre> <h2 id="compatibility">Compatibility and status</h2><p> VX/SOR targets Linux. The callable API and the DCL verbs are compatible with OpenVMS Sort/Merge for the features an application actually relies on to sort and merge records. The matrix below is an honest account of what is live, what is partial, and the handful of areas that are accepted for compatibility but do not change results. </p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Status</th><th>Note</th></tr></thead><tbody><tr><td>Ten callable <code>SOR$</code> routines</td><td><span class="ac-live">live</span></td><td>All built into the VX/RT runtime library.</td></tr><tr><td><code>SORT</code> / <code>MERGE</code> DCL verbs</td><td><span class="ac-live">live</span></td><td>Full VMS command-line parsing, driving the same engine.</td></tr><tr><td>External merge sort with fast k-way merge</td><td><span class="ac-live">live</span></td><td>Legacy N-way merge retained as a fallback.</td></tr><tr><td><code>/SPECIFICATION</code> file parser</td><td><span class="ac-live">live</span></td><td>FIELD, CONDITION and INCLUDE/OMIT directives.</td></tr><tr><td>STABLE, NODUPLICATES options</td><td><span class="ac-live">live</span></td><td>Default is STABLE; STABLE with NODUPS is rejected as on VMS.</td></tr><tr><td><code>SOR$STAT</code> counters</td><td><span class="ac-v1">partial</span></td><td>Core record and length counters are wired; other codes return zero.</td></tr><tr><td>User data-type compare via DTYPE</td><td><span class="ac-v1">partial</span></td><td>Works, but currently exported as <code>SYR$DTYPE</code> (see the callable API section).</td></tr><tr><td>TAG / INDEX / ADDRESS process types</td><td><span class="ac-v1">partial</span></td><td>Reachable through the file interface; <code>SOR$RELEASE_REC</code> handles RECORD only.</td></tr><tr><td><code>/PROCESS</code>, <code>/FORMAT</code> qualifiers</td><td><span class="ac-v1">accepted</span></td><td>Parsed for compatibility; values do not change the sort.</td></tr><tr><td><code>NOSIGNAL</code> option</td><td><span class="ac-v1">accepted</span></td><td>Accepted; the engine returns conditions as values and never signals.</td></tr><tr><td>Windows build</td><td><span class="ac-no">not offered</span></td><td>VX/SOR runs on Linux.</td></tr></tbody></table></div><h6>Behaviour notes for a migrating team</h6><ul><li>Register a user compare routine through <code>SYR$DTYPE</code> or the lower-case <code>sor$dtype</code> until the upper-case <code>SOR$DTYPE</code> alias is corrected.</li><li>TAG, INDEX and ADDRESS sorts are driven through <code>SOR$PASS_FILES</code>, not through the release/return record loop.</li><li>The <code>NOSIGNAL</code> option and the disk-allocation arguments are accepted but have no observable effect, because the engine reports every condition as a returned status value.</li><li><code>/PROCESS</code> and <code>/FORMAT</code> at the DCL layer parse cleanly and are ignored, so existing command lines run without edits.</li></ul><h2 id="quickref">Quick reference</h2><h6>Call lifecycle</h6><div class="sor-flow" aria-label="VX/SOR callable API lifecycle"><div class="sor-flow-sequence"><div class="sor-flow-node sor-flow-stage"><strong><code>INIT_SORT</code> or <code>BEGIN_SORT</code></strong><span>Initialize the sort context</span></div><div class="sor-flow-node sor-flow-stage"><strong>Supply input</strong><span><code>RELEASE_REC</code> x N or <code>PASS_FILES</code> x N</span></div><div class="sor-flow-node sor-flow-stage"><strong><code>SORT_MERGE</code></strong><span>Sort records and merge runs</span></div><div class="sor-flow-node sor-flow-stage"><strong><code>RETURN_REC</code> x N</strong><span>Read until end-of-file, then call <code>END_SORT</code></span></div></div><div class="sor-flow-note"> Call <code>SPEC_FILE</code> or <code>DTYPE</code> before <code>BEGIN_SORT</code>. Call <code>STAT</code> at any time after initialization. </div></div><h6>DCL syntax</h6> <pre>$ SORT/KEY=(POSITION:1,SIZE:10) [/DESCENDING] [/STABLE] [/NODUPLICATES] [/STATISTICS] [/SPECIFICATION=RULES.SPC] in.dat out.dat $ MERGE/KEY=(POSITION:1,SIZE:10) in1.dat,in2.dat out.dat</pre> <h6>Callable routines</h6><div class="kw-grid"><div>SOR$INIT_SORT</div><div>SOR$BEGIN_SORT</div><div>SOR$PASS_FILES</div><div>SOR$RELEASE_REC</div><div>SOR$SORT_MERGE</div><div>SOR$RETURN_REC</div><div>SOR$END_SORT</div><div>SOR$SPEC_FILE</div><div>SOR$STAT</div><div>SOR$DTYPE</div></div><h6>Key buffer</h6> <pre>[N, {type, order, offset, length} x N] INIT_SORT offsets 1-based; BEGIN_SORT offsets 0-based.</pre> <h6>Tuning environment</h6><div class="table-wrapper"><table><thead><tr><th>Variable</th><th>Effect</th></tr></thead><tbody><tr><td><code>SOR_PAGE_SIZE</code></td><td>In-memory logical-block size (bytes, above 1024; default 4 MiB).</td></tr><tr><td><code>SOR_SWAP_IO_SIZE</code></td><td>Swap-file I/O unit and maximum record size (bytes, above 1024; default 4 MiB).</td></tr><tr><td><code>SORTWORK0..9</code></td><td>Placement of the SORTWORK swap files.</td></tr><tr><td><code>VXSOR_MERGE_ENGINE</code></td><td>Set to <code>legacy</code> to force the older N-way merge.</td></tr><tr><td><code>SORLIB</code></td><td>Names a replacement sort engine shared library.</td></tr></tbody></table></div><h6>Limits</h6><div class="kw-grid"><div>64 contexts</div><div>10 input files</div><div>11 SORTWORK files</div><div>50 keys per context</div><div>100 conditions per spec node</div></div></div>

Frequently Asked Questions

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

What is VX/SOR and how does it help with OpenVMS migration?

VX/SOR is a complete VMS/OpenVMS SORT MERGE implementation that fully supports the SOR$ API set, VX/DCL command line, and Linux shell execution. It enables organizations to migrate their OpenVMS sorting and merging operations to Linux without modifying existing application logic.

The tool replicates VMS sorting capabilities using multi-file merge sort techniques with quick-sort enhancements for optimal performance. For migration planning assistance, contact us.

Which SOR$ API functions does VX/SOR support?

VX/SOR provides complete support for all essential SOR$ API functions including SOR$BEGIN_SORT, SOR$END_SORT, SOR$INIT_SORT, SOR$MERGE, SOR$PASS_FILES, SOR$RELEASE_REC, SOR$RETURN_REC, SOR$SORT_MERGE, SOR$SPEC_FILE, and SOR$STAT.

Can VX/SOR run SORT commands through VX/DCL?

Yes, VX/SOR integrates fully with VX/DCL, supporting native VMS command syntax for SORT and MERGE operations with complete command parsing and execution support.

How does the VX/SOR migration process work?

VX/SOR enables seamless migration by providing call-compatible APIs that ensure applications using VMS sorting functions continue working without code changes. The tool maintains identical sorting behavior through its comprehensive qualifier support and API compatibility.

Which SORT qualifiers are supported in VX/SOR?

VX/SOR supports all essential VMS SORT qualifiers including ASCENDING, BINARY, CHARACTER, DECIMAL, DESCENDING, KEY, PROCESS, SEQUENTIAL, STATISTICS, and DUPLICATES, ensuring identical sorting behavior across platforms.

Does VX/SOR provide performance optimization features?

VX/SOR leverages multi-file merge sort techniques with quick-sort enhancements to optimize performance. The STATISTICS qualifier provides detailed performance metrics, allowing teams to monitor and optimize sorting operations effectively.

What is VX/SOR?

VX/SOR is a complete reimplementation of the OpenVMS SORT/MERGE utility for Linux. It provides the full SOR$ callable Sort/Merge API and the SORT and MERGE DCL verbs, so migrated applications can sort and merge records on Linux exactly as they did on OpenVMS.

Do I have to change my application code to use VX/SOR?

No. VX/SOR provides call-compatible SOR$ routines and VX/DCL integration, so programs that call the OpenVMS sort routines and DCL scripts that run SORT or MERGE keep working without changes to their sort logic.

Which SOR$ routines does VX/SOR provide?

VX/SOR ships a callable library covering SOR$INIT_SORT, SOR$BEGIN_SORT, SOR$PASS_FILES, SOR$RELEASE_REC, SOR$SORT_MERGE, SOR$RETURN_REC, SOR$END_SORT, SOR$SPEC_FILE, SOR$STAT and SOR$DTYPE, so existing OpenVMS applications that call these routines migrate to Linux without rewriting their sort code.

How do I run a SORT from the command line?

Use the SORT verb through VX/DCL with the same syntax as OpenVMS. It takes one or more input files and an output file, and /KEY defines the sort key by 1-based byte position and size. For example: SORT/KEY=(POSITION:1,SIZE:10) ACCOUNTS.DAT SORTED_ACCOUNTS.DAT.

How do I merge files that are already sorted?

Use the MERGE verb. MERGE combines two or more inputs that are already sorted on the same keys into one ordered output file. It does not re-sort the inputs, which makes it faster than a full sort when the data is already in order.

Which SORT qualifiers does VX/SOR support?

VX/SOR acts on /KEY (position, size, number, ascending or descending, and the CHARACTER, D_FLOATING, F_FLOATING, G_FLOATING, H_FLOATING and PACKED_DECIMAL data types), /STABLE, /STATISTICS, /DUPLICATES and /NODUPLICATES, /SEQUENTIAL and /SPECIFICATION. Some further keywords are accepted for compatibility so existing command lines run without edits.

Does VX/SOR help with sort performance?

Yes. VX/SOR uses an external merge sort with a fast buffered k-way merge, and the /STATISTICS qualifier reports records read, sorted and output plus elapsed and CPU time. Environment variables let you tune the in-memory block size, the swap I/O size, and the placement of the SORTWORK files.

Can I define the sort in a specification file instead of on the command line?

Yes. The /SPECIFICATION qualifier reads FIELD, CONDITION and INCLUDE/OMIT directives from a specification file, so you can define fields and record-selection rules declaratively and reuse them across jobs instead of building long command lines.

Transform Your Legacy Software Today!

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