Stack User Guide


Version 4.2.448.0

1. Introduction to Raincode Legacy Compilers

1.1. Raincode Legacy Compilers User Guide

This section introduces the reader to the Raincode Legacy Compilers User Guide.

Raincode® is a registered trademark. All other product and company names and marks mentioned in this document are the property of their respective owners. Note that PL/I, CICS, MVS and z/OS are registered trademarks of IBM. SQL Server, Visual Studio and Host Integration Server are registered trademarks of Microsoft.

The purpose of this document is to explain how to use Raincode Legacy Compilers to:

  • Build managers

  • PL/I, COBOL or ASSEMBLER developers.

Wherever you see the phrase legacy program in this document, it refers to the legacy programming languages supported by Raincode Legacy Compilers.

This document is divided into several sections: Each covers an aspect of Raincode Legacy Compilers and where appropriate, its interaction with Raincode’s and other vendor products.

1.2. Raincode Legacy Compilers

This section introduces the reader to the Raincode Legacy Compilers.

The Raincode Legacy Compilers include Raincode Legacy Compiler - PL/I, Raincode Legacy Compiler - COBOL and Raincode Legacy Compiler -ASSEMBLER; collectively referred to as Raincode Legacy Compilers herein unless referred to by their names.

The concept of a legacy compiler concerns the compilation of existing source code portfolios rather than the costly and time-consuming development of new source code. Raincode Legacy Compilers are extensively compatible with existing mainframe compilers, and compile your mainframe legacy source code (PL/I or COBOL or ASSEMBLER) on the .NET Framework with no change to your source code (See section Single sourcing ). Raincode Legacy Compilers are also in compliance with .NET (previously named .NET Core), which makes the legacy compiler platform agnostic.

1.2.1. Features of Raincode Legacy Compilers

The Raincode Legacy Compilers:

  • Allows systems to be moved as is rather than through a complex translation or rewriting process, reducing the risks associated with migration.

migration
Figure 1. A typical migration scenario using Raincode Legacy Compilers
  • Supports a comprehensive set of data types, reliably simulating mainframe data types for data structure and program execution.

  • Fully supports command line control.

  • Provides an optional Raincode Legacy Compilers Visual Studio plug-in (See section Visual Studio Integration)

  • Supports SQL extensions:

    • Supports embedded SQL (targeting SQL server and DB/2 HIS), as any legacy program statements in source code; and no external preprocessor

    • Regarding SQL, Raincode Legacy Compilers can retarget to SQL Server database, without having to change the source code, only through database metadata and configuration files.

  • Supports CICS extensions:

    • Native support for a basic CICS subset

    • Implemented directly at the compiler level

    • More comprehensive support is to be provided by 3rd party tools.

  • Enables integration of your migrated legacy application with external .NET components written in any .NET compliant language, e.g. VB.NET, C#.

  • Supports generation of .NET code in 32 / 64 bit, 100% fully managed code.

  • Generates thread-safe code.

  • Supports block and line I/O File Access:

    • Through ExtFH [1], Raincode Legacy Compilers support Callable File Handler(ExtFH), enabling programs to perform file operations (See section I/O Configuration). File Access through ExtFH (unmanaged code) has advantages, namely that it is the common standard for COBOL.

  • Features .NET native file access:

    • Written in fully managed C# code

    • Supports native Mainframe file formats.

  • Features dynamic memory management (See section Memory allocation).

  • Builds a repository containing several artifacts of the source program for cross reference, ad hoc queries, etc.; this repository can be used to analyze the portfolio, estimate migration workload, and plan the migration (See Raincode Legacy Compilers artifacts repository)

  • Provides a successful data migration, which may depend on how your data is stored in your mainframe’s RAM; consider the following:

  • Provides precision (See section Appendix Good to know) and intermediate value arithmetics (See Appendix Good to know).

1.3. Single sourcing

Raincode Legacy Compilers compile existing legacy program portfolios for the .NET platform without altering the source code. In practical terms, this facility is provided by:

2. Visual Studio Integration

Raincode Legacy Compilers are fully integrated with the Microsoft Visual Studio© development environment and provide developer productivity tools through the Raincode Language Service Visual Studio extension, such as:

For more details on the syntax coloring, read Microsoft Visual Studio’s color-coding in Writing Code in the Code and Text Editor.
  • Intellisense - Microsoft Visual Studio© provides a tool for context-based auto completion of the code. Some of the features of this tool has been implemented by the Raincode Language Service Visual Studio extension.

    The project needs to be compiled recently to ensure that intellisense suggestions are up-to-date since intellisense relies on the compilation repository database as the data source.
    • Complete word:
      Press Ctrl + space or Press Ctrl + J.

      intellisense1
      Figure 2. Complete word
    • Quick Info:
      Press ctrl + K + I, or when the user hovers the cursor on the file name, it shows the file’s content in a tooltip.

vs intellisense 3
Figure 3. Quick Info
  • GO TO Definition - Microsoft Visual Studio© GO TO Definition navigates you to the line of code where the selected file name is defined.

    • Press F12, or right-click and select Go To Definition on the included file name, and it opens the corresponding file.

vs intellisense 4
Figure 4. GO-TO definition
  • Find All References - Microsoft Visual Studio© Find All References helps you find all the usage of a code element referenced in the codebase.

    • Press Shift + F12, or right-click on the element and select Find All References, and it will display all the elements in a tool window.

vs intellisense 7
Figure 5. Find All References

For more details of integration with the Microsoft Visual Studio ©, see the section Developer Productivity Tools

  • PL/I, COBOL, JCL, and HLASM aware debugger (See section Symbolic Debugging)

  • Raincode Language Service Visual Studio extension allows you to drill up and down, with tangible ease, through the component parts of your project.

Raincode Language Service Visual Studio extension will not be functional if the installation of Visual Studio© is minimal. See the section, Raincode Language Service Visual Studio extension is not Functional for more details.

2.1. Developer Productivity Tools

Raincode Legacy Compilers integrates with Microsoft Visual Studio© to allow configuration of command-line (See section Raincode Legacy Compilers command-line options) and execution (See section rclrun command-line options) options, such as those controlling SQL server database (See section SQL and CICS support) and CICS integration.

The Raincode Legacy Compilers can be invoked from within Microsoft Visual Studio©.

Errors found while compiling the source code are displayed in the Error view (See section Viewing a project’s error list), and a simple click positions the cursor on the erroneous line in the source file.

Successfully compiled programs can be launched from within the development environment (See sections Running compiled programs and Building a project), taking all the parameters controlling run-time configuration into account.

2.2. Symbolic Debugging

Raincode Legacy Compilers are fully integrated with the development environment debugger. It allows users to set breakpoints within the legacy source code and provides a step-by-step debugging facility. It also allows users to view and modify variable values either through the Locals window (See section Debugging a module), which displays the local variables of the current procedure, or through the Watch window (See section Debugging a module), which displays specific variables.

The debugger can perform mixed language debugging, and it automatically switches from legacy program source debugging to C# (or any other .NET language) when needed.

Some sample screens showing typical Raincode Language Service Visual Studio extension debugging activity are shown in this document (See section Debugging a module).

2.3. Conditional Breakpoint

The Raincode Debugger supports the same conditional expressions and syntax as C#.

2.3.1. Raincode Debugger Conditional Expressions

Relational
  • Equals: x == 90 x==1.4 x == “string”, x == z

  • Inequal: x!= 90.8 x != 1.4 x != “string” , x != y

  • Less than: x < 1 x < 30.5 y < x

  • Greater than: x > 1 x > 30.5 x > y

  • Less than or equal to: x ⇐ 1 x ⇐ 30.5 x ⇐ z

  • Greater than or equal to: x >= 230 x >= 30.5 x >= t

Logical
  • And: x == 90 && y < z

  • Or: x ⇐ y || str != “string”

  • Not: !(x ⇐ 0.10 && x >= 0.20)

Samples
  • (WorkVar.Id == “3D” && Desc.Name == “Peter”) || Desc.Age < 20

  • !Rate ⇐ 0.1 && Rate >= 0.2) || (Rate >= 0.50 && Rate ⇐ 30 && (Currency == “USD” || Currency == “$”)

2.3.2. Setting Conditional Breakpoints in the Visual Studio

  • Set the breakpoint, then right-click and select Settings

bp1
Figure 6. Set the breakpoint
  • Check condition Checkbox

bp2
Figure 7. Check the condition checkbox
  • Write the condition to break upon (when it’s true)

bp3
Figure 8. Write the condition
  • Close and you will see a red dot with a black cross in the middle

bp4
Figure 9. Conditional Breakpoint

2.4. Visualization Tools

Include and Call dependencies can be visualized as a graph. These graphs are trees displayed in a dockable tool window.

The project needs to be compiled recently to ensure that graphs are up-to-date since visualization tools rely on the compilation repository database as the data source.

2.4.1. Call Dependencies

To visualize the call dependencies:

  • Select ViewRaincodeCall Dependencies

calldependencies
Figure 10. Call Dependencies Menu
  • Select or type the project name in the ComboBox to display the graph.

graphdisplay
Figure 11. Call Dependencies Display
Call Dependencies for COBOL Local Calls

Select localcalls from the window toolbar to display all the local calls. It displays the relation between COBOL Paragraphs (a block of code starting at a label definition) and the Perform statements.

call cobol local calls
Figure 12. COBOL Local Calls
Call Dependencies for COBOL External Calls

Select externalcall from the window toolbar to display all the external calls. It displays the relation between COBOL Procedures (a block of code starting at a procedure division or an entry point definition) and the Call statements.

call cobol external calls
Figure 13. COBOL External Calls
Call Dependencies for PL/I

It displays the relation between Procedures and the Call statements.

call pl one External calls
Figure 14. PL/I Calls

2.4.2. Include Dependencies

  • Select ViewRaincodeInclude Dependencies

includedependencies
Figure 15. Include Dependencies Menu
  • Select or type the project name in the ComboBox to display the graph.

graphdisplay Include
Figure 16. Graph Display Include Dependencies
Include Dependencies for COBOL
includedepencobol
Figure 17. Include Dependencies for COBOL
Include Dependencies for PL/I
includedepenplone
Figure 18. Include Dependencies for PL/I

2.4.3. Toggle Direction

All graphs can be inverted using toggle Toggle direction button from the window toolbar. Toggle Direction is applicable for both the call and include dependencies.

The Include Graphs can display both the including and included by relations, and the Call Graphs can display the calling and called from relations.

2.5. Bracket Highlighting

When your cursor is positioned at the opening or closing brace, the corresponding brace gets highlighted to make the code more readable, as shown in the screenshot below. Bracket highlighting will work for (), {}, and [] and all languages (COBOL, PL/I, ASM, and JCL).

bracekthighlight
Figure 19. Bracket-highlighting

2.6. Theme

Raincode Language Service Visual Studio extension allows you to choose the theme for your IDE.

Visual Studio 2019

Select Tools→Options→General, and under the Visual Experience, you should find the Color Theme tab.

From the Color Theme, you can choose your preferred theme.

themeselection
Figure 20. Theme Selection-VS2019

Visual Studio 2022

Select Tools→ Theme. From the theme submenu, you can choose your preferred theme.

themeselection2022
Figure 21. Theme selections-VS2022

Following are the two options for the theme:

2.6.1. RCMainframe Blue

rcmainframeblue
Figure 22. RCMainframe Blue

2.6.2. RCMainframe Dark

rcmainframedark
Figure 23. RCMainframe Dark

2.7. Ignore code completion with given special characters

Raincode Language Service Visual Studio extension allows mentioning a list of special characters to ignore in code completion, i.e., when you key in any of those, code completion doesn’t trigger; instead, a special character will appear on the editor.

To add those special characters, select Tools→Options. Look for submenu Raincode, and under the Raincode, select General. A window will appear, as shown below in the screenshot. Add the special characters in the box provided. It provides different tabs for PL/I, COBOL, and HLASM.

specialcharacters
Figure 24. Add special characters to ignore in code completion

You can also turn off the code completion by checking the box Disable code completion

Example:

Since we have added the hyphen (-) as one of the special characters to ignore in code completion, when you input the hyphen (-), code completion won’t trigger. Instead, it lets you type in further.

chooserequiredvariable
Figure 25. Choose the required Variable

However, since we have not added an asterisk (*) as a special character if you input *, the code completion will choose the first available Variable (W-1) and add an * to the end of it.

appendspecialcharacter
Figure 26. Add special character to the end

2.8. Smart Tab and Backspace

Raincode Language Service Visual Studio extension provides a feature that has customized the Tab and Backspace indentation. Usually the Tab indentation is specified by the value mentioned in the Tools→ options → Text Editor → Cobol → Tabs.

In the customized one, pressing the Tab key aligns the cursor with the next filled position towards the right in the first non-empty row above. For the backspace, when the row is empty, the key should move the cursor to the left, aligning it with the beginning of the previous filled space in the row above.

Smart Tab and Backspace indentation are only available for COBOL.
Smart Backspace will work only for the empty line.

To enable the feature, select Tools→Options→Raincode→General. Check the box for enable smart tab and backspace.

enablesmarttabandbackspace
Figure 27. Enable smart tab and backspace

As you can see in the screenshot below. The tab is at character 18.

tabatcharacter18
Figure 28. Tab at character 18

Once you press, the tab takes you to character 23, aligned with the rightmost position in the first now-empty row above.

tabatcharacter23
Figure 29. Tab at character 23
Smart Tab indentation will yield unexpected results when used along with the intellisense. You have to either disable intellisense (How to enable or disable intellisense) or cancel the intellisense suggestions (using the Esc key) before pressing the tab.

Similarly, pressing the backspace key on an empty line will take the cursor to the left and align it with the beginning of the last filled space in the row above.

The screenshots below show the behavior of the backspace key.

Backspaceatcharacter12
Figure 30. BACKSPACE at character 12
Backspaceatcharacter8
Figure 31. BACKSPACE at character 8

2.9. Migration via Raincode Language Service Visual Studio extension

Raincode Language Service Visual Studio extension allows to manage migration in a user-friendly way via Microsoft Visual Studio© screens. The following sections contain images that are familiar to all Microsoft Visual Studio© users, but here they are geared particularly towards the Raincode Legacy Compilers user.

Follow the following sections effectively to use the Raincode Language Service Visual Studio extension.

2.9.1. Creating a Raincode Language Service project

Raincode Language Service supports custom project files for building legacy applications, referred to as Raincode project files or .rcproj files.

Raincode project files are built on top of the standard XML-based MSBuild project files supported by Visual Studio (For example, C# project files → .csproj files).

To improve the user experience in Visual Studio, the extension supports Raincode project templates for four different legacy languages:

  1. COBOL Application

  2. PL/I Application

  3. HLASM Application

  4. JCL Application

To create a project, follow the following steps:

  • Start the Microsoft Visual Studio©, and the Start Page screen will appear

startpage
Figure 32. Microsoft visual studio start page
  • Click Create a new project link on the Start Page screen or click on continue without code and select FileNew Project from the Microsoft Visual Studio© File menu bar, and the New Project screen will appear

image014
Figure 33. Microsoft Visual Studio interface - New Project screen
  • Select the type of application project (e.g. PL/I, COBOL, JCL or ASSEMBLER) and click Next

If project templates are not visible, scroll to the bottom of the list to find them or search them by typing the project in the Search for templates (Alt+S) box.
configurenewproject
Figure 34. Microsoft visual studio configures a new project
  • Enter the name of the project in the Project name box

  • Enter the path to the project files folder in the Location box or click the …​ button to navigate to the desired project files folder

  • If Place solution and project in the same directory check-box is checked, then a folder is created for the solution files, and inside that folder, another folder is created which contains the project. Name of the project is automatically taken by the name entered in the Solution name box.

checkbox selected
Figure 35. Create a directory for solution check box checked

If, however, Place solution and project in the same directory check-box is unchecked, then the solution files are created in the same directory as that of the project

checkbox not selected
Figure 36. Create the directory for the solution check box unchecked
If name is not provided in the Solution name box, then the default name in the Name box will be taken as the name of the solution.
  • Click the Next button, and the target framework selection screen will appear.

targetselection
Figure 37. Target framework selection screen
  • Click the Create button, and the screen will appear (See the section Adding Raincode Language Service items), showing the following windows:

    • The Solution Explorer window, with the following elements:

      • The name of the solution(s)

      • The name of each of the projects contained in the solution and each project will contain the name of each of the modules contained in each project

  • The Properties window:

    • Select any one of the elements in the Solution Explorer window, and its properties will be displayed in the Properties window

    • Use the Properties window to view and edit the properties of the selected item

    • By default, properties are grouped by category. Expand a category to see its properties

    • Show advanced properties by clicking the down arrow at the bottom of a category.

2.9.2. Adding Raincode Language Service items

The Raincode Language Service Visual Studio extension provides a number of different types of items (See section Available item templates) that allow you to work effectively with Raincode Legacy Compilers via Microsoft Visual Studio©; they can be found in the Add New Item screen, as shown in Add New Item screen.

To add a new item to the project, the following steps are required:

  • Right-click on the desired project in the Solution Explorer window, select AddNew Item,

image015
Figure 38. Microsoft Visual Studio interface - Add a module to a project

and the Add New Item screen will appear

image016
Figure 39. Raincode Language Service Visual Studio extension - Items
  • Click on the item which needs to be added to the project.

The available Raincode Legacy Compilers items are:

Table 1. Raincode Legacy Compilers available item templates
Available Items Description
image017 1

This creates an empty Assembler Module

image017 2

This creates an empty Assembler Include

image017 3

This creates an empty Assembler Macro

image019

This creates a new COBOL copybook

image020

This creates an empty COBOL module

image017

This creates an empty File Configuration

image022

This creates an empty JCL file

image021

This creates an empty PL/I include

image017 4

This creates an empty PL/I module

  • Click the Add button to add a new item to the existing project

  • To add an existing item, select Add Existing Item

Exisitingitem
Figure 40. Raincode Language Service Visual Studio extension - Add Existing Item
  • Select the desired item and choose Add. The selected item will be copied into the project directory. Alternatively, a shortcut pointing to the item can be added instead of copying the file. To create a shortcut, click the drop-down arrow next to Add and select Add As Link

image
Figure 41. Raincode Language Service Visual Studio extension - Add Item
  • Slightly different icons distinguish copied items and Linked items. As shown in the screenshot below CSMOD.cs is a copied item, and Customerlogger.cs is a linked item.

icon
Figure 42. Added existing item icon

2.9.3. Viewing/Editing a module’s contents

To view/edit a module’s contents, follow the following steps:

  • Select the relevant module in the Solution Explorer window, and a window displaying its contents will appear.

  • Select the variables that you wish to edit.

  • Once done with the edits, select File and the appropriate Save option to save the module’s file.

  • Raincode Language Service Visual Studio extension provides an option to compile a single module. Right-click on a legacy module and select Compile module in the menu to compile that module.

compile
Figure 43. Compile single module

This feature is unavailable for any C# (or other .NET language) code. The general behavior is to compile the whole project.

2.9.4. Building a project

To build a project after editing its contents, take the following steps:

Right-click on the relevant project in the Solution Explorer window and select the Rebuild option from the drop-down menu as shown in the Microsoft Visual Studio interface - Rebuild

image023
Figure 44. Microsoft Visual Studio interface - Rebuild

Depending on the result of the rebuild, you will see one of the messages described below:

  • If the rebuild has been successful, it will be indicated in the output window at the bottom of the screen:

********* Rebuild All: 1 succeeded, 0 failed, 0 skipped *********
Build Succesful
Figure 45. Build Successful
  • If the rebuild has been unsuccessful, it will be indicated in the output window at the bottom of the screen:

********* Rebuild All: 0 succeeded, 1 failed, 0 skipped *********
Build Unsuccessful
Figure 46. Build Unsuccessful

2.9.5. Pack

The Pack feature of the Raincode Language Service Visual Studio extension generates a NuGet package with all the assemblies generated during the compilation of the Raincode project.

The generated package file can be imported as any NuGet Package. All the assemblies contained in the imported package will be added as references in the project.

How to generate a NuGet package
  • Right-click on the project name in the solution explorer; from the context menu, select the Pack or select Pack from the Build menu

pack3
Figure 47. Context Menu Pack
  • A NuGet Package (.nupkg) will be generated with all the compiled assemblies.

pack4
Figure 48. Generated NuGet Package
Reconfiguring package properties
  • Right-click on the desired project and select the Properties option

pack6
Figure 49. Microsoft Visual Studio interface project configuration package properties tab
  • A window containing tabs related to the package properties will appear

pack5
Figure 50. Microsoft Visual Studio interface Project configuration package properties details

Properties that can be configured:

  • Generate NuGet package on build: If YES is selected, the package will be generated after each build on the project.

  • Package id: An identifier for the package. Defaults to the main assembly name. It will be used in the name of the package file and identify the package in package repositories.

  • Package Version: A Version number for the package.

  • Authors: The name(s) of the author(s) of the package. Defaults to the main assembly name.

Additional files can also be packaged by adding explicit packaging instructions to the project file:

  • Right-click the project name in the solution explorer and select the Edit option.

pack7
Figure 51. Additional files
  • Add the required lines to the project files.

For example, if the user wants all the copy books to be added to the package under the CopyBooks folder, they can add the following lines to the project file:

<ItemGroup>
    <Content PackagePath="CopyBooks" Include="**\*.cpy" />
  </ItemGroup>
pack8
Figure 52. Edit packaging instructions

2.9.6. Viewing a project’s error list

To view the list of errors for the active project, take the following steps:

  • Select the Error List option from the View menu.

  • The errors should appear at the bottom of the screen.

  • Clicking on errors brings you to the position in the code where the error occurs.

image024
Figure 53. Microsoft Visual Studio interface - Error list

2.9.7. Debugging a module

To debug a module, take the following steps:

  • Select the relevant module in the Solution Explorer window

  • Select the breakpoints where the execution should suspend

  • Run the code

    • Press Start Raincode Debugger, or press F5, or select Debug → Start Debugging from Debug menu bar of Visual Studio©.

Debug code
Figure 54. Debug
  • When execution suspends, the Locals [2], Call Stack [3], Watch [4], and IntelliTrace [5] windows will appear.

image025
Figure 55. Microsoft Visual Studio interface - Debugger
  • You may select variables to change their values on the window displaying the module’s source code.

image026
Figure 56. Microsoft Visual Studio interface - Change variable values

2.9.8. Reconfiguring a module

To reconfigure a module, take the following steps:

  • Right-click on the desired module in the Solution Explorer window and select the Properties item

image027
Figure 57. Microsoft Visual Studio interface - Reconfiguring a module
  • The Property page will appear on the screen.

property windows
Figure 58. Microsoft Visual Studio Interface Property Pages
  • Edit the property values as required.

The following is a list of the properties.[6], per item added to a project, shown in Raincode Legacy Compilers, Properties window (See the section Adding Raincode Language Service items):

  • General: Build Action options drop-down list includes:

    • None

    • Compile(PL/I)

    • Include

    • FileConfig

    • Compile(Cobol)

    • Compile(HLASM)

image027 1
Figure 59. Microsoft Visual Studio interface - Build action
  • Misc:

    • File Name: indicates the name of the particular project file

    • Force QIX: if set to Yes, the flag: QIX is passed to Raincode Legacy Compilers when the file is compiled. The drop-down options list includes

      • No

      • Yes

image027 2
Figure 60. Microsoft Visual Studio interface - Misc

2.9.9. Reconfiguring a project

To reconfigure a project, take the following steps:

  • Right-click on the desired project in the Solution Explorer window

  • Select the Properties option, and a window containing tabs relating to the project’s properties will appear.

image028
Figure 61. Microsoft Visual Studio interface - Project configuration
Project Properties

The following tabs are available to perform project reconfiguration:

image028 1
Figure 62. Microsoft Visual Studio interface - Project configuration Window
  • Microsoft Visual Studio’s© General tab: There are several options available in the general tab. One is Assembly Version, which allows you to set the version number of the generated DLLs by Raincode Legacy Compilers.

assemblyversion
Figure 63. Assembly Version
Before the build is started or after the build is finished, commands that execute are specified in pre/post build events. See the section Static execution mode and DB2 through HIS for more details.
  • Select the appropriate value(s) to edit and enter the new value(s)

  • Click the OK or Apply, to save the configuration file

Rebuild the solution if the name of connect string is modified in the Repository configuration.

2.9.10. SQL Rewriting

The SQL rewriting feature with the Raincode compilers will transform the SQL statements during the compilation process, but the source code remains intact. The Raincode Language Service Visual Studio extension allows you to see the transformed SQL statement by pointing the mouse to the original statement, as shown in the screenshot below:

image029
Figure 64. Microsoft Visual Studio interface - Project SQL Rewriting

2.9.11. Static execution mode

The static execution mode is not to be confused with static SQL. Static and dynamic SQL can be present in a single program, and a program can be executed in static or dynamic mode. For more details, see the section The virtues of static SQL.

To enable static execution mode, configure the following values in the project properties:

In the common properties tab:

  • General→Generate Static SQL, Select YES.

generate static sql his
Figure 65. Generate Static Sql
  • Rclrun→SQLExecutionMode, Select StaticOnly

Sqlexecutionmodehis
Figure 66. SQLExecutionMode
When Generate Static SQL is enabled, StaticOrDynamic execution mode is equivalent to Static execution mode.

The working of static execution mode depends on the type of connector used.

DB2 through HIS

In the case of HIS, the compiler generates an XML file that is to be converted into a package by a post-build event. In DB2 terms, creating a package through a file is called binding.

In the configuration tab, Build→Post-Build Event, add the following string as shown in the screenshot below

image028 2
Figure 67. Post Build Event

The @(BindFiles) variable can obtain a list of bind files. It will contain the list of bind files separated with a semicolon.

DB2 through DB2 Connect

In the case of DB2 connect, the compiler generates an SQL script that creates stored procedures. The script is then executed via a post-build event.

In the configuration tab, Build→Post-Build Event, add the following string as shown in the screenshot below

postbuildeventsconnect
Figure 68. Post Build Event
Ensure to remove <> (angle brackets) while entering the name of the database, server, username, password and filename.

To support DB2 style CONNECT statement, there is a requirement to Catalog the connection. It has been done beforehand using the below mentioned commands.

db2cmd -i -w db2clpsetcp
    db2 catalog tcpip node <alias> remote <Server> server <portnumber>
    db2 catalog database <Database> at node <alias>
    db2 terminate
    db2 list database directory
SQLServer

In the case of SQL Server, the compiler generates an SQL script that creates stored procedures. The script is then executed via a post-build event.

  • In the configuration tab, Build→Post-Build Event, add a call to sqlcmd with the file containing the stored procedures as an argument.

post build events
Figure 69. Post Build Event
Make sure to remove <> (angle brackets) while entering the name of database, server, username, password and filename.

2.10. Profiling in Visual Studio

Microsoft Visual Studio © includes a comprehensive profiler which can find performance hotspots in any .NET project. When compared with C#, though using it together with COBOL or PL/I introduces subtle differences one must be aware of:

The profiler works mainly at the function level. The Raincode legacy compilers sometimes generate additional ancillary functions (when calling an external program) and sometimes generate fewer functions than one would expect (when a single big function is produced for large COBOL programs).

Visual Studio profiler is a very effective tool for objectively analyzing the performance of a compiled application. However, it may fail to show the module names of programs compiled using the Raincode compilers, showing a hexadecimal number instead, as shown in the screenshot below:

image030 cropped
Figure 70. Microsoft Visual Studio-Profiling

To address this issue, one must select the Tools followed by Options menu option and select the Debugging followed by the Symbols tab in the various options one can choose from.

image031 cropped
Figure 71. Profiling

One can then add a source for symbols to be used by the debugger. While this can be used to connect to shared symbol servers, it can also specify a directory in which the .DLL and the PDB files will be found, as shown in the screenshot below [7].

vs profiler symbols location
Figure 72. Profiler Symbols Location

One must then exit and restart Visual Studio before inspecting profiling traces where more comprehensive names now replace the cryptic hexadecimal numbers.

vs profiler correct names
Figure 73. Profiler correct names

Besides, if the module has been compiled with the :Debug flag enabled, functions representing COBOL or PL/I programs can be zoomed in for visual identification of performance hotspots using color codes:

vs profiler visuals
Figure 74. Profiler Visuals

2.11. General debugging options

To configure the Raincode Language Service Visual Studio extension’s debugger options, select Tools→Options and select General under Raincode.

vsdebug 1
Figure 75. Raincode Language Service Visual Studio extension options

2.11.1. Logging

To improve the diagnostics and troubleshooting experience Raincode Language Service Visual Studio extension logs important messages about its operation.

The log file can be found at %APPDATA%\RaincodePlugin\log.log on the user’s system.

The extent of logging can also be modified by setting the Log Level to the appropriate value.

loglevel
Figure 76. Log level

2.11.2. File synchronization

filessynchro
Figure 77. File synchronization

This submodule automatically searches for and downloads the copybook files located on the server and used by the currently opened source file.

When the checkbox checkbox1 is checked, the extension will include all the file extensions.

When the checkbox checkbox is checked, the extension will find the root project directory based on the file being parsed and the name of the project as given in Source control project. Starting from the root, all subdirectories will be scanned in order to search for the copybook files needed when parsing the current file.

2.11.3. Disable code completion

Select the checkbox to disable the code completion for the required language.

disablecodecompletion
Figure 78. Disable code completion

2.11.4. Directories containing systemwide copybooks

This submodule allows users to specify absolute and relative directory paths, giving more flexibility for the locations of the copybook files and more control over the places the extension will look for them.

In Directory paths (absolute), the user can specify a set of absolute paths towards directories containing copybook files.

directoryabsolute
Figure 79. Directory paths (absolute)

Furthermore, the user can specify a set of paths relative to a base directory. This base directory can be selected in the Root directory field, using the Browse…​ button.

rootdirectory
Figure 80. Base directory for relative paths

If the Scan all projects for CopyBooks checkbox is checked, the extension will automatically scan all of the project subdirectories of the base directory to find the copybook files. If it is not checked, the extension will look for the copybook files in the directories specified in the Directory paths (relative to the root directory) list. All of the entries have to be manually added by the user.

setofrelativepath
Figure 81. Set of a relative path
File synchronization submodule and Directories containing systemwide copybooks submodule are independent of each other.

2.12. Upgrading out-of-date Raincode projects

As the Raincode Language Service extension evolves, so does the structure of Raincode project files or .rcproj files. This means that by installing a new version of Raincode Legacy Compilers and Tools, old .rcproj files might become incompatible and even fail to load or build in Visual Studio. One of Raincode Language Service extension features is to detect this incompatibility and automatically upgrade the .rcproj into the new format.

Open the Visual Studio solution containing Raincode projects to upgrade out-of-date project files.

If an incompatibility is detected, a dialog box will appear asking about the action to perform.

project upgrade dialog
Figure 82. Automatic project file upgrade dialog

Click Yes to perform the upgrade, after which the confirmation window will appear.

If you click No, the project upgrade will not be performed, and project files will remain incompatible. If you wish to repeat the process, close the solution and reopen it.
project upgrade success
Figure 83. Successful upgrade confirmation

In the confirmation window, click OK. The project will be loaded in the Visual Studio environment.

The old project file is backed-up and stored in the same directory with .bck extension during the upgrade.

This can be used to inspect what happened during the upgrade process.

project upgrade backup
Figure 84. Old project file backup

2.13. Barthol Bank: A Sample Visual Studio - project

2.13.1. Introduction

The Barthol Bank project is a sample Visual Studio project provided as part of the Raincode COBOL compiler distribution. It implements a simplistic bank with accounts and transactions. Even if simplistic, it is not just a sample project without a true purpose. At Raincode, the Barthol Bank is run by our colleague Yannick Barthol (hence the name), who volunteered to manage sandwich money for the entire staff graciously, even if it may also be a way of ensuring supply. Food is an important matter to Yannick.

The Barthol Bank is quite unusual. It is lenient when payment is overdue, and does not pay interests on deposits either. The application is made of a number of COBOL modules. It uses sequential-indexed files for storage, so that it can be run out of the box without having to configure relational database access prior to execution.

2.13.2. Barthol Bank

Double-click the Barthol Bank.sln solution file in the sample directory to Execute Barthol Bank.

image035
Figure 85. Barthol Bank

Press the F5 key or click the Start Raincode Debugger option in the DEBUG menu to compile and start the Barthol Bank Project.

image036
Figure 86. Barthol Bank-Debug
image036 a
Figure 87. Barthol Bank Deubug-1
image036 b
Figure 88. Barthol_Bank_Debug-2

2.14. Sample JCL Application

Steps required to run JCL in Raincode Language Service Visual Studio extension:

  • Open JCL Project:

    • Select File→New Project or Select File→Open Project/Solution from the file menu bar of Visual Studio©

  • Build/Rebuild the Project:

    • Right-click on JCL Project and Select Build/Rebuild or Select Build→Build Solution/Rebuild from the Build menu bar of Visual Studio©

  • Once Build/Rebuild is successful:

    • Press F5 or Press Start Raincode Debugger, or Select Debug→Start Debugging from Debug menu bar of Visual Studio©

  • The steps executed can be seen in the output window.

The example shown below in the screenshot is a test program that tests GDGs using IDCAMS.

samplejcl
Figure 89. Sample JCL Example

2.14.1. Run JCLs separately

Raincode Language Service Visual Studio extension allows you to execute individual JCLs separately when your project contains multiple JCLs.

Execute the following steps to run JCLs:

  • Right-click on the JCL file in the solution explorer. Choose Run JCL Program from the context menu.

runjclprogram
Figure 90. Run JCL Program
  • The JCL Runner window will appear. Select OK to proceed.

JCLrunner
Figure 91. JCL Runner

2.15. Sample ASM Application

Steps required to run ASM in Raincode Language Service Visual Studio extension:

  • Open ASM Project:

    • Select File→New Project or Select File→Open Project/Solution from the file menu bar of Visual Studio©

  • Build/Rebuild the Project:

    • Right-click on ASM Project and Select Build/Rebuild or Select Build→Build Solution/Rebuild from the Build menu bar of Visual Studio©

  • Once Build/Rebuild is successful:

    • Press F5 or Press Start Raincode Debugger, or Select Debug→Start Debugging from Debug menu bar of Visual Studio©

sampleASM
Figure 92. Sample ASM Example

3. dotnet CLI integration

Only available with Raincode Net60 Installer

Apart from integrating Visual Studio, Raincode Language Service supports integration with dotnet CLI. dotnet CLI integration is beneficial since it enables the migration and development of legacy applications with just a command prompt and a text editor.

3.1. Raincode project and item templates

After the successful installation of Raincode Language Service packages described in the installation guide, users can check the available templates of dotnet new.

  • Run the launcher.bat from the installation directory and execute:

     dotnet new --list

You should find Raincode project and item templates in the list of available templates, as shown in the picture below.

dotnet new list
Figure 93. Available project and item templates

3.1.1. Creating COBOL application from the command prompt

The previous section shows that all Raincode templates are integrated with dotnet new commands. To use a specific template, specify its short name with dotnet new command with desired parameters.

We will focus on COBOL templates in the following examples, but all the templates can be used similarly.
Default dotnet new options can be found here.
Before proceeding, make sure that the current directory in the command prompt does not require administrator permissions.
  • To see help details of the COBOL application template execute:

     dotnet new rccobol -h
dotnet new project template help
Figure 94. dotnet new Project template help
  • To create a COBOL application targeting net6.0 framework (which is the default), execute:

     dotnet new rccobol -n "MyCobolProject"
dotnet new create cobol
Figure 95. dotnet new create COBOL application
  • Inspect the result of the previous operation by executing the following sequence of commands:

     cd MyCobolProject
     more MyCobolProject.rcproj
inspect created cobol project
Figure 96. Inspect created COBOL project file
  • To see help details of the COBOL module template, execute:

     dotnet new rccobolmodule -h
dotnet new item template help
Figure 97. dotnet new Item template help
  • To add a COBOL module named MYCOBMOD to the previously created project, execute:

     dotnet new rccobolmodule -n MYCOBMOD
dotnet new create cobol module
Figure 98. dotnet new create COBOL module
  • Inspect the result of the previous operation by executing the following sequence of commands:

     dir
     more MYCOBMOD.cob
inspect created cobol module
Figure 99. Inspect created COBOL module file
  • To build the project execute:

     dotnet build
dotnet build output
Figure 100. dotnet build output
  • After the successful build, you can run the application by executing:

     rclrun MYCOBMOD :AddAssemblySearchDir=.\bin\Debug\net6.0
run MYCOBMOD application
Figure 101. Running the MYCOBMOD application
The last step assumes rclrun has been added to the PATH environment variable.

3.2. Manually upgrading out-of-date Raincode projects

Apart from the automatic upgrade of Raincode project files described here. It is also possible to upgrade out-of-date project files with the RaincodeProjectUpgradeTool manually. Once successfully installed (See: Install RaincodeProjectUpgradeTool), Raincode project files can be upgraded by invoking raincode_project_upgrade_tool from the command prompt.

  • To see help details of the tool, execute:

     raincode_project_upgrade_tool -Help
raincode project upgrade tool help
Figure 102. RaincodeProjectUpgradeTool help
  • To upgrade a single file, execute:

     raincode_project_upgrade_tool -File=Projects\Proj1\Proj1.rcproj
raincode project upgrade tool file
Figure 103. RaincodeProjectUpgradeTool upgrade file
  • To upgrade all .rcproj files in a folder, execute:

     raincode_project_upgrade_tool -Dir=Projects -Recursive
raincode project upgrade tool dir
Figure 104. RaincodeProjectUpgradeTool upgrade all .rcproj files in folder
  • If a project file is not compatible for the upgrade, it will be intact with the message:

raincode project upgrade tool fail
Figure 105. RaincodeProjectUpgradeTool upgrade file failure

4. Compiling programs in the Command Prompt Window

Raincode Legacy Compilers comes with three command-line executables for the compilers; this depends on the language to be compiled: plirc.exe for PL/I, cobrc.exe for COBOL and hlasmrc.exe for ASSEMBLER.

The runner is same for all the languages.

The command-line executables for the Raincode Legacy Compilers are described in the following sections.

4.1. The plirc.exe PL/I command-line executable

The plirc.exe is the Raincode Legacy Compilers executable image for PL/I programs. By running plirc.exe, you can check the following:

4.2. The cobrc.exe COBOL command-line executable

The cobrc.exe is the Raincode Legacy Compilers executable image for COBOL programs. By running cobrc.exe, you can check the following:

4.3. The hlasmrc.exe ASSEMBLER command-line executable

The hlasmrc.exe is the Raincode Legacy Compilers executable image for Assembler programs. By running hlasmrc.exe, you can check the following:

4.4. RCLRUN

The rclrun is a stand-alone utility, which can be used to run legacy programs which are compiled by Raincode Legacy Compilers in the form of a DLL (See section EXE vs DLL). It provides a number of command-line arguments to control how the program should be run, as a standalone program or as a CICS transaction , which relational database it should connect to, etc.

Externalizing these parameters - as such concerns are not to be addressed in the application programs - allows for a more transparent migration. The legacy source code can be moved, as is (See section Single sourcing), from the mainframe, and the idiosyncrasies of its execution environment can be reproduced by using the adequate rclrun command-line options (See section rclrun command-line options).

4.5. Sample programs

There are few sample legacy programs included in the Raincode Legacy Compilers distribution. The procedure for all legacy programs supported by Raincode Legacy Compilers, is the same.

Before compiling and running any Legacy program, location of the ilasm and peverify tools must be specified appropriately in the rc_config.xml configuration file (See appendix rc_config.xml configuration file).

A few environment variables must be set; open Command Prompt window and execute the batch file named setenv_rccomp.bat:

>C:\Program Files\Raincode\Compilers\setenv_rccomp.bat
Setting up Raincode Legacy Compilers environment

Alternatively, run the Raincode Legacy Compilers Command Line launcher.bat from %RCDIR%:

Command Line launcher
Figure 106. Command Line launcher

Raincode Legacy Compilers and additional tools are now made available in PATH environment variable.

4.5.1. Compile and run hello.pli

To compile and run sample program hello.pli, run a command:

C:\Program Files\Raincode\Compilers\samples>plirc :EXE hello.pli

This will generate an executable hello.exe file that can be executed:

C:\Program Files\Raincode\Compilers\samples>HELLO.exe

The output will be Hello World!

Alternatively, you can compile this sample program to a DLL:

C:\Program Files\Raincode\Compilers\samples>plirc hello.pli

and execute the generated DLL file using rclrun (See section RCLRUN):

C:\Program Files\Raincode\Compilers\samples>rclrun HELLO

The output will be Hello World!

PL I sample
Figure 107. Sample program hello.pli

4.5.2. Compile and run hello.asm

To compile and run sample program hello.asm, run a command to generate a DLL:

C:\Program Files\Raincode\Compilers\samples>hlasmrc hello.asm :MaxMem=400 :IncludeSearchPath="C:\Program Files\Raincode\Compilers\includes\hlasmrc\wellknown;C:\Program Files\Raincode\Compilers\includes\hlasmrc\internal"

and execute the generated DLL file using rclrun:

C:\Program Files\Raincode\Compilers\samples>rclrun hello.dll -StringRuntimeEncoding=IBM037

The output will be Hello World!

hlasm sample
Figure 108. Sample program hello.asm
For more detailed information on the command-line compilation options available in Raincode Legacy Compilers (See section Raincode Legacy Compilers options).
For detailed information on the list of accepted encoding names, refer to List of encodings.

5. Raincode Legacy Compilers options

This section describes how to use the compile time command-line options available in Raincode Legacy Compilers - see the sections below for details on each available option. To run a legacy program after compiling it with Raincode Legacy Compilers see sections RCLRUN and Running compiled programs.

The Raincode Legacy Compilers are used by running the following executables in the Command Prompt window:

Note that, you may also run the Raincode Legacy Compilers via the Raincode Legacy Compilers Visual Studio interface (see sections Visual Studio Integration and Reconfiguring a project).

5.1. Raincode Legacy Compilers command-line options

Raincode Legacy Compilers command-line options can be specified using a leading colon : or a dash -. The Raincode Legacy Compilers also accept response files (see the sample response file below) prefixed by the @ character (e.g. plirc.exe @file.resp program.pli), and containing any combination of command-line options and source file names to compile. A typical response file looks like:

:M64
:SQL=sqlserver
:SQLServerSchemaCacheFile=schema.xml
:DLL
:DBDriver=SQLITE
:DBConnectString=results.db3
:MaxMem=1000
:FirstUsableColumn=2
:LastUsableColumn=72
:IncrementalMode=TRUE

5.2. Command-line options common to plirc.exe, cobrc.exe and hlasmrc.exe

5.2.1. Repository

Table 2. Command-line options common to plirc.exe, cobrc.exe and hlasmrc.exe
Command-line option Default value Description

AdditiveRepository

FALSE

Indicates that existing entries in the compilation repository must not be removed when compiling a module. This allows for the repository to contain information related to multiple compilation runs applied to the same program, or multiple programs denoted by the same name (Written in the same language or not). When this option is activated, it is the user’s reponsibility to ensure that the repository database is emptied from time to time. This is a boolean value.

DBConnectString

The repository connection string.

Also refer to:

  • DBDriver

DBDriver

Sets the connection string to use for the repository persistence system as an ODBC connection string if ODBC persistence is used, or as a physical file name if SQLITE is used instead. Values can be any of the following:

  • ODBC

  • Sqlite

Also refer to:

  • DBConnectString

RepositorySaveErrorContext

FALSE

Indicates whether the context (lines before and after the error in the source file) should be stored in the repository database for reference. This is a boolean value.

5.2.2. Code generation

Command-line option Default value Description

AllowCallInterception

FALSE

Allow executing an interception method which is registered with the program executable at runtime. This is a boolean value.

DLI

FALSE

Generate code for DLI, even in the asbence of any DLI statement in the source programs. This is a boolean value.

Also refer to:

  • EXE

  • DLL

  • QIX

DLL

TRUE

Generate a DLL file. This is a boolean value.

Also refer to:

  • EXE

  • QIX

  • DLI

Debug

FALSE

Add debug statements in the compiled code to allow for debugging. This is a boolean value.

DebugInfoReduceSize

FALSE

When -Debug is given, reduce the size of the debug information at the loss of some debug functionality (currently, only the ability to edit variables during debugging is removed). This is a boolean value.

EXE

FALSE

Generate an EXE file (unless it would be incompatible with another command-line option). This is a boolean value.

Also refer to:

  • DLL

  • QIX

  • DLI

GenerateMap

FALSE

Generate a map file with all declarations, sizes and allocation info. This is a boolean value.

M32

TRUE

Generate a 32 bits .NET assembly. This option is ignored when generating DLLs. This is a boolean value.

Also refer to:

  • DLL

  • EXE

  • M64

M64

FALSE

Generate a 64 bits .NET assembly. Internally, PL/I and COBOL pointers are represented on 32 bits for strict compatibility with the mainframe. This option is ignored when generating DLLs. This is a boolean value.

Also refer to:

  • DLL

  • EXE

  • M32

PEVerifyOptions

Options to pass to PEVerify.

QIX

FALSE

Generate code for QIX, even in the absence of any QIX statement in the source programs. When code is generated in this mode, DLL’s are produced even if the :EXE option has been specified explicitly. This is a boolean value.

Also refer to:

  • DLL

  • EXE

RegisterParamTypeDescriptors

FALSE

Indicates whether the type descriptors for parameters should be generated in calls. This is a boolean value.

RegisterTypeDescriptors

FALSE

Indicates whether the type descriptors for variables should be saved in the module for easy retrieval at runtime. This is a boolean value.

RemoveFileVerifyFailed

TRUE

Indicates whether generated files should be removed if the standard .NET verification utility returns errors. This is a boolean value.

Target

NetCore

Specify the target framework used. Default may be set by environment variable RCTARGET=<NetFramework/NetCore/Mono/net48/net6.0>. net48 is an alias of NetFramework, net6.0 is an alias of NetCore. Values can be any of the following:

  • NetFramework

  • NetCore

  • Mono

  • net48

  • net6.0

UseFilenameAsModuleName

FALSE

Uses the file name as name for the generated assembly instead of the name of the main procedure. This is a boolean value.

VerifyIL

FALSE

Indicates whether the standard .NET verification utility peverify should be used to test the output of the compile for consistency. This is a boolean value.

VerifyMemory

FALSE

Indicates whether memory verification statements should be generated in the compiled program. This is useful to detect memory overrun during the execution. This is a boolean value.

Win32ResourceHandling

:internal_obj

Tells the compiler if and how it should add win32 resources to the generated PE file. Note that adding win32 resources is available only on Windows. The resources are added to the PE file by using ilasm’s /RESOURCE argument. The possible values for the option are: :none don’t add win32 resources :internal_res generate a .res file for ilasm (only if -Target=NetFramework) :internal_obj generate a .obj file for ilasm :msrc use rc.exe (it must be in the path or defined in rc_config.xml, and the right environment variables have to be defined; only if -Target=NetFramework) <path> path to an already compiled resource file. It will be passed straight to ilasm.

5.2.3. Assembly attributes

Command-line option Default value Description

AssemblyCompanyAttribute

Sets the assembly company attribute.

AssemblyConfigurationAttribute

Sets the assembly configuration attribute.

AssemblyCopyrightAttribute

Sets the assembly copyright attribute.

AssemblyDescriptionAttribute

Sets the assembly description attribute.

AssemblyProductAttribute

Sets the assembly product attribute.

AssemblyTitleAttribute

Sets the assembly title attribute.

AssemblyTrademarkAttribute

Sets the assembly trademark attribute.

AssemblyVersionAttribute

Sets the assembly version attribute.

5.2.4. .NET Interface

Command-line option Default value Description

CommentHelpers

TRUE

Indicates whether a summary comment should be generated on the helper class with generation date and time, compiler version, and used copybooks. This is a boolean value.

DeclDescriptors

Generate XML-based descriptors for the declarations found in the source. If an argument is given, it is used as filename to store the descriptor. Otherwhise, if the option is used without an explicit argument value, the output filename is built by appending .decl.xml to the source file name. This option can be used when the input is a full-fledged program, or a stand-alone COBOL copybook.

HelperClassName

Produce the helper class, structured with a namespace qualifying prefix followed by the class name.

HelperClassOutputDir

Sets the output directory for the generated helper classes. If this option is not set, :OutputDir will be used by default.

Also refer to:

  • OutputDir

HelperStructure

Uses the variable <var> from the program at hand as a model for generatiing helper classes. This option can be used when the input is a full-fledged program, or a stand-alone COBOL copybook.

ShortcutHelpers

TRUE

Indicates whether properties should be generated for nested sub-structure fields. This is a boolean value.

5.2.5. SQL

Command-line option Default value Description

DB2ClearRowsetPositioning

FALSE

Clear FOR ROWSET POSITIONING in DB2 DECLARE CURSOR. This is a boolean value.

DB2CollectionId

{COLLECTION}

Specify the collection id to use when generating DB2 bind files for HIS.

DB2CurrentAsRowId

FALSE

Implement WHERE CURRENT OF cursor as a current row id. This is a boolean value.

DB2GenerateLUWPackage

FALSE

Generate DB2 packages for LUW target. This is a boolean value.

DB2PackageVersion

YYYY-MM-DD-hh.mm.ss.0000000

DB2 packages version.

DB2STTDATFMT

Specify option STTDATFMT when generating DB2 bind files for HIS. The STTDATFMT element informs the DRDA Server which statement date format to use in SQL statements. This optional element accepts a string value. There is no default value for this element.

DB2StoredProcDefaultParamMode

INOUT

DB2 stored procedure parameter passing mode if signature not available. Values can be any of the following:

  • INOUT

  • IN

  • OUT

DB2StoredProcSignatures

DB2 stored procedure signatures filename. Each line contains a signature: CREATE PROCEDURE <procname>(<mode> <name> <type>,…​) where CREATE PROCEDURE, <name>, and <type> may be omitted e.g.: <procname>(<mode>,…​) <mode> is IN, OUT, or INOUT <mode> may also be omitted in which case it is assumed to be IN lines starting with ';' or '#' are comment lines.

GenerateStaticSQL

TRUE

Generate stored procedures (SQLServer) or bind files (DB2). This is a boolean value.

IgnoreWithHold

FALSE

Ignores the WITH HOLD clause on cursors. All cursors are considered WITHOUT HOLD, so there is no need for server-based cursor together with their attached performance penalty. This is a boolean value.

SQL

Sets a target database for the compilation. This option can be used multiple times to specify multiple target databases to be supported by the currently compiled module. Values can be any of the following:

  • NONE

  • DB2IBM

  • DB2HIS

  • SQLSERVER

  • ORACLE

  • POSTGRES

SQLDEC15

FALSE

Use DEC15 rules for SQL DECIMAL precision. This is a boolean value.

SQLErrorCodeTarget

DB2

Defines the set of error codes expected by the compiled program in terms of the SQL dialect it was originally written for. Values can be any of the following:

  • DB2

  • SQLServer

  • ORACLE

SQLGenerateCanonic

FALSE

Generate canonic information for SQL statements. This is a boolean value.

SQLLockTableAsCommand

FALSE

Generate SQL command for LOCK table. This is a boolean value.

SQLSelectIntoReturnsFirstRow

FALSE

Ensure that the row returned by SELECT INTO is the first row returned by the DB. This is a boolean value.

SQLSourceDialect

DB2

The SQL dialect of the source files. Values can be any of the following:

  • DB2

  • SQLServer

  • ORACLE

SQLStoredProceduresSchemaName

The name of the SQL Server schema containing generated stored procedures.

SQLTrimInputStrings

FALSE

Strips trailing blanks before sending the value to the database. This is a boolean value.

SqlDelim

APOST

Specify the string delimiter to use in SQL queries. APOST (default) means single quote as string delimiters and double quote for sql identifiers. QUOTE means double quote as string delimiters and single quote for sql identifiers. Stored as the 'SqlDeli' attribute on the 'SqlStatements' element in the .sql.xml bind file. Values can be any of the following:

  • APOST

  • QUOTE

SqlOutputDir

Sets the output directory for the bind files or stored procedure files produced by the compiler to support static SQL statements. If this option is not set, :OutputDir will be used by default.

Also refer to:

  • OutputDir

TraceSQLOperationsOnTables

FALSE

Print the operations (Read, Delete, Insert, Update) for each table in a SQL statement on the standard output. This is a boolean value.

TraceSQLStatements

FALSE

Print on the standard output the rewritten SQL. This is a boolean value.

5.2.6. Compilation

Command-line option Default value Description

DependencyMode

FALSE

Only compile programs that need recompiling based on the information found in the repository database. This is a boolean value.

Also refer to:

  • IncrementalMode

IncrementalMode

FALSE

Only compile programs that are not in the repository database yet. This is a boolean value.

Also refer to:

  • DependencyMode

5.2.7. Miscellaneous

Command-line option Default value Description

DumpFullParseTree

FALSE

Output an XML parse tree of every input file on a file named with the parsed source file name with the forced .xml extension. This parse tree is complete in the sense that it includes all attributes, even those that hold their corresponding type’s default value. This is a boolean value.

DumpLicenseFiles

Dump all license files found in LicenseFilePath. This is a boolean value.

DumpParseTree

FALSE

Output an XML parse tree of every input file on a file named with the parsed source file name with the forced .xml extension. This parse tree is made more compact by omitting all attributes that hold the corresponding type’s default value. This is a boolean value.

KeepTempFiles

FALSE

When parsing source files, this tool uses temporary files. By default, these files are deleted at the end of the process. This option can be used to ensure that these files are preserved rather than deleted. This is a boolean value.

LicenseFilePath

Points to the licenses file directory where to look for *.rclic.

MaxMem

Can be used to specify the amount of available memory the process can use, specified in megabytes as a rough estimate.

MergeOutputStreams

Merges the standard error stream with the standard output stream, so that a redirection captures all messages in the appropriate sequence. This is a boolean value.

ParallelThreadCount

0

If greater than 0, specifies the maximum number of compilations to run simultaneously.

RcDebug

Starts the script execution in interactive debug mode. When used as :RcDebug or RcDebug=CLI, starts in gdb’s command line interface mode. When used as :RcDebug=MI, starts in gdb’s machine interface mode. One can also give an IP address and port number after a slash character, as in :RcDebug=MI/127.0.0.1:61083 to indicate that the input and output must be redirected through a socket, which must have been opened by the caller.

TempPath

Sets the temporary directory, where temporary files are created. If not specified using this configuration variable, the system environment variables TEMP and TMP will be used instead.

Also refer to:

  • HomePath

VerboseExternalCommands

FALSE

Display external commands executed by the compiler. This is a boolean value.

Warnings

Configures warning messages, globally or one by one (depending on the code), by using one or more commands separated by commas. Supported commands are:

-?: display the list of the supported warnings, and their current setting

-<n1> or <n1>:<n2>: enable warnings with code equal to n1 or between n1 and n2 (if the second form is used)

--<n1> or -<n1>:<n2>: disable warnings with code equal to n1 or between n1 and n2,

-+<n1> or +<n1>:<n2>: force warnings with code equal to n1 or between n1 and n2 as errors. A wildcard "*" can be used to operate on all warning messages instead of explicitly referring to any specific single warning or range of warnings.

For instance, a command-line option such as ':Warnings=-*,1070,+1100' first disables all warning messages, and then enables the one with code 1070, and the warning with code 1100 which is then considered as an error. One can also ensure that the appropriate warning messages have been altered, by using a command-line option such as: ':Warnings=-*,1070,+1100,?'.

5.2.8. Parsing

Command-line option Default value Description

ErrorLimit

0

Maximum number of errors that will be reported by the parser before aborting the parsing process altogether.

StepLimit

5000

Number of steps the parser must execute while backtracking before considering that no parse tree can be recognized based on the current input file. In theory, the default value of 5000 should cover most cases gracefully. A parser that does not find a way to go forward after a few hundred iterations usually can be considered as lost, and will most likely not be able to parse the input file. This limit has been set to avoid that the parser considers an exponential number of cases without much hope of finding anything useful anyway.

5.2.9. Input

Command-line option Default value Description

FirstUsableColumn

1

Sets the first usable column in the input file (starting with 1). When the source file is a plain ASCII file compiled using a modern compiler that supports free form, the first usable column would typically be 1, while a source file compiled with an older mainframe compiler may use the first characters for line numbering, in which case 7 is a reasonable value.

Also refer to:

  • LastUsableColumn

  • IgnoreClassification (Cobol only)

  • Margins

LastUsableColumn

400000

Sets the last usable column in the input file (1 denotes the first column). If the source file is compiled with an older mainframe compiler, it may use the characters above index 72 for comment or versioning purposes.

Also refer to:

  • FirstUsableColumn

  • IgnoreClassification (Cobol only)

  • Margins

Margins

1-400000

Serves as a shortcut, setting both :FirstUsableColumn and :LastUsableColumn in a single command.

Also refer to:

  • FirstUsableColumn

  • IgnoreClassification (Cobol only)

  • LastUsableColumn

TabSize

8

Sets the number of spaces to align tab characters on. At load time, all Tab are replaced by spaces. If set to 0, no tab expansion take place.

5.2.10. IDMS

Command-line option Default value Description

IdmsInstance

Specify the value to use for the --instance parameter when calling lz_idms_copy.

5.2.11. Preprocessing

Command-line option Default value Description

IncludeCaseInsensitive

FALSE

If set, include filenames are not case sensitives. Setting this option has no effect on hosts with non-case sensitive file systems (e.g. Windows). This is a boolean value.

IncludeExtension

Provides a comma- (or semicolon-) separated set of extensions to use when opening include files. '.' character must be explicitly specified. Example: :IncludeExtension=.foo,.bar.

Also refer to:

  • IncludeSearchPath

  • IncludeSearchDir

IncludeSearchDir

Generalizes :IncludeSearchPath by allowing a directory and all its sub-directories to be searched recursively when looking for include files. Several paths can be specified separated by ',' or ';' or by specifying this option multiple times on the command-line. Directories are searched in the order specified on the command line, with the exception of the the current directory '.' which is always checked first when looking for include files.

Also refer to:

  • IncludeSearchPath

IncludeSearchPath

Include a search path for include files. Several paths can be specified separated by ',' or ';', by specifying this option multiple times on the command-line or by using the INCLUDE environment variable. Directories are searched in the order specified on the command line, with the exception of the the current directory '.' which is always checked first when looking for include files.

Also refer to:

  • IncludeSearchDir

5.2.12. Output

Command-line option Default value Description

MemoryMapOutputDir

Sets the output directory for the memory maps. If this option is not set, :OutputDir will be used by default.

Also refer to:

  • OutputDir

Output

Overrides the default target filename for the compiler. This option should not be used with wildcards, as all the resulting object files will be written to the same target filename, each compiled module overwriting the previous one.

Also refer to:

  • OutputDir

OutputDir

Sets the folder where the generated object and executable files must be stored. This option can be used when compiling multiple legacy programs in a single run using wildcards. The resulting object files will be stored in the specified output folder, independently from the folder in which the corresponding source file was read.

5.2.13. Profiling

Command-line option Default value Description

ProfileMode

FALSE

Enables profiling mode, which will produce a file named rc.xls containing the number of calls, the intrinsic time (spent in the procedure, excluding time spent in sub-procedures), average and maximum, and the total time (including sub-procedures), average and maximum, per procedure defined in the script. This is a boolean value.

5.2.14. SQLServer

Command-line option Default value Description

RetrieveSQLMetaData

TRUE

Retrieve the SQL meta-data from the data source specified. This is a boolean value.

Also refer to:

  • SQLServerConnectionString

SQLDebugStoredProcedures

TRUE

Generate debug calls to the stored procedures. This is a boolean value.

SQLRewritingRulesFile

The name of a file containing rules for rewriting SQL expressions. This option can be specified multiple times.

SQLSemanticRulesFile

The name of a file containing semantic definitions for SQL functions. This option can be specified multiple times.

SQLServerCommonSchemaName

Raincode

The name of the SQL Server schema to be used by the emulation functions.

SQLServerConnectionString

The connection string of the .NET Framework Data Provider for SQL Server or the ODBC data source to retrieve SQL meta-data.

Also refer to:

  • RetrieveSQLMetaData

SQLServerDefaultSchemaName

dbo

The name of the SQL Server schema to be used by the SQL statements.

SQLServerDefaultSchemaOnly

FALSE

Only searches for tables and views in the default schema. This is a boolean value.

SQLServerDefaultSchemaToken

The token to be used by the generated SQL statements for the default schema.

SQLServerInnerJoinKeepOnCondition

FALSE

Prevents INNER JOIN on condition from being moved to the WHERE clause of the enclosing SELECT. This is a boolean value.

SQLServerSchemaCacheFile

The file used to cache the meta-data of the SQL Server instance ro which the source code refers.

SQLServerScrollLocks

FALSE

Use SCROLL_LOCKS option in cursor definitions. This is a boolean value.

SQLServerSessionTableAsLocalTempTable

FALSE

Use a local temporary table to emulate a DB2 session table. This is a boolean value.

SQLServerTypeWarning

FALSE

Use TYPE_WARNING option in cursor definitions. This is a boolean value.

SQLServerUpdLock

FALSE

Use WITH(UPDLOCK) hint in definitions of FOR UPDATE cursors. This is a boolean value.

SQLStopOnUnknownFunctions

FALSE

Report an error upon finding unknown SQL functions. This is a boolean value.

5.2.15. Producing SQLServer stored procedures

Command-line option Default value Description

StoredProcedureName

Produces a stored procedure with name <Name>.

StoredProcedureParamsMode

Sets the modes for the parameters when represented as stored procedures. The modes are set as a comma- or colon-separated list of IN|OUT|INOUT|RETURN|RESULTSET. The number of modes set using this option must match the number of parameters in the COBOL or PL/I program at hand.

5.2.16. Encoding

Command-line option Default value Description

StringRuntimeEncoding

windows-1252

Specifies the encoding used at runtime for character strings. Limited to single-byte encodings.

StringSourceFileEncoding

windows-1252

Specifies the encoding used for the source file. This option supports the same set of encoding as StringRuntimeEncoding with the same restrictions.

5.2.17. Tracing

Command-line option Default value Description

TraceExecution

Sets the granularity of tracing in the generated code. Tracing options can be combined, by separating options with commas, as in: :TraceExecution=qix,sql. Values can be any of the following:

  • all

  • internals

  • procedures

  • arguments

  • programs

  • statements

  • qix

  • sql

TrackStatements

FALSE

Generate tracking statements, allowing to find out in which program and on what line an exception occured. This is a boolean value.

5.3. Command-line options for cobrc.exe

5.3.1. COBOL Options

Table 3. Command-line options for cobrc.exe
Command-line option Default value Description

Align

8

Specifies the alignment for level-01 and level-77 in WORKING-STORAGE and LOCAL-STORAGE. Values can be any of the following:

  • 1

  • 2

  • 4

  • 8

Arith

Extend

Mimics the mainframe’s ARITH command-line option, which enables the extended mode where maximum precision can go from the default of 18 to 31. However Please note that unlike the mainframe COBOL compiler, Raincode will use the extended mode with 31 digits by default. This option can thus be used to force it down to the compatibility mode with 18 digits. Values can be any of the following:

  • Extend: Extended mode where numeric values are limited to a maximum of 31 digits.

  • Compat: Compatibility mode where numeric values are limited to a maximum of 18 digits.

AssignDynamic

FALSE

When ASSIGN TO XXX without quotes is used, if XXX exist as a Data field, the value of the field is used for file name. This is a boolean value.

AssumeImplicitPicture

FALSE

Simulates the behaviour of IBM Enterprise COBOL where data items for which a type cannot be derived are assumed to be typed as PICTURE X(1). A warning will be raised whenever such an assumption is made. This is a boolean value.

CheckExecuteAfterEnd

TRUE

If set to TRUE, a missing STOP RUN or GOBACK in the main program will result in a crash. This is a boolean value.

CobolVersion

3

Specify the version of Cobol used for the compilation. Values can be any of the following:

  • VS: OS/VS COBOL

  • VS1: alias for OS/VS COBOL

  • VS2: VS COBOL II

  • 3: Enterprise COBOL for z/OS Version 3

  • 4: Enterprise COBOL for z/OS Version 4

  • 5: Enterprise COBOL for z/OS Version 5

  • 6: Enterprise COBOL for z/OS Version 6

DeclareBuiltins

FALSE

Add implicit variable and procedure declarations to the parse tree produced from the source, to care for variables such as RETURN-CODE or DFHEIBLK if the source is recognized as a CICS program. This is a boolean value.

DefInit

TRUE

Initialize data items to system defaults. This is a boolean value.

DisableFinishStatement

TRUE

Deactivates the recognition of the FINISH statement as recognized by some COBOL dialects. This is a boolean value.

DisableLvl77StorageOptimization

TRUE

Disables level 77 data fields default storage optimization by using COMP instead of DISPLAY format to improve performance. This will be applied for data fields under following conditions: The Data field is defined in working or local storage. The Data field is not external. The Data field is defined with integral PIC clause (digits < 19). The Data field is defined without USAGE clause. The Data field is not involved in redefine. The Data field’s address is not taken. This is a boolean value.

DisableOdoDynamicAddressing

FALSE

Disables dynamic addressing of items immediately following an ODO (OccursDependingOn) table. All variable-length tables are considered to have the maximum size specified. Default is FALSE. This is a boolean value.

EnableLvl01StorageOptimization

FALSE

Enables level 01 data fields default storage optimization by using COMP instead of DISPLAY format to improve performance. This will be applied for data fields under the same conditions described for :DisableLvl77StorageOptimization flag. This is a boolean value.

INTDATE

ANSI

Sets the initial date of the calendar. Values can be any of the following:

  • ANSI: Initial date is Jan 1st 1601.

  • LILIAN: Initial date is Oct 15th 1582.

IgnoreClassification

FALSE

Deactivates the automatic source file classification mechanism, which detects whether the file is a copybook or not; and whether it should be considered as ANSI COBOL as opposed to free-form COBOL. This classification mechanism is disabled automatically when :FirstUsableColumn or :LastUsableColumn is specified explicitly. This is a boolean value.

Also refer to:

  • FirstUsableColumn

  • LastUsableColumn

IgnoreStandardLabels

TRUE

Ignore LABEL RECORD IS STANDARD. This is a boolean value.

Level88Helper

TRUE

Generate helpers for Cobol level 88. This is a boolean value.

MainframeConstantComparison

FALSE

Mimic the mainframe behavior when comparing zoned decimals with constants. This is a boolean value.

NoTrunc

FALSE

Causes the COMP and COMP-5 data items to be stored directly on their binary representation without truncating them according to the PIC clause of their declaration. This is a boolean value.

Numcheck

FALSE

check the validity of DISPLAY variables. This is a boolean value.

OptimizeVarCaching

FALSE

Use liveness analysis to avoid rereading variables from memory when it can be asserted they have not been modified between two reads. This is a boolean value.

ProtectLinkageAddressability

TRUE

When a LINKAGE section element is referenced, generate code that will trigger an exception if the element is not addressable. This is a boolean value.

QuoteAsApos

FALSE

Figurative constant QUOTE represents an apostrophe ('); otherwise, it represents a double quote ("). This is a boolean value.

RangeCheck

FALSE

Generate subscript checking code. This is a boolean value.

SourceDialect

Default

Controls the source dialect for the COBOL parser. Values can be any of the following:

  • Default: And IBM-inspired, more permissive dialect

  • GCOS: The source programs are/were compiled with BULL’s GCOS compiler which influences the way the sizes of COMP-1 and COMP-2 data fields are computed

  • Microfocus: The source programs are/were compiled with the Microfocus COBOL compiler and a number of syntactical extensions must be recognized accordingly.

  • Unisys: Accept Unisys extensions to the COBOL standard as part ofthe recognized syntax.

  • ISeries: Accept IBM iSeries extensions to the COBOL standard as part of the recognized syntax.

StrictIBM

FALSE

Causes the compiler to be stricter than it would otherwise be, and detect as error cases that would be equally detected as error by IBM' COBOL compilers for the mainframe platforms. This is a boolean value.

TraceParagraphs

FALSE

Generates a call to public virtual execution context method ParagraphEntry at the beginning of every paragraph, and a call to public virtual execution context method ParagraphExit at the end of every paragraph. This is a boolean value.

TrackPrograms

FALSE

Add instrumentation to keep the stack of called programs available when a crash occurs. Used in conjunction with -TrackStatements and Debug, a full stack dump with locations and variable values can be generated. This is a boolean value.

Trunc

Std

Specifies how binary data (COMP, COMP-4, COMP-5) is truncated. Values can be any of the following:

  • Std: Truncates binary data to the number of digits in the picture clause. No effect on COMP-5

  • Bin: All binary data are handled as COMP-5.

5.3.2. Encoding

Command-line option Default value Description

CollatingSequence

Native

An encoding name specifying a collating sequence to be used when no collating sequence is defined in the Cobol configuration section. Default is the runtime encoding collating sequence.

Also refer to:

  • EbcdicCollatingSequence

EbcdicCollatingSequence

IBM01140

Allows specifying the encoding used for the EBCDIC collating sequence when ALPHABET is EBCDIC in the Cobol configuration section.

Also refer to:

  • CollatingSequence

5.3.3. Preprocessing

Command-line option Default value Description

ContinuedStringSupport

TRUE

Support line continuation heuristics, where a line that ends with an open quote will continue on the next line if the next line starts with a string, even in the absence of a continuation hyphen. This is a boolean value.

CopyBookExtension

.cpy

Specifies the extension for copy/include files when none is provided in the source code.

FatalMissingIncludes

FALSE

Indicates that processing must stop if copybooks or include are missing, rather than proceed and try to parse the source code as it is, without expanding the missing files. This is a boolean value.

FirstColumnComments

FALSE

Allows COBOL comments to begin on column 1 even if :FirstUsableColumn is set to a value greater than 1. This is a boolean value.

FirstColumnDirectives

Ignore

Controls how an initial D or d is to be treated, ignored, or left in the source for further processing. Values can be any of the following:

  • Ignore: Do not skip the first character directive, let the parser deal with it and fail if need be.

  • Skip: Skip the first column directive in all cases.

  • SkipDetached: Skip the first column directive if it is followed by a space character.

ReplaceStatementSupport

TRUE

Support REPLACE statements in the COBOL preprocessor. This is a boolean value.

TruncateComments

TRUE

Ensures that comments are not truncated, even if they exceed the automatically detected or manually set :LastUsableColumn. This is a boolean value.

Also refer to:

  • LastUsableColumn

  • Margins

5.3.4. Input

Command-line option Default value Description

ExcludeAcuLines

Support for the AcuCOBOL extension that allows for the inclusion or exclusion of source lines based on code set in columns 73 to 78. For instance, is :IncludeAcuLines='C-WIN' is specified on the command line, lines ending with C-WIN on columns 73 to 78 will be excluded when parsing the source files, while lines ending with !C-WIN will be included.

Also refer to:

  • IncludeAcuLines

IncludeAcuLines

Support for the AcuCOBOL extension that allows for the inclusion or exclusion of source lines based on code set in columns 73 to 78. For instance, is :IncludeAcuLines='C-WIN' is specified on the command line, lines ending with C-WIN on columns 73 to 78 will be included when parsing the source files, while lines ending with !C-WIN will be excluded.

Also refer to:

  • ExcludeAcuLines

5.3.5. Report writing

Command-line option Default value Description

RW

This option controls the behavior of the COBOL report writer, by means of a series of suboptions: RW:CleanUp=<TRUE|FALSE> to indicate whether the generated intermediate source files must be deleted. Default is TRUE. RW:LineLength<Len> to provide the maximum line length to be used in reports. Default is 200. RW:CheckAssertions=<TRUE|FALSE> to generate additional checking code (including array range accesses). This option can be used to troubleshoot a report that does not behave as expected, to ensure that a number of internal conditions are fulfilled and critical situations such as memory overruns are detected before corrupting the internal state of the report writer. Default is FALSE. RW:Trace=<TRUE|FALSE> to generate code for detailed tracing, indicating the paragraph names in the generated code, as they are entered and exited, so that execution can be monitored. Default is FALSE. These options will only be processed if the report writer is activated, and if the program at hand contains at least one report definition.

5.3.6. SQL

Command-line option Default value Description

SQLDecimalPoint

Use comma (,) as decimal separator in float literals. Values can be any of the following:

  • COMMA

  • PERIOD

5.3.7. Parsing

Command-line option Default value Description

UnrecognizedExecSupport

TRUE

Support unrecognized EXEC blocks, in the form of code fragments between matching EXEC and END-EXEC keywords that cannot be parsed accurately. This is a boolean value.

5.4. Command-line options for plirc.exe

5.4.1. PL/I Options

Table 4. Command-line options for plirc.exe
Command-line option Default value Description

BIFPREC

31

Sets the precision for builtin PL/I functions that return fixed binary values. Values can be any of the following:

  • 15

  • 31

Builtins

Controls the behavior of the PL/I builtins as supported by the Raincode PL/I compiler. This option takes a set of comma-separated commands as parameter. The available commands are: ?: display the list of the supported builtins, their status and their class -<builtin>: disable builtin named <builtin> +<builtin>: makes builtin named <builtin> available with the Implied status (so that it is available directy, without further ado) !<builtin>: makes builtin named <builtin> available with the On demand status (so that it can be access after an explicit DECLARE statement). If a builtin is modified multiple times by a sequence of commands, the last occurring command prevails. For instance, a command-line option such as :Builtins=+STG*,? activates the STG builtin with the Implied status, and then lists all the supported builtins (where you can ensure that STG is indeed implied). Note, however, that even though PL/I builtins have aliases (STG for STORAGE, for instance), the commands used with this= command-line option operate on the name, not on the builtin directly. You must disable both STG and STORAGE separately, as in :Builtins=-STG,-STORAGE to disable this builtin entirely. This allows discriminate treatment, to disable STG and not STORAGE, for instance.

DetectDeadProcedures

TRUE

Activates the detection of unused procedures. This is a boolean value.

DetectDeadVariables

TRUE

Activates the detection of unused variables. This is a boolean value.

FIXEDBIN

31

Sets the maximal precision for FIXED BINARY. Values can be any of the following:

  • 31

  • 63

FIXEDDEC

15

Sets the maximal precision for FIXED DECIMAL. Values can be any of the following:

  • 15

  • 31

FixedBinMinSize

2

Sets the minimum size in bytes for fixed binary. For a fixed bin(7), some versions of PL/I will allocate 1 byte while others will allocate 2 bytes. Values can be any of the following:

  • 1

  • 2

JSONCase

ASIS

Sets the case of the JSON generated. Values can be any of the following:

  • LOWER

  • UPPER

  • ASIS

Linkage

OPTLINK

Sets the linkage convention for procedure calls. Note that return values are always passed BYADDR as the last parameter regardless of the value of this option. Values can be any of the following:

  • OPTLINK

  • SYSTEM

WINDOW

1950

Sets the default window used in date handling builtins.

XmlCase

UPPER

Sets the case of the XML generated by the XMLCHAR builtin. Values can be any of the following:

  • ASIS

  • UPPER

5.4.2. Preprocessing

Command-line option Default value Description

DefinePreProSymbol

Defines a preprocessor symbol with an associated value.

Also refer to:

  • System

  • SysParm

KeepPreProFiles

FALSE

Keep the intermediate file(s) produced by the preprocessor. This is a boolean value.

PreProOutputDir

Sets the output directory for the intermediate files produced by the preprocessor. If this option is not set, :OutputDir will be used by default. This option has no effect if :KeepPreProFiles is not set.

Also refer to:

  • OutputDir

  • KeepPreProFiles

PreProcess

TRUE

Enables the preprocessor. If the preprocessor is not enabled, macros are not expanded. This is a boolean value.

SysParm

Sets the value returned by the preprocessor builtin function SYSPARM.

Also refer to:

  • System

System

.NET

Sets the value returned by the preprocessor builtin function SYSTEM.

Also refer to:

  • SysParm

5.4.3. Input

Command-line option Default value Description

LineLengthCompat

FALSE

Pad the input lines with blanks to the right margin (:Margins option). This is necessary if significant blanks were trimmed during mainframe exportation (e.g. in multi-line string constants). (:Margins option) must be specified and right margin ⇐ 256. This is a boolean value.

TabSize

2

Sets the number of spaces to align tab characters on. At load time, all Tab are replaced by spaces. If set to 0, no tab expansion take place.

5.5. Command-line options for hlasmrc.exe

5.5.1. Miscellaneous

Table 5. Command-line options for hlasmrc.exe
Command-line option Default value Description

ContinueOnErrors

FALSE

Force ignoring of errors. This is a boolean value.

InstructionFile

Path to an instruction definition xml file.

InternalIncludePath

Path to raincode internal macros.

ProfileFile

File to COPY before any instruction.

SectionAlignment

8

Default alignment of sections.

SourceToRuntimeEncoding

Path to an encoding file used to convert character constants to the original encoding.

StoreWarningsImmediately

FALSE

Store messages in the repository as soon as generated. They may have a wrong RC_ID but will be present if the compiler crashes. This is a boolean value.

StoredWarningLevel

0

Minimum level of messages to store in the repository.

Sysasm

Value of the &SYSASM global symbol.

Sysparm

Value of the &SYSPARM global symbol.

TestEnvironment

FALSE

Don’t output full paths in error messages. This is a boolean value.

5.5.2. Code generation

Command-line option Default value Description

EmulatorTracing

FALSE

Generate traceable emulator calls. This is a boolean value.

Profiling

FALSE

Generate profiling data at runtime. This is a boolean value.

QIXEpilog

TRUE

Generate epilog macro calls. This is a boolean value.

QIXProlog

TRUE

Generate prolog macro calls. This is a boolean value.

SafeInlining

TRUE

Detect self-modifying code at runtime. This is a boolean value.

5.6. Connecting to the database

You can specify the connection string used during the compilation to connect to the SQL Server database to obtain the schema of the target database.

Connecting with a cache file - when both cache file and connection string are provided, the cache file is first checked; if not available the database connection is used instead, and a cache file is created with the database schema information.

Successfully re-hosting mainframe legacy source code on the .NET platform requires that you must ensure that your source code will transfer from the DB2 Server and run as is on the .NET SQL Server with no change to your source code. To learn about the differences between the IBM DB2 and Microsoft SQL Server database platforms, and the steps necessary to convert a DB2 database to SQL Server read the 'Guide to Migrating from DB2 to SQL Server' [8].

Raincode Legacy Compilers allows you to compile your source code on the .NET environment through any one of three translation scenarios (see figure Raincode Legacy Compilers - Mainframe SQL to .NET TSQL translation scenario):

image043
Figure 109. Raincode Legacy Compilers - Mainframe SQL to .NET TSQL translation scenario

Translation is performed at compile time. This transformation induces no performance penalty whatsoever because Raincode Legacy Compilers behaves exactly as if the transformed SQL code was found in the code submitted to Raincode Legacy Compilers by the user.

The following SQL options are required for both simple and schematic translations:

  • The :SQLSourceDialect option specifies the SQL dialect used in the source code

  • The :SQL option specifies the SQL dialect used by the actual DBMS.

For schematic transformation you should add the :SQLServerSchemaCacheFile option to specify the XML file describing the database schema.

Information is available in this document on how to execute a program which connects to either DB2 using IBM DB2 Connect, DB2 using DB2HIS or SQL Server. You may also read about Raincode Legacy Compilers SQL support in this document (See section SQL and CICS support).

5.7. Merging DLLs

The Raincode Legacy Compilers generate one DLL per compiled program. If deploying several DLLs is not desirable, it is possible to merge them into a single DLL. This is done using a tool from Microsoft called ILMerge. This tool is available at these locations:

ILMerge requires at least 3 parameters:

  • the name of the output DLL;

  • the directory where the DLLs referenced by the compiled programs can be found

  • the list of DLLs to merge.

For example, assuming we want to merge 3 DLLs (prog1.dll, prog2.dll, prog3.dll) into a single DLL merged.dll:

ILMerge.exe /out:merged.dll /lib:"$env:RCDIR/bin" prog1.dll prog2.dll prog3.dll

To run the resulting assembly, the command-line option AutoMapAssembly of rclrun is used to load the different symbols. To continue with the same example, assuming prog1 is the entry point:

rclrun -AutoMapAssembly=merged.dll prog1

5.8. Meta Comments

Meta-comments are comments of a recognizable form, which are used by Raincode Legacy Compilers to implement extensions. They are coded as comments so that they are ignored if encountered by a mainframe legacy compiler. Meta-comments are used in a number of areas, including the ability to annotate SQL host variables (See section Using meta-comments to annotate SQL constructs).

Meta-comments must conform to a specific form:

  • They must fit on a single source line

  • They must start with /\*RC: in PL/I, or *RC: in COBOL. This prefix is case independent

  • In PL/I, they must end with */. Additional asterisks can be used, and will thus not be part of the meta-comment

  • The meta-comment’s content is whatever exists after the prefix and before the end of the comment (*/, in PL/I, end of line in COBOL). Trailing asterisks are not part of the meta-comment’s content

  • The meta-comment’s content cannot contain asterisks other than the asterisks used to start and end the delimiting comment.

Beware, however, as the Raincode Legacy Compilers do not warn about malformed meta-comments. It will ignore them, as they are then recognized simply as comments. Valid and invalid PL/I meta-comments are shown in the Tables below
Table 6. Valid PL/I meta-comments
Comments in PL/I Description
/*RC:Hello*/

Contains "Hello".

/*******rc:890981******/

Contains "890981".

Table 7. Invalid PL/I meta-comments
Comments in PL/I Description
/*RC :Hello world*/

Contains a white space " ".

/*RC:Dir *.txt*****/

Contains an asterisk *.

/*RC:RSTC 1 3 2 4 1 2 3
        17 19 */

Spans multiple lines.

Table 8. Valid COBOL meta-comments
Comment in COBOL Description
**RC:Hello

Contains "Hello"

**RC:890981**

Contains "890981"

Table 9. Invalid COBOL meta-comments
Comment in COBOL Description
**RC: Hello world

Contains a white space " "

**RC:Dir *.txt

Contains "*"

5.9. PL/I Builtins

PL/I builtins are predefined functions that provide a variety of services, string manipulations, type conversions, math functions and more. Builtins can be declared explicitly before use, as in,

DCL BOOL BUILTIN;

but most compilers are tolerant in this area, and allow you to use these builtins without explicitly declaring them. Consistently with PL/I’s general philosophy, you can also declare variables, labels or procedures with builtin names, using scoping rules to disambiguate. The set of builtins available to the programmer also depends on the version of Raincode Legacy Compilers.

To see a complete list of the available Raincode Legacy Compilers builtins for PL/I, simply use the plirc:Builtins=? command-line option. This option also gives the builtin class and the builtin status which are described below.

Raincode Legacy Compilers builtin support is configurable to a large degree, so that you can tailor the compilers to your specific environment, and define the set of builtins you want to access.

Raincode Legacy Compilers makes a distinction between three classes of builtins as described below:

  • Wired builtins are dealt with by Raincode Legacy Compilers intimately, with specific, not function-like behaviors, e.g.: SUBSTR.

  • Internal builtins are recognized by Raincode Legacy Compilers, but behave just like functions:

    • They take parameters, perform some action and possibly return a value.

    • They can be versatile, in the sense that they can support variable number of parameters (MIN, MAX) and/or have a different behavior and return type depending on the type of the arguments (ABS).

Only Internal builtins applies to COBOL
  • The builtins also have a status which can be:

    • Inactive – Can’t be used at all.

    • On demand – Can only be used if an explicit matching DCL BUILTIN declaration is found in the source code.

    • Implied – Can be used, whether there is an explicit matching DCL BUILTIN declaration or not.

Wired builtins can only be Inactive or Implied; the On demand status is not applicable to them.

5.10. TraceParagraphs

When compiling compiler with command-line option :TraceParagraphs, a call to ParagraphEntry is generated at the entry of every paragraph and a call to ParagraphExit is generated at the exit of every paragraph, with the paragraph name as string argument. For more details, see section COBOL options.

Tracing paragraph entry and exit is achieved by overriding these two methods, defined in the execution context

        public virtual void ParagraphEntry(string s)
        {

        }
        public virtual void ParagraphExit(string s)
        {

        }

5.11. Return codes

The Raincode legacy compilers detect semantics and syntax errors during compilation and return the value of the return codes.

For return code values between 10 and 38, the higher the value, the earlier the problem.

When multiple files are compiled, the compiler will use the highest return code found for each file. If using the repository, each file will still be reported in it with its relevant return code.

For more details, refer to the description of the return codes.

5.12. Available Source/Target SQL Dialect

Raincode provides some supported combinations of SQLSourceDialect and SQL with their values. For more details on the options, refer to the options of Sql. Sometimes, not all combinations are valid. For available options, refer to the table mentioned below:

Table 10. Available Source/Target SQL Dialect

SQLSourceDialect

SQL

DB2

DB2IBM

DB2

DB2HIS

DB2

SQLServer

SQLServer

SQLServer

ORACLE

ORACLE

6. Running compiled programs

6.1. EXE vs DLL

The Raincode Legacy Compilers default behavior is to generate a Dynamic Link Library (DLL) file. This behavior can be overridden explicitly by using the :EXE command-line option to produce an executable file. EXE files can be run directly, while DLLs can be called from other legacy programs or started using the rclrun utility (See section rclrun command-line options).

6.2. rclrun command-line options

This section describes how to use the command-line options available in the rclrun - see the sections below for details on each available option.

The command-line options are retrieved as follows:

  • From the rclrun configuration file (rclrun.exe.config), section RainCodeSection, if it exists

  • From the command line

  • From the content of the environment variable RCLRUNARGS

  • From the file rclrun.args, if found in the current working directory

All steps are evaluated, and final command-line options are the concatenation of the contents of each of them.

The command-line options are case independent.

A single command-line option may have one of the following forms:

  • -Name=Value or :Name=Value, where value may be optional

  • An unnamed argument

  • @Name where Name is the name of a response file in the current working directory

  • The files (rclrun.args and response files) contain one command-line option per line. Empty lines or lines starting with ; or # are ignored.

6.2.1. Assembly

Command-line option Default value Description

AddAssemblySearchDir

Add a directory (or a list of directories separated by ';') where to look for assemblies (can be repeated to specify multiple directories).

AddAssemblyTopSearchDir

Insert a directory (or a list of directories separated by ';') on top of places where to look for assemblies (can be repeated to specify multiple directories).

AutoMapAssembliesDirectory

Behaves as if AutoMapAssembly had been set for all assemblies in the directory (can be repeated to specify multiple directories).

AutoMapAssembly

Scans an assembly for the exported symbols (can be repeated to specify multiple assemblies). The assembly is the name of the assembly without it’s extension.

AutoMapModules

Scans an assembly for the exported symbols and prefetches the corresponding modules (can be repeated to specify multiple assemblies). The assembly is the name of the assembly without it’s extension.

AutoMapModulesDirectory

Behaves as if AutoMapModules had been set for all assemblies in the directory (can be repeated to specify multiple directories).

MapSymbolToAssembly

Tells the runtime in which assembly to look for a program (can be repeated to specify multiple mappings). Format: <symbol>,<assembly>. <assembly> is the name of the assembly without it’s extension By default, the runtime looks for an assembly with the same name as the program.

6.2.2. Database

Command-line option Default value Description

CollectionId

Specify the DB2 COLLECTION ID.

DB2

Connect to DB2 using IBM DB2 Connect with connection string.

DB2HIS

Connect to DB2 using HIS with the given connection string.

DB2UseResultSetCursor

False

Use DB2ResultSet for cursors. This is a boolean value.

DoNotCommitAtEnd

False

Do not commit before closing connection to database. This is a boolean value.

ForceBulkInsertUpperCaseTableName

False

Force table name to upper case when using bulk insert. This is a boolean value.

ORACLE

Connect to Oracle with the given connection string.

PartialConnectionString

Partial connection string to be added to the connection string created by the runtime when dynamically connecting to a database.

SQL

The DBMS to use. Use this to lazily connect to the database. Note: Postgres is reserved for future use. Values can be any of the following:

  • SqlServer

  • DB2

  • DB2HIS

  • Postgres

  • Oracle

SQLCommandTimeout

The time in seconds to wait for the command to execute.

SQLExecutionMode

Values can be any of the following:

  • StaticOnly

  • DynamicOnly

  • StaticOrDynamic

SQLMaxCollationChar

Replaces HIGH-VALUES with the given hexadecimal value in the SQL statement string parameters.

SQLPlatform

Values can be any of the following:

  • ZOS

  • LUW

SqlServer

Connect to SQL Server with the given connection string.

SQLServerCleanupProcedure

A SQL statement to execute before closing the connection to the database.

SQLServerDefaultSchemaRuntime

The schema to be used at runtime for the default schema.

SQLServerDefaultSchemaToken

The token used at compile time for the default schema.

SQLTransactionIsolationLevel

Values can be any of the following:

  • READ_UNCOMMITTED

  • READ_COMMITTED

  • REPEATABLE_READ

  • SNAPSHOT

  • SERIALIZABLE

UseAnsiTypes

False

Use ANSI types for ANSI descriptors. This is a boolean value.

UseBulkInsert

False

Attempt inserting several rows at once; in case of insertion error, the number of actually inserted rows may differ from the number of rows inserted without using this option. This is a boolean value.

6.2.3. Dependency extraction

Command-line option Default value Description

ExtractDependencies

False

Extract the transitive call dependencies rather than execute the program. This is a boolean value.

6.2.4. filesCat

Command-line option Default value Description

CatalogConfiguration

Path to the configuration file for the batch catalog.

6.2.5. Files

Command-line option Default value Description

ExtfhDll

Path to the EXTFH dll used; Default is Raincode EXTFH implementation rcextfh_dll.dll.

ExtfhFuncName

EXTFH

Entry point name of the EXTFH function in the dll.

FileConfig

Path to the XML configuration file describing files.

FileConfigEncoding

utf-8

The encoding of the file specified in the -FileConfig option.

SearchFilePathUsingEnvironment

False

Whether to look for DD_<filename> environment variable to map a logical name on a file path. This is a boolean value.

6.2.6. IMSql

Command-line option Default value Description

IMSqlCacheSize

10

The size of the Cache (in number of segments) used when querying the database.

IMSqlDBConnectionString

IMSql DB connection string to SQL Server.

IMSqlIOHandlerType

None

The type of handler to use for IO messages. Values can be any of the following:

  • NONE

  • PROC

  • QUEUE

IMSqlTMConnectionString

IMSql TM connection string to SQL Server.

PSB

Name of the PSB to allocate before execution.

RegionName

Region’s name.

RegionType

Region type used by this runtime. Valid value are BMP, MPP, DBB.

6.2.7. Miscellaneous

Command-line option Default value Description

BigEndianPointers

True

Tells the runtime if pointers should be stored as big-endian values or not. This is a boolean value.

CacheCobolInitialize

False

Enable caching the result of COBOL INITIALIZE. This is a boolean value.

Comment

User defined comment passed as argument.

DateTimeOffset

The datetime offset value used to initialize the runtime’s time.

Debug

False

Start a debugger. This is a boolean value.

DotNetConfigFile

An additional app.config file.

DumpLicense

Check and dump current License. This is a boolean value.

Help

Display the help message. This is a boolean value.

InitialDateTime

The Initial datetime value used to initialize the runtime’s time.

InitStatics

False

Initializes static areas of all prefetched modules at startup time, instead of lazily. This is a boolean value.

LogLevel

WARNING

Values can be any of the following:

  • SILENT

  • ERROR

  • WARNING

  • INFO

  • DEBUG

  • TRACE

  • PROGRAM_OUTPUT

  • DIAGNOSTIC

NonStandardEncodingsAssembly

Name of the plugin assembly containing non-standard encoding implementations.

Profile

False

Start a JetBrain Profiling. This is a boolean value.

ProgramArguments

Arguments passed to the program.

RangeCheck

False

Enable range checking in COBOL. The code must be compiled with the -RangeCheck option. This is a boolean value.

SafeMemoryMode

False

Enables safe mode in the memory allocator. Works in conjunction with the :VerifyMemory flag on the compilers. Must be the first parameter of rclrun. This is a boolean value.

StringRuntimeEncoding

Sets the encoding to use for character strings. Must be one of .NET’s builtin encodings and an SBCS encoding; in the absence of the compiler option StringRuntimeEncoding and of this option, windows-1252 is used.

Version

Display version information. This is a boolean value.

6.2.8. Plugin

Command-line option Default value Description

Plugin

Loads plugin-providing assembly.

PluginPath

Path where plugins (and other assemblies) are searched.

6.2.9. QIX

Command-line option Default value Description

Qix

Run in QIX mode. This is a boolean value.

QixApplId

Set the QIX APPLID.

QixChannel

Sets the CHANNEL name to use for this transaction.

QixCommarea

String value to use as the commarea.

QixConnectionString

Set the SQL Server connection string to use for QIX counters.

QixContainerFromFile

Add a channel and container with a comma separated triplet "channel,container,path", to respectively specify the channel name, container name, and binary file with the content of channel/container.

QixDatForm

Sets QIX DATFORM Date format (default is YYMMDD).

QixDumpCommareaOnExit

Dumps the content of the commarea on the console upon exit. This is a boolean value.

QixDumpContainerOnExit

Dumps the content of a channel/container on the console upon exit.

QixFacility

Set the QIX FACILITY.

QixFile

Path to a binary file to be loaded and used as the commarea.

QixNetName

Set the QIX NETNAME.

QixStartCode

Set the QIX STARTCODE.

QixSysId

Set the QIX SYSID.

QixTsQueuesFromFile

Create a TS queue for each file specified. Use a comma separated list for each file, and end with a record length. Example: Q_IN,Q_OUT,80.

QixUserId

Set the QIX USERID.

6.2.10. Schema cache generation

Command-line option Default value Description

SQLServerSchemaCacheFile

The name of the file used to cache the meta-data of the SQL Server instance to which the connection string refers.

6.2.11. Sort

Command-line option Default value Description

SortAssembly

Path to the Raincode Sort dll.

SortConfig

Control statements to give the sort routine. Should either be a DFSORT style OPTION statement or an RCSET command.

6.3. Dynamic linking

6.3.1. Entries

Entries are ancillary callable entities defined within the legacy program procedures (the example below shows PL/I):

REPORT_ERROR : PROCEDURE(MSG);
  DCL MSG CHAR(*); ...
  REPORT_WARNING : ENTRY(MSG);
  REPORT_FATAL_ERROR : ENTRY(MSG);
...

Every procedure can have an unlimited number of additional entries defined. Entries can be called as a procedure as soon as their enclosing program has been loaded. In other words, in the example above, as soon as REPORT_ERROR has been called at least once, REPORT_WARNING can be called immediately.

6.3.2. The FETCH statement

Alternatively, the standard PL/I FETCH statement can be used to load a procedure (or more specifically, the corresponding DLL file) without actually executing it so that all of its entries are made available and can be called. For instance,

CALL REPORT_ERROR("Invalid data");

loads the REPORT_ERROR.DLL module if it is not present in the module table and executes the REPORT_ERROR procedure. On the other hand, a statement such as FETCH REPORT_ERROR; only loads the REPORT_ERROR.DLL module without executing anything. In both cases, the REPORT_WARNING entry cannot be called directly unless the REPORT_ERROR.DLL module has been successfully loaded and initialized.

6.4. Execute a program which connects to Database

6.4.1. Execute a program which connects to DB2 using DB2HIS

Executing a program which connects to DB2 using DB2HIS is only available when the generated dll has been compiled with the :SQL=DB2HIS option. To execute it and connect to a DB2 database, the following arguments [9] should be used. (See section Bind generated.db2.xml files with DB2 database)

rclrun :db2his="Provider=DB2OLEDB;User Id=user;Password=passwd;Initial catalog=MyDB;Network Transport Library=TCP;Host CCSID=id;PC Code Page=1252;Network Address=x.x.x.x;Network Port=8010;Package Collection=collid; Default Schema=schema; Process Binary as Character=FALSE;" program.dll

If static SQL is used, the package files (e.g. the .db2.xml files, generated during compilation) should be bound to the database.

6.4.2. Execute a program which connects to SQL Server

Executing a program which connects to SQL Server is only available when the generated dll has been compiled with the :SQL=SQLServer option. To execute it and connect to a SQL Server database, the following arguments should be used.

rclrun :sqlserver="Data Source=serverName; Initial Catalog=MyDB; Uid=login; Pwd=password" program.dll

If static SQL is used, the script files (e.g. the.sp.sql files, generated during compilation) should be applied to the database, as presented in this document (See section Bind generated .sp.sql files with SQL Server database).

6.4.3. Execute a program which connects to DB2 using IBM DB2 Connect

Executing a program which connects to DB2 using IBM DB2 Connect is only available when the generated dll has been compiled with the SQL:DB2IBM option. To execute it and connects to a DB2 database, the following arguments should be used.

rclrun :DB2=" Server=serverName;Database=MyDB;UID=user;PWD=password " program.dll

If static SQL is used, the script files (e.g. the .sp.sql files, generated during compilation) should be applied to the database, as presented in this document (See Section Bind generated .sp.sql files with DB2 using IBM DB2 Connect).

Ensure that the database server and the client have the same date format.

6.5. I/O Configuration

In addition to the information found in the source code, the RainCodeLegacyRuntime environment allows for the external definition of a number of parameters that control the behavior of files.[10]

These parameters can be set on a file by file basis, one for each logical file used in the legacy program, to associate the driver used to access the file, its physical location, and other parameters associated with the specific driver used to access the file.

The available file drivers supporting different formats are:

  • DotNetFHRecordBaseFile driver

    • Handles all file organization types (indexed, relative, sequential, line-sequential)

    • Represents ExtFH driver implementation in C#, making plain ExtFH driver obsolete

    • Used as DEFAULT driver in RainCodeLegacyRuntime environment for file manipulation

  • EXTFHRecordBasedFile driver

    • Legacy file driver handling all file organization types with implementation in C

  • PdsFolder driver

    • Handles PDS folders (currently limited support)

  • BlockedFile driver

    • Legacy file driver handling Fixed-Block and Variable-Block files

  • LSEQFile driver

    • Legacy file driver handling line-sequential files

  • FujitsuVariableRecordLengthFile driver

    • Legacy file driver handling variable record based files in Fujitsu format

  • SimpleRecordBaseFile driver

    • Legacy file driver with a custom implementation of a driver which accepts all file organization types

  • VsamSql driver

    • Uses SQLServer tables to store data rather than plain files on the file system.

  • VsamLite driver

    • Uses SQLite tables to store data rather than plain files on the file system.

  • VsamOracle driver

    • Uses Oracle tables to store data rather than plain files on the file system.

See Examples for more information about configuring the different file driver types.

Other than configuring and manipulating files from the legacy programs and the scripts from the Raincode Runtime Environment version 4.0, it is possible to perform the same actions from C#. Version 4.0 includes IO libraries, independent from the RainCodeLegacyRuntime, which serves as utility class libraries for the IO configuration and file manipulation:

6.5.1. RainCodeLegacyFileDriver library

Namespace: RainCodeLegacyFileDriver

RainCodeLegacyFileDriver is a C# class library providing modules for the I/O configuration and file manipulation from C# programs:

6.5.2. File parameters configuration module

Namespace: RainCodeLegacyFileDriver.Config.ConfigV2

Before creating a file, the system needs information about the associated driver and file’s attributes to perform this task. For this reason, file parameters can be configured by using either of the following approaches:

  • By the XML configuration file

  • Programmatically using a public API provided as part of RainCodeLegacyFileDriver library. For more information, check out Creating file configurations.

The parameters described in the following section hold a strong relationship to the ExtFH standard. This allows the usage of third party file drivers. There are, however, several parameters that are used internally to perform the connection between both the legacy program and the specific driver. Some parameters do not have use for certain drivers and in these cases, they are ignored: for example, configuring parameters about record keys for non-indexed files.

For more information about the file parameter types used in the configuration classes, go to the RainCodeLegacyRuntimeUtils library
File parameters configuration classes

There are several classes defined for configuring the file parameters (hierarchically: top-bottom):

  • FileConfigCollection

  • FileConfig

  • KeyConfig

  • ComponentConfig

When we talk about the file configuration, we always refer to a FileConfigCollection class. FileConfigCollection represents top level file configuration which can include several FileConfig objects for each instance of a file. Individual instances of a FileConfig are kept in a configuration list and are used to configure the parameters of each file instance separately. This is particularly useful when the same physical file is used in different modes from the user program.

The table FileConfigCollection attributes describe attributes that can be declared in a FileConfigCollection.

Table 11. FileConfigCollection attributes

Attribute name

Type

Description

FileConfigs

List<FileConfig>

The list of FileConfig objects for each instance of a file

FileConfig class defines all the attributes required for configuring a file.

The table FileConfig attributes describe attributes that can be declared in a FileConfig.

Table 12. FileConfig attributes

Attribute name

Type

Description

LogicalName

string

The logical name of the file in the legacy program

Path

string

The path to the file on the file system

SameAs

string

A reference to the logical name of the FileConfig which parameters need to be copied

DriverAssembly

string

The full file driver assembly name [Assembly]Namespace.Class or file driver name alias (check File manager module for more info)

RecordSize

int?

They are used for configuring the record size of the file. If size > 0 minimum, maximum and current record size will be set to the desired value. 0 value is used when the size should not be configured or is not known at file creation time

MinimumRecordSize

int?

The minimum size of the record in the file. Non-null value makes sense only in the Variable recording mode, otherwise is ignored

RecordingMode

RecordingModeType

The recording mode of the file

FileOrganization

FileOrganizationType

The file organization of the file

AccessMode

AccessModeType

The access mode of the file

LockMode

LockModeType

The lock mode of the file

Disposition

DispositionType

The disposition of the file. Mod disposition type implicitly sets OpenMode to Extend and OptionalFile to true

Keys

List<KeyConfig>

The list of keys configurations

LineEndCRLF

bool?

The indication of CRLF at the end of the line

MainFrameFBA

bool?

The indication of Mainframe FBA format

DefaultKey

int?

The default key value ranges from 1 to MAX USHORT. For DefaultKey != 1, a program that does not declare key in the SELECT clause, OPEN, READ, will use the designated key as a base index

LineAdvancingFile

bool?

The indication of line advancing file. If true, an EOR (LF in Unix and CR/LF in .NET) is added after each written record

MultipleReelFile

bool?

The indication of multiple reel file

NoDetectLock

bool?

The indication of no detect lock mode

FileNameIsExternal

bool?

The indication that filename is defined externally using environment variables, otherwise Path specifies the file location

IOCreateIfMissing

bool?

The indication that the non-existent file will be created on opening in Extend or InputOutput modes, otherwise OPEN operations fails

OptionalFile

bool?

The indication of the optional file flag. Opening the optional file in INPUT mode gives 05 file status when the file does not exist; otherwise, the OPEN operation fails

DriverParameterElems

string

The XML string of parameters that are specific to a particular driver implementation (uncommon parameters)

PrintStream

bool?

The indication that the file will be used in the print stream mode. If not explicitly defined through PliTabs. DEFAULT values for the line size, page size, and tab size are respectively: 120, 60, and 24

Dummy

bool?

The indication that the file is a DUMMY file

PliTabs

PliTabs

Configuration of the line, tab and page sizes used for the PRINT files in the PL/I programs

ConcatenationMembers

List<FileConfig>

NOT SUPPORTED IN V4

EnforcedFileOrganization

Same syntax as <FileOrganization>

This parameter Enforce the file organization whatever the COBOL program specifies.
This means a COBOL program may define a file as SEQUENTIAL.
If the FileConfig include <EnforcedFileOrganization> LineSequential </EnforcedFileOrganization>, the file will be read/write as a LineSequential.

EnforcedRecordingMode

Same syntax as <RecordingMode>

This means a COBOL program may define a file as RECORDING MODE IS F. If the FileConfig include <EnforcedRecordingMode> Variable </EnforcedRecordingMode>, the file will be read/write as a Variable size.

Null values of nullable types mean "do not configure."

PliTabs class defines attributes required for overriding the tab control table for the PL/I programs.

The table PliTabs attributes describe attributes that can be declared within the PliTabs.

For more information about the usage, see Examples.

Table 13. PliTabs attributes

Attribute name

Type

Description

LineSize

int?

The line size in a print file

PageSize

int?

The page size in a print file

TabSize

int?

The tab position in a print file

Null values of nullable types mean "do not configure."

KeyConfig class defines all the attributes required for configuring a file key.

The table KeyConfig attributes describe attributes that can be declared in a KeyConfig.

Table 14. KeyConfig attributes
Attribute name Type Description

AllowDuplicate

bool?

The indication that several different records can have the same key

PrimaryKey

bool?

The indication that the key is the primary used

SparseKey

bool?

The indication that the key is sparse

SparseCharacter

string

The sparse character

CompressTrailingSpaces

bool?

The indication of compression of the trailing spaces

CompressLeadingCharacters

bool?

The indication of compression of the leading characters

CompressDuplicates

bool?

The indication of compression of the duplicates

Components

List<ComponentConfig>

The list of key components configuration

Null values of nullable types mean "do not configure."

ComponentConfig class defines all the attributes required for configuring a key component.

The table ComponentConfig attributes describe attributes that can be declared in a ComponentConfig.

Table 15. ComponentConfig attributes
Attribute name Type Description

Offset

int

The component offset

Length

int

The component length

Default FileConfig - The table Default FileConfig attribute values describes default values used for configuring file parameters if they are not explicitly specified:

Table 16. Default FileConfig attribute values
Attribute name Value

DriverAssembly

[RainCodeLegacyFileDriver]RainCodeLegacyFileDriver.Driver.DotNetFHRecordBaseFile

RecordingMode

RecordingModeType.Fixed

FileOrganization

FileOrganizationType.Sequential

AccessMode

AccessModeType.Sequential

LockMode

LockModeType.Manual

LineEndCRLF

true

MainFrameFBA

true

LineAdvancingFile

false

MultipleReelFile

false

NoDetectLock

false

FileNameIsExternal

false

IOCreateIfMissing

false

OptionalFile

false

Dummy

false

Creating file configurations

There are two ways to create file configurations:

  1. File configuration can be specified in the XML configuration file and passed to FileManager utility, which can deserialize, register, and use registered configurations to create file drivers.

    Simple example of XML configuration file:

    <FileConfigCollection>
        <FileConfig LogicalName="FILE1">
            <DriverAssembly>DotNetFHFile</DriverAssembly>
            <Path>"C:\Temp\file1"</Path>
        </FileConfig>
        <FileConfig LogicalName="FILE2">
            <DriverAssembly>EXTFHFile</DriverAssembly>
            <Path>"C:\Temp\file2"</Path>
        </FileConfig>
    </FileConfigCollection>

    The above example configures two files, referenced in the legacy programs by the logical names FILE1 and FILE2. It associates them with a driver used to access them (FILE1 with DotNetFH driver, and FILE2 with ExtFH driver) and a location.

    The below example is a bit more complex showing how the DotNetFHFile file driver can be configured. Apart from configuring the standard file parameters it also configures a key since it is defined as an Indexed file organization file:

    <FileConfigCollection>
      <FileConfig LogicalName="AsciFile">
        <Path>AsciiIndexed</Path>
        <DriverAssembly>DotNetFHFile</DriverAssembly>
        <RecordSize>67</RecordSize>
        <MinimumRecordSize>50</MinimumRecordSize>
        <RecordingMode>Variable</RecordingMode>
        <FileOrganization>Indexed</FileOrganization>
        <AccessMode>Sequential</AccessMode>
        <LockMode>Manual</LockMode>
        <Keys>
          <KeyConfig>
            <AllowDuplicate>false</AllowDuplicate>
            <PrimaryKey>true</PrimaryKey>
            <SparseKey>false</SparseKey>
            <SparseCharacter> </SparseCharacter>
            <CompressTrailingSpaces>false</CompressTrailingSpaces>
            <CompressLeadingCharacters>false</CompressLeadingCharacters>
            <CompressDuplicates>false</CompressDuplicates>
            <Components>
              <ComponentConfig>
                <Offset>0</Offset>
                <Length>18</Length>
              </ComponentConfig>
            </Components>
          </KeyConfig>
        </Keys>
        <LineEndCRLF>true</LineEndCRLF>
        <MainFrameFBA>true</MainFrameFBA>
        <DefaultKey>3</DefaultKey>
        <LineAdvancingFile>true</LineAdvancingFile>
        <MultipleReelFile>false</MultipleReelFile>
        <NoDetectLock>true</NoDetectLock>
        <FileNameIsExternal>true</FileNameIsExternal>
        <IOCreateIfMissing>false</IOCreateIfMissing>
        <OptionalFile>true</OptionalFile>
        <PrintStream>false</PrintStream>
        <Dummy>false</Dummy>
        <DriverParameters><CheckCatalogConsistency>true</CheckCatalogConsistency></DriverParameters>
      </FileConfig>
    </FileConfigCollection>
  2. Programmatically (from C# program) by invoking public constructor and setting desired attributes manually

File configuration is always created prior to a file driver (and a file creation) because it is always used to configure a file (with default or user-specified attributes).

6.5.3. File manager module

Namespace: RainCodeLegacyFileDriver.Manager

FileManager class provides user-friendly methods which simplify file configuration and file driver creation. The following methods are exposed in the form of FileManager public API:

  • Methods for loading and registering file configurations from XML string or XML file:

    public void LoadAndRegisterConfigurationFromData(string fileConfigurationStr)
    public void LoadAndRegisterConfigurationFromFile(string fileConfigurationPath)

    Both methods LoadAndRegisterConfigurationFromData and LoadAndRegisterConfigurationFromFile deserialize file configuration from XML format, and register (store) provided configurations by their logical name locally.

    public void RegisterFileConfigCollection(FileConfigCollection configCollection);

    The last method has the same functionality, except it accepts file configuration created programmatically.

    Registered configurations can be used later on for creating associated file drivers.

  • Methods for creating file drivers:

    public RecordBasedFile CreateFile(string logicalName, bool forceCreate = false)

    Tries to create a file driver by its logical name.

    If forceCreate == false, it will try to find a registered file configuration that matches logicalName to create an associated file driver. If the match is found, the file driver object is created, the file driver is configured by matching file configuration, and reference returned as return value. Otherwise, it returns null.

    If forceCreate == true, and no matching file configuration is found, a default file configuration (with Default FileConfig attribute values) will be used for creating and configuring the file driver.

    public RecordBasedFile CreateFileFromConfig(FileConfig config);

    Creates a file driver and configures it based on the passed file configuration config.

    Here we do not use FileConfigCollection as a top level configuration but FileConfig which is a convenient way for manually configuring specific file instances.
  • Utility method for specifying usage of external names for the files:

    public void SetSearchFilePathInEnvironment(bool value)

    Sets all registered file configurations FileNameIsExternal flag to a value that controls how the file Path is determined.

    • For value == true, file names are resolved at run time using environment variables: For example, given Path="DATAFILE", the file names checked during file opening are:

      • The value of environment variable:

        • DD_DATAFILE or

        • dd_DATAFILE or

        • DATAFILE or

      • The value of environment literal:

        • DATAFILE

    • For value == false, the value of the Path field is the file name.

FileManager also provides a list of file driver aliases, which can be used for specifying the file driver types in the file configuration:

public const string DOT_NET_FH_FILE_ALIAS = "DotNetFHFile";
public const string EXTFH_FILE_ALIAS = "EXTFHFile";
public const string PDS_FOLDER = "PDSFolder";
public const string LEGACY_FUJITSU_FILE_ALIAS = "Legacy.FujitsuFile";
public const string LEGACY_LSEQ_FILE_ALIAS = "Legacy.LSEQFile";
public const string LEGACY_BLOCKED_FILE_ALIAS = "Legacy.BlockedFile";
public const string LEGACY_SRB_FILE_ALIAS = "Legacy.SRBFile";
public const string VSAM_SQL_ALIAS = "VsamSql";
public const string VSAM_LITE_ALIAS = "VsamLite";
public const string VSAM_ORACLE_ALIAS = "VsamOracle";
Internally all aliases are mapped to full assembly name. For example: The "DotNetFHFile" is mapped to "[RainCodeLegacyFileDriver]RainCodeLegacyFileDriver.Driver.DotNetFHRecordBaseFile", etc.

These constants can be used for the DriverAssembly field in the FileConfig to specify which file driver needs to be created associated with that file. Otherwise, this field needs to include the entire file driver assembly name. If the DriverAssembly is left out, the DotNetFHFile will be created as the default file driver.

See Examples for more information.

File Driver Assembly

When not specified by the <DriverAssembly> element of the <FileConfig> XML type, the runtime uses a driver assembly by default:

[RainCodeLegacyFileDriver]RainCodeLegacyFileDriver.Driver.DotNetFHRecordBaseFile

The user can define his own class (assembly) by defining the environment variable as:

RC_DEFAULT_FILEDRIVER= [Assembly]Class.name

6.5.4. Record based file driver module

Namespace: RainCodeLegacyFileDriver.Driver

RecordBasedFile class represents a base for the file drivers and provides public: File attributes API and File operations API.

The class defines the following file attributes:

Table 17. File attributes

Attribute name

Type

Description

LogicalName

string

The logical name of the file in the legacy program

Path

string

The path to the file on the file system

CurrentRecordSize

int

The current record size for the file operation. In Variable recording mode needs to be set prior to WRITE and REWRITE

MinimumRecordSize

int

The minimum record size of the file. In Variable recording mode is set to 0, unless otherwise specified. In Fixed recording mode is equal to MaximumRecordSize

MaximumRecordSize

int

The maximum record size of the file. Can be used for the allocation of the I/O buffer.

LastRecordSize

int

The size of the last accessed record in the file. In the Variable recording mode needs to be checked to retrieve the actual size of READ

FileOrganization

FileOrganizationType

The file organization of the file

FileFormat

FileFormatType

The file format type

RecordingMode

RecordingModeType

The recording mode of the file

DataCompression

DataCompressionType

The data compression mode of the file

OpenMode

OpenModeType

The open mode of the file

AccessMode

AccessModeType

The access mode of the file

LockMode

LockModeType

The lock mode of the file

InterlanguageLocking

InterlanguageLockingType

The interlanguage locking mode

LineEndCRLF

bool

The indication of CRLF at the end of the line

MainFrameFBA

bool

The indication of the Mainframe FBA format

DefaultKey

int

The default key value ranges from 1 to MAX USHORT. For DefaultKey != 1 a program that does not declare key in the SELECT clause, OPEN, READ, will use the designated key as a base index

LineAdvancingFile

bool

The indication of the line advancing file. If true an EOR (LF in Unix and CR/LF in .NET) is added after each written record

MultipleReelFile

bool

The indication of the multiple reel file

NoDetectLock

bool

The indication of no detect lock mode

ExternalFileName

bool

The indication that file name is defined externally using the environment variables, otherwise Path specifies the file location

CreateIfMissing

bool

The indication that the non-existent file will be created on opening in Extend or InputOutput modes, otherwise OPEN operations fails

OptionalFile

bool

The indication of the optional file flag. Opening optional file in the INPUT mode gives 05 file status when the file does not exist, otherwise OPEN operation fails

AdvancingMode

bool

Indicates that the file is in the advancing mode. Note: should not be confused with the line advancing mode (which means the 'line sequential' mode)

Keys

Key[]

The array of file keys

CurrentKeyValue

byte[]

The current key value

CurrentKeyIndex

int

The current key index

DefaultKeyIndex

int

The default key index

GenKey

bool

Indicates that the file is marked as GENKEY

LastRecordNumber

int

The number of the last accessed record in the file.

IOBufferVarying

bool

The indication that I/O buffer is varying

LastStatusCode

ushort

Holds information of the last file status code in the ushort format

LastStatusCodeString

string

Holds information of the last file status code in the string format (example: "10" for EOF status)

LastStatusMessage

string

Holds the last status message. Generally, an error messages (if occurred)

DriverEncoding

RuntimeEncoding

The encoding used by the driver

RecordBasedFile default values - The table RecordBasedFile default values describe the default values for the plain file driver (which has not been configured by the file config).

Table 18. RecordBasedFile default values

Attribute name

Value

OpenMode

OpenModeType.Input

CurrentRecordSize

FileConstants.DEFAULT_RECORD_SIZE = 80

MinimumRecordSize

FileConstants.DEFAULT_RECORD_SIZE = 80

MaximumRecordSize

FileConstants.DEFAULT_RECORD_SIZE = 80

FileFormat

FileFormatType.Default

DataCompression

DataCompressionType.NoCompression

InterlanguageLocking

InterLangLockType.None

LastStatusCodeString

StatusCodes.STATUS_00_SUCCESS;

DriverEncoding

RuntimeEncoding.DefaultEncoding = RuntimeEncoding(1252)


File attributes API

Generally, all the File attributes can be configured in the Get# Set# format, where # is replaced with the desired attribute name. For example:

public ushort SetOpenMode(OpenModeType value);
public OpenModeType GetOpenMode();
...
public ushort SetRecordingMode(RecordingModeType mode);
public RecordingModeType GetRecordingMode();
...
File operations API
The open file operation
/// <summary>
/// Handles the OPEN file operation
/// </summary>
/// <returns>Status code of the operation</returns>
public ushort Open();
The close file operation
/// <summary>
/// Handles the CLOSE operation
/// </summary>
/// <param name="unit">Indicates reels or units closing, default value is false</param>
/// <param name="detail">Type of closing, default type not specified</param>
/// <returns>Status code of the operation</returns>
public ushort Close(bool unit, CloseType detail)
The read file operations
/// <summary>
/// Handles the READ DIRECT operation
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="recordOffset">Offset of the record in the file, not the record number</param>
/// <param name="lockType">Lock type for the operation, default type is NotSpecified</param>
/// <returns>Status code of the operation</returns>
public ushort ReadDirect(ArraySegment<byte> buffer, uint recordOffset, OperationLockType lockType)

/// <summary>
/// Handles the READ PREVIOUS operation
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="keyIdentifier">Defines index num to follow (INDEXED ONLY)
/// if value is ushort.MaxValue the index is not changed, otherwise 0</param>
/// <param name="lockType">Lock type for the operation, default type is NotSpecified</param>
/// <returns>Status code of the operation</returns>
public ushort ReadPrevious(ArraySegment<byte> buffer, ushort keyIdentifier, OperationLockType lockType)

/// <summary>
/// Handles the READ POSITION operation
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="position">Relative record number</param>
/// <returns>Status code of the operation</returns>
public ushort ReadPosition(ArraySegment<byte> buffer, ref uint position)

/// <summary>
/// Handles the READ RANDOM operation
/// RELATIVE files: recordNumber is the record number
/// INDEXED files: Key in record area is used, recordNumber is the index number (0 = primary)
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="recordNumber">Record or index number. See method summary</param>
/// <param name="lockType">Lock type for the operation, default type is NotSpecified</param>
/// <returns>Status code of the operation</returns>
public ushort ReadRandom(ArraySegment<byte> buffer, uint recordNumber, OperationLockType lockType)

/// <summary>
/// Handles the READ SEQUENTIAL operation, and updates the key data upon finish
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="keyIdentifier">Defines index num to follow (INDEXED ONLY)
/// if value is ushort. the index is not changed, otherwise 0</param>
/// <param name="lockType">Lock type for the operation, default type is NotSpecified</param>
/// <returns>Status code of the operation</returns>
public ushort ReadSequential(ArraySegment<byte> buffer, ushort keyIdentifier, OperationLockType lockType)
The write file operations
/// <summary>
/// Handles the WRITE operation (relative files) based on the data already set
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="recordNumber">Desired record number, default is 0</param>
/// <returns>Status code of the operation</returns>
public ushort Write(ArraySegment<byte> buffer, uint recordNumber)

/// <summary>
/// Handles the WRITE ADVANCING operation
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="advancingMode">Desired advancing mode</param>
/// <param name="counter">Line/page/channel counter value</param>
/// <returns>Status code of the operation</returns>
public ushort WriteAdvancing(ArraySegment<byte> buffer, AdvancingType advancingMode, int counter)
The rewrite file operation
/// <summary>
/// Handles the REWRITE operation
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <param name="recordNumber">Desired record number</param>
/// <returns>Status code of the operation</returns>
public ushort Rewrite(ArraySegment<byte> buffer, uint recordNumber)
The delete record file operation
/// <summary>
/// Deletes the record that was last read
/// </summary>
/// <param name="buffer">IO buffer for the operation</param>
/// <returns>Status code of the operation</returns>
public ushort DeleteRecord(ArraySegment<byte> buffer)
The start file operations
/// <summary>
/// Handles the START operation based on record number for relative files
/// </summary>
/// <param name="recordNumber"></param>
/// <param name="comparator">Key comparison type</param>
/// <returns>Status code of the operation</returns>
public ushort StartRel(uint recordNumber, KeyComparatorType comparator)

/// <summary>
/// Handles the START operation based on key for indexed files
/// </summary>
/// <param name="buffer">IO buffer for the operation where the key is provided</param>
/// <param name="comparator">Key comparison type</param>
/// <param name="keyIdentifier">The id of the key</param>
/// <param name="keyLength">The length of the key</param>
/// <returns>Status code of the operation</returns>
public ushort StartIndexed(ArraySegment<byte> buffer,
    KeyComparatorType comparator,
    ushort keyIdentifier,
    ushort keyLength)
The step file operations
/// <summary>
/// Handles the STEP FIRST operation
/// </summary>
/// <param name="lockType">Lock type for the operation, default type is NotSpecified</param>
/// <returns>Status code of the operation</returns>
public ushort StepFirst(OperationLockType lockType)

/// <summary>
/// Handles the STEP NEXT operation
/// </summary>
/// <param name="lockType">Lock type for the operation, default type is NotSpecified</param>
/// <returns>Status code of the operation</returns>
public ushort StepNext(OperationLockType lockType)
The other file operations (commit, rollback, erase, unlock)
/// <summary>
/// Handles the DELETE FILE (ERASE) operation
/// </summary>
/// <returns>Status code of the operation</returns>
public ushort DeleteFile()

/// <summary>
/// Handles the COMMIT operation
/// </summary>
/// <returns>Status code of the operation</returns>
public ushort Commit()

/// <summary>
/// Handles the ROLLBACK operation
/// </summary>
/// <returns>Status code of the operation</returns>
public ushort Rollback()

/// <summary>
/// Handles the UNLOCK operation
/// </summary>
/// <returns>Status code of the operation</returns>
public ushort Unlock()
All file operation methods return unsigned short status code indicating the file status after the operation.
Enumeration types used for the parameters and the return values, and the file status codes types are defined in the RainCodeLegacyRuntimeUtils library.

For the general rules of using the file operation API, see the General rules, and for the typical use cases, see the Examples.

6.5.5. Supported file drivers module

Namespace: RainCodeLegacyFileDriver.Driver

RainCodeLegacyFileDriver library includes implementations of several file drivers which can handle several file formats. All supported file drivers are derived from the base RecordBasedFile class and expose the same API. Supported file drivers (as listed before) are:

  • DotNetFHRecordBaseFile driver

  • EXTFHRecordBasedFile driver

  • PdsFolder driver

  • BlockedFile driver (VB-FB files)

  • LSEQFile driver

  • FujitsuVariableRecordLengthFile driver

  • SimpleRecordBaseFile driver

  • VsamSql driver

  • VsamLite driver

  • VsamOracle driver

All the file drivers can be further derived for special purposes.

DotNetFH driver is used as the default driver for file handling unless file configuration specifies differently.

6.5.6. RainCodeLegacyRuntimeUtils library

Namespace: RainCodeLegacyRuntimeUtils

Includes definition of the file parameter enumeration types, various constants and status codes used in the RainCodeRuntime libraries implemented in the following classes:

  • FileEnums class - defines all the enumeration types used for the file attributes and the file configurations

  • FileConstants class - includes constant definitions used in the RainCodeLegacyFileDriver library

  • StatusCodes class - includes the COBOL file status codes and their string-numeric conversions methods

  • RuntimeEncoding class - wrapper class for used the encoding

Defined FileEnums types:

  • enum FileOrganizationType defines:

    • LineSequential = 0x00,

    • Sequential = 0x01,

    • Indexed = 0x02,

    • Relative = 0x03,

    • Undefined = 0xFF

  • enum RecordingModeType defines:

    • Fixed = 0x00,

    • Variable = 0x01,

    • Undefined = 0xFF

  • enum AccessModeType defines:

    • Sequential = 0x00,

    • Random = 0x04,

    • Dynamic = 0x08,

    • User = 0x80,

    • Undefined = 0xFF

  • enum LockModeType defines:

    • Exclusive = 0x01,

    • Automatic = 0x02,

    • Manual = 0x04,

    • RetryLock = 0x08,

    • SkipLock = 0x10,

    • WriteLock = 0x40,

    • MultipleRecord = 0x80,

    • Undefined = 0xFF

  • enum DispositionType defines:

    • New = 0x00,

    • Old = 0x01,

    • Mod = 0x02,

    • Shr = 0x03,

    • Undefined = 0xFF

  • enum FileFormatType defines:

    • Default = 0x00,

    • CISAM = 0x01,

    • Level2 = 0x02,

    • COBOL = 0x03,

    • IdxFormat = 0x04,

    • BtrieveAnsiEmul = 0x05,

    • BtrieveNoAnsiEmulation = 0x06,

    • MainframePrintFile = 0x0B,

    • Undefined = 0xFF

  • enum DataCompressionType defines:

    • NoCompression = 0x00,

    • MicroFocus = 0x0F,

    • UserDefined = 0xF0,

    • Undefined = 0xFF

  • enum OpenModeType defines:

    • Input = 0x00,

    • Output = 0x01,

    • InputOutput = 0x02,

    • Extend = 0x03,

    • Undefined = 0xFF

  • enum InterLangLockType defines:

    • None = 0x00,

    • Active = 0x80,

    • Undefined = 0xFF

  • enum OperationLockType defines:

    • NoLock = 0x00,

    • Lock = 0x01,

    • KeptLock = 0x02,

    • NotSpecified = 0x03,

    • Undefined = 0xFF

  • enum CloseType defines:

    • DetailForRemoval = 0x00,

    • DetailWithNoRewind = 0x01,

    • DetailWithLock = 0x02,

    • DetailNotSpecified = 0x03,

    • Undefined = 0xFF

  • enum AdvancingType defines:

    • NoAdvancing = 0x00,

    • BeforeLine = 0x01,

    • AfterLine = 0x02,

    • BeforePage = 0x03,

    • AfterPage = 0x04,

    • BeforeChannel = 0x05,

    • AfterChannel = 0x06,

    • Undefined = 0xFF

  • enum KeyComparatorType defines:

    • PrimeKey = 0x00,

    • Equal = 0x01,

    • LessThan = 0x02,

    • LessOrEqual = 0x03,

    • GreaterThan = 0x04,

    • GreaterOrEqual = 0x05,

    • Undefined = 0xFF

6.5.7. General rules

Following rules need to be applied when the RainCodeLegacyFileDriver API is used to achieve the desired behavior:
  • Creating file driver with the File manager module

    There are three ways of creating a file driver:

    • Invoking CreateFile(string logicalName, bool forceCreate) by setting forceCreate to true - creates file driver with the default configuration

    • Invoking CreateFile(string logicalName, bool forceCreate) by setting forceCreate to false - the file configuration with the same logicalName must be registered BEFORE invoking the method

    • Invoking CreateFileFromConfig(FileConfig config) - config must be created programmatically with the desired attributes

    See Examples for more information.

  • Invoking file operations

    • Open - requires setting: file open mode, and if not previously configured (via file configuration): file recording mode, file access mode, and record size BEFORE invoking the method.

      Setting record size can be done by calling the InitializeRecordSize(int size) which will set current, maximum and minimum record sizes. If variable recording mode is set prior to setting a record size, the minimum record size will be set to 0 implicitly otherwise, it will be set to the specified size.

    • Read - in variable recording mode requires: GetLastRecordSize to be called AFTER invoking a read operation to get the actual size of the read data.

    • Write/Rewrite - in variable recording mode requires: SetCurrentRecordSize(int size) to be called with the size of the data to be written, BEFORE invoking write operation

    • Overall:

      • All file operation methods return unsigned short file status code, as a result of the operation. The file status codes and their conversions to/from the string are available in the RainCodeLegacyRuntimeUtils → class: StatusCodes

      • File operation methods that work with the records (read, write, delete, rewrite, start) require IO buffer allocated from the user side and passed as a parameter to the specific file operation method

      • Size of the allocated buffer should match at least the MinimumRecordSize, and at most the MaximumRecordSize

      • Pointer to the buffer is passed as the ArraySegment<byte> object:

        byte[] IObuffer = new byte[MaximumRecordSize];
        ArraySegment<byte> IObufferPtr = new ArraySegment<byte>(IObuffer);
        statusCode = file.Write(IObufferPtr, 0);

6.5.8. I/O Raincode runtime support

Raincode Legacy Compilers supports I/O similar to those described in this document for SQL (See section SQL support) and for CICS (See section CICS support). Operations on files are forwarded to an object of the RainCodeLegacyRuntime.IO.IORuntime class, attached to the current execution context. The structure of the IO subsystem is built according to the following diagram (Raincode Legacy Compilers supports the I/O structure of the IO subsystem).

image071
Figure 110. Raincode Legacy Compilers supports the I/O structure of the IO subsystem

During its configuration phase, the IORuntime (See section Transient data queues support ) creates instances of the record-based file handlers (for READ, WRITE and REWRITE operations) and instances of the stream-based file handlers (for PUT and GET operations). Afterwards, all the I/O operations are delegated to the corresponding handler.

6.5.9. Record-based handlers

The record-based handler RainCodeLegacyRuntime.IO.RecordBasedFile implements the record based operations on a file and acts as a wrapper of the file driver API RainCodeLegacyFileDriver library.

6.5.10. Stream-based handlers

The stream-based handler RainCodeLegacyRuntime.IO.Streams.FileStream implements the stream operations on a file. Stream operations on a memory character stream are performed using an instance of the RainCodeLegacyRuntime.IO.Streams.StringStream class, which is instantiated on demand based on the memory area on which the PUT and GET operations will be performed.

6.5.11. Specializing IORuntime

Custom behavior of the IO subsystem can be achieved by subclassing the IORuntime class:

public class MyOperation: IORuntime
{
  public MyOperation(ExecutionContext context)
      :base(context)
  {
  }
}

In that case, the custom execution context must redefine the IORuntime factory, as follows:

public class MyExecutionContext: ExecutionContext
{
  private MyOperation IOOperation
  {
    get;
    set;
  }

  public override IORuntime getIOOperation()
  {
    if(IOOperation==null)
    {
      IOOperation = new MyOperation(this);
      IOOperation.ConfigureSystemDatasets();
    }

    return IOOperation;
  }
}

6.5.12. Configuration of the file support

The file support configures the parameters of the files according to the configuration files provided to rclrun using the parameter :file_config when the file configuration is specified in the xml (See section Creating file configurations). You can programmatically configure the files by registering new file configurations by invoking IOOperation.LoadAndRegisterConfigurationFromFile(string path) or when the file configuration is programmatically created IOOperation.RegisterConfiguration(FileConfigCollection configCollection).

The file configuration can be programmatically loaded (instead of using the :file_config attribute of rclrun) using the following code:

executionContext
    .getIOOperation()
    .LoadAndRegisterConfigurationFromFile("c:\\Temp\\file.cfg");
See Example 14 in Examples for more information on how to register the programmatically created file configurations.

6.5.13. Examples

For all examples, an assumption is made that the FileManager object has been previously created.
Example 1. Create the DotNetFH file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.DOT_NET_FH_FILE_ALIAS;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
fcfg.DriverAssembly = FileManager.DOT_NET_FH_FILE_ALIAS can be omitted because DotNetFH is used as a default driver.
Example 2. Create the DotNetFH fixed record based file
FileConfig fcfg = new FileConfig("TEST");
fcfg.RecordingMode = RecordingModeType.Fixed;
fcfg.RecordSize = 100;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 3. Create the DotNetFH variable record based file
FileConfig fcfg = new FileConfig("TEST");
fcfg.RecordingMode = RecordingModeType.Variable;
fcfg.RecordSize = 100;
fcfg.MinimumRecordSize = 60;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 4. Create the DotNetFH line-sequential file
FileConfig fcfg = new FileConfig("TEST");
fcfg.FileOrganization = FileOrganizationType.LineSequential;
fcfg.RecordingMode = RecordingModeType.Variable;
fcfg.RecordSize = 132;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 5. Create the DotNetFH relative file
FileConfig fcfg = new FileConfig("TEST");
fcfg.FileOrganization = FileOrganizationType.Relative;
fcfg.RecordingMode = RecordingModeType.Fixed;
fcfg.RecordSize = 40;
fcfg.AccessMode = AccessModeType.Random;
fcfg.Path = "C:\Temp\output.txt";
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 6. Create the legacy ExtFH file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.EXTFH_FILE_ALIAS;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 7. Create the legacy FB file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.LEGACY_BLOCKED_FILE_ALIAS;
fcfg.RecordingMode = RecordingModeType.Fixed;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 8. Create the legacy VB file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.LEGACY_BLOCKED_FILE_ALIAS;
fcfg.RecordingMode = RecordingModeType.Variable;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 9. Create the legacy LSEQ file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.LEGACY_LSEQ_FILE_ALIAS;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 10. Create the legacy Fujitsu file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.LEGACY_FUJITSU_FILE_ALIAS;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...
Example 11. Create the legacy Simple record based file with the default configuration
FileConfig fcfg = new FileConfig("TEST");
fcfg.DriverAssembly = FileManager.LEGACY_SRB_FILE_ALIAS;
RecordBasedFile file = FileManager.CreateFileFromConfig(fcfg);
...

Example 12. Open and close the file operations
XML configuration file at: "C:\\tmp\\cfg.xml"
<FileConfigCollection>
    <FileConfig LogicalName="INFILE">
        <RecordSize>67</RecordSize>
        <MinimumRecordSize>50</MinimumRecordSize>
        <RecordingMode>Variable</RecordingMode>
        <LineEndCRLF>true</LineEndCRLF>
        <MainFrameFBA>true</MainFrameFBA>
    </FileConfig>
</FileConfigCollection>
Source code
FileManager.LoadAndRegisterConfigurationFromFile("C:\\tmp\\cfg.xml");
RecordBasedFile file = FileManager.CreateFile("INFILE");

file.SetOpenMode(OpenModeType.Input);
statusCode = file.Open();
...
statusCode = file.Close();
Example 13. Read and write the file operations
...
FileManager.LoadAndRegisterConfigurationFromFile(filePath);
RecordBasedFile file = FileManager.CreateFile(fileName);
// before opening make sure to follow OPEN rules
file.SetOpenMode(OpenModeType.InputOutput);
file.SetRecordingMode(RecordingModeType.Variable);
file.SetAccessMode(AccessModeType.Sequential);
file.InitializeRecordSize(100);

statusCode = file.Open();
byte[] IObuffer = new byte[file.GetMaximumRecordSize()];
ArraySegment<byte> IObufferPtr = new ArraySegment<byte>(IObuffer);
...
file.SetCurrentRecordSize(80);
statusCode = file.Write(buffer);
...
statusCode = file.ReadSequential(buffer);
readLen = file.GetLastRecordSize();
...
statusCode = file.Close();
Check the file status codes before proceeding with the file handling
Example 14. Register the programmatically created file configuration in the desired execution context
FileConfigCollection configCollection = new FileConfigCollection();
FileConfig fcfg = new FileConfig("TEST");

fcfg.DriverAssembly = FileManager.DOT_NET_FH_FILE_ALIAS;
fcfg.RecordingMode = RecordingModeType.Fixed;
fcfg.RecordSize = 100;
configCollection.FileConfigs.Add(fcfg);
//Or add several file definitions ...

executionContext.getIOOperation().RegisterConfiguration(configCollection);
...
Example 15. Explicitly specify the line size for the SYSPRINT
XML configuration for SYSPRINT
<FileConfigCollection>
    <FileConfig LogicalName="SYSPRINT">
        <Path>SYSPRINT.txt</Path>
        <RecordingMode>Variable</RecordingMode>
        <FileOrganization>LineSequential</FileOrganization>
        <PrintStream>true</PrintStream>
        <PliTabs>
            <LineSize>133</LineSize>
        </PliTabs>
    </FileConfig>
</FileConfigCollection>
The record size will be automatically calculated from the line size if not specified.

6.6. ExtFH driver specifics

The ExtFH driver embedded by default with the environment has the following parameters (these are handled on the mainframe by the JCL, on the .NET platform through configuration):

  • Default is the only available file format

  • InterlanguageLocking is not available

  • MultipleReel file is not available

  • NoDetectLock is not available.

6.7. VsamSql driver specifics

The VsamSql driver stores data in a table instead of a file; therefore, it has two specific elements: the connection string to the database and the table’s name in which the data are stored.

Example of XML file configuration to use VsamSql driver:

<FileConfigCollection>
    <FileConfig LogicalName="output_1">
        <Path>:DEFAULT:TEST.WR2</Path>
        <DriverAssembly>VsamSql</DriverAssembly>
        <RecordSize>42</RecordSize>
        <DriverParameters>
            <ConnectionString>
Data Source=sqlsrv2017; Uid=user; Pwd=pwd;Initial Catalog=VsamSql;
            </ConnectionString>
            <TableName>REC_SEQ</TableName>
        </DriverParameters>
    </FileConfig>
</FileConfigCollection>

6.8. VsamLite driver specifics

The VsamLite driver stores data in a table instead of a file; therefore, it has one specific element: the table in which the data are stored.

Example of XML file configuration to use VsamLite driver:

<FileConfigCollection>
    <FileConfig LogicalName="output_1">
        <Path>output_1.db3</Path>
        <DriverAssembly>VsamLite</DriverAssembly>
        <RecordSize>42</RecordSize>
        <DriverParameters>
            <TableName>REC_SEQ</TableName>
        </DriverParameters>
    </FileConfig>
</FileConfigCollection>

6.9. VsamOracle driver specifics

The VsamOracle driver stores data in a table instead of a file; therefore, it has two specific elements: the connection string to the database and the table’s name in which the data are stored.

Example of XML file configuration to use VsamOracle driver:

<FileConfigCollection>
    <FileConfig LogicalName="output_1">
        <Path>:DEFAULT:TEST.WR2</Path>
        <DriverAssembly>VsamOracle</DriverAssembly>
        <RecordSize>42</RecordSize>
        <DriverParameters>
            <ConnectionString>
User Id=DOC; Password=pwd; Data Source=//oracledb.mycompany.be:1521/orcl            </ConnectionString>
            <TableName>REC_SEQ</TableName>
        </DriverParameters>
    </FileConfig>
</FileConfigCollection>

6.10. Timeshift for test purpose

Timeshift testing is needed to test how a program behaves in the future or the past. The Raincode legacy runtime provides a way to perform Timeshift testing by letting you modify the date and time retrieved by the program when executing.

Timeshift can be:

  • absolute by defining an initial date and time for the program run, or

  • relative by defining a date time offset relative to the current time.

There are two alternative ways to define Timeshift:

1. Using rclrun command line parameters defined in rclrun command-line options: -InitialDateTime for absolute Timeshift and -DateTimeOffset for relative Timeshift

2. Using environment variables which must be defined before starting rclrun: RC_INITIALDATETIME for absolute Timeshift and RC_DATETIMEOFFSET for relative Timeshift

The date time offset is defined in C# format for TimeSpan. This offset is retrieved (only once) at the program start and is added to DateTime.Now whenever the program retrieves the date or time.

The initial date and time are defined in C# format for DateTime. If the date time offset is not defined, the initial date and time are retrieved (only once) at program start and are used to compute the date time offset between now and the provided value. This offset is then used as described above.

Subprograms use the same date time offset.

6.11. Double-byte encodings

Raincode Legacy Compilers has limited support for the MBCS encodings. The MBCS encodings is currently only supported in COBOL through the builtin functions DISPLAY-OF and NATIONAL-OF. There is no support for the PIC G at the moment.

The MBCS Encodings are provided by implementing a custom System.Text.EncodingProvider. The rclrun can load a plugin which contains an implementation of an EncodingProvider, through the argument NonStandardEncodingsAssembly. The Raincode Legacy Compilers distribution provides a plugin (RainCodeLegacyRuntimeEncodings) that currently adds support for the codepages 1388 and 1399.

Raincode Legacy Compilers supports national (PIC N), including the national literals. If the source code contains multibyte national literals, it is best to use the utf-8 encoding, and pass the Utf8Input flag to the compilers.

For detailed information on the list of accepted encoding names, refer to the List of encodings.

7. SQL and CICS support

This section describes how Raincode Legacy Compilers support SQL and CICS.

7.1. The common mainframe way

The common way of dealing with SQL or CICS extensions in a Mainframe environment is to use a precompiler as depicted:

image044
Figure 111. SQL and CICS support - SQL Precompiler, the common mainframe way

A precompiler takes source code with embedded SQL statements [11], and replaces them in the original text with one or more calls to a run-time system that produce the desired effect on the database.

This approach, while pragmatic and tested over time, also has serious drawbacks:

  • Precompilers are complex and sensitive beasts, as they require at least a basic understanding of the host language at hand (in this case, PL/I)

  • Each database vendor has its own precompiler, as they must generate calls to different proprietary run-time systems

  • Error reporting and debugging suffer from this process, since legacy compilers report errors, and you debug at the level of the legacy compiler’s input, namely, the output of the SQL preprocessor [12]

  • It also increases compilation time, as an additional pass is required in the process.

7.2. The Raincode Legacy Compilers way

To address the concerns listed above, Raincode Legacy Compilers takes a different approach (See SQL and CICS support - The Raincode Legacy Compilers way):

  • Raincode Legacy Compilers recognize SQL and CICS statements natively, as any other legacy program statement in the source code

  • These statements translate to calls to the legacy program execution context at hand (See section AddressSpace)

  • The legacy program execution context then redirects these SQL and CICS calls to a pluggable run-time component.

image045
Figure 112. SQL and CICS support - The Raincode Legacy Compilers way

Raincode’s architecture allows you to support a new target database by providing an appropriate Raincode Legacy Compilers Visual Studio plug-in, eliminating the need to analyze the source program and its extensions. Through inheritance, you can also extend the existing Raincode Legacy Compilers Visual Studio plug-in functionality.

It can even allow different execution contexts to be connected to separate SQL run times, allowing each thread to connect to a separate database handler.

7.3. Source-level SQL Conversions

Even though SQL is a standard defined by the ISO/IEC.[13], there are big differences between competing implementations, up to a point where SQL code can be moved, as is, from one database to another, although in fairly ideal cases only. When dealing with any large system, dialect issues must be considered when moving from one database server to another.

Raincode Legacy Compilers addresses this issue by integrating a compile-time transformation that converts DB/2 SQL into equivalent SQL Server SQL, providing a combined solution to re-host legacy program code and database migration. The Raincode Legacy Compilers translation scenario for mainframe SQL to .NET TSQL is represented in detail in the diagram shown in Raincode Legacy Compilers - Mainframe SQL to .NET TSQL translation scenario.

7.3.1. No translation scenario

The conversion scenario where there is no translation performed is described below and in the diagram (Raincode Legacy Compilers - No translation scenario):

  • You may wish to maintain your code on the original DB2 server to ensure that your applications can continue to use the DB2 server while running on the .NET platform

  • Manual translation requires complex arrangements by you, resulting in the loss of single sourcing (See section Single sourcing) - manual migration requires many hours of complex work fraught with the potential for human error.

image046
Figure 113. Raincode Legacy Compilers - No translation scenario

The following are examples of the transformations performed by Raincode Legacy Compilers when converting DB/2 SQL code to SQL Server during simple translation:

DB2

SELECT <expression> FROM SYSIBM.SYSDUMMY1

becomes:

TSQL

SELECT <expression>

DB2

SELECT * FROM <table> FETCH FIRST ROW ONLY

becomes:

TSQL

SELECT TOP 1 * FROM <table>

7.3.2. Simple translation scenario

To run your Mainframe SQL statements on .NET, you must translate them. However, if database schema information is not available, then only a simple translation of your source code can be performed via Raincode Legacy Compilers.

image047
Figure 114. Raincode Legacy Compilers - Simple translation scenario

Note that for both Simple and Schematic translation, you must use both of the following options:

  • :SQLSourceDialect=<db2|sqlserver>(indicates the dialect used in the legacy program’s source file for the SQL statements)

  • :SQL=<sqlserver|db2|db2his> (indicates the database targets of the compilation).

7.3.3. Schematic translations (SQL Server) scenario

If information about your database schema is available, you can perform a complete translation via Raincode Legacy Compilers. Raincode Legacy Compilers requires a SQL Server instance (Raincode Legacy Compilers - Schematic translation scenario) to connect to and extract a schema.

The objective is to determine the types of the columns in the database because some DB2 SQL functions have to be translated differently for SQL Server, depending on the types of arguments used.

sql schema migration
Figure 115. Raincode Legacy Compilers - Schematic translation scenario

For a schematic transformation, add both of the simple options (See section Simple translation scenario) and also :SQLServerSchemaCacheFile=<filename.xml> (indicates the name of the file used to cache the meta-data of the SQL Server instance to which the source code refers to).

The following are examples of the transformations performed by Raincode Legacy Compilers when converting DB/2 SQL code to SQL Server during schematic translation (note that these are SQL fragments):

DB2

<date> + 15 YEARS

becomes:

TSQL

DATEADD(YEAR, 15, <date>)

7.3.4. Access to a schema

A number of the transformations performed by the Raincode Legacy Compilers are context insensitive and can be applied by looking at the SQL code alone. Some other transformations require type information and thereby imply that the schema must be known so that type of expressions can be inferred.

image048
Figure 116. Raincode Legacy Compilers - Transformation and Static SQL

Raincode Legacy Compilers require a SQL Server instance to connect to and extract the schema. The connection string for this schema can be given using the :SQLServerConnectionString command-line option.

The only parts of the schema used in this conversion process are the tables, columns, stored procedures and user defined functions. The indexes and all other SQL artifacts are ignored.

7.3.5. Cached schema

It is not always convenient to have a SQL Server instance ready when compiling. As a substitute, you can use a cached schema in the form of an XML file that provides the minimal information required for the SQL code analysis. You can specify the cached schema to use by using the :SQLServerSchemaCacheFile command-line option (See section Raincode Legacy Compilers command-line options).

When this command-line option is combined with :SQLServerConnectionString the schema is read from the database connection and saved in the cache file. It is a simple and reliable way to build a cache file out of an existing database, and allows the cache file to be moved around, to allow for the compilation of legacy program modules on machines without a connection to the appropriate database instance.

<?xml version="1.0" encoding="ASCII"?>
<DataBase>
  <Schema Name="dbo" Dialect="Microsoft SQL Server 2008">
    <Table Name="people">
      <Column Name="LastName" OrdinalPostion="1">
        <SqlType xsi:type="Char" MaximumLength="20">
        </SqlType>
      </Column>
      <Column Name="FirstName" OrdinalPostion="2">
        <SqlType xsi:type="VarChar" MaximumLength="20">
        </SqlType>
      </Column>
      <Column Name="Birthdate" OrdinalPostion="3">
        <SqlType xsi:type="Date">
        </SqlType>
      </Column>
    </Table>
    <Table Name="tbl1">
      <Column Name="ColI1" OrdinalPostion="1">
        <SqlType xsi:type="Int" NumericPrecision="10" NumericPrecisionRadix="10">
        </SqlType>
      </Column>
      <Column Name="ColI2" OrdinalPostion="2">
        <SqlType xsi:type="Int" NumericPrecision="10" NumericPrecisionRadix="10">
        </SqlType>
      </Column>
    </Table>
  </Schema>
</DataBase>

This example cache file above describes a database instance with two tables and their respective columns. The table XML description of SQL database schema describes the syntax of the XML file which is referenced by the :SqlServerSchemaCacheFile command- line option; the XML file must have a root element of <DataBase> or <Schema>.

Table 19. XML description of SQL database schema
Element name Description

<DataBase>

  • Example

    <DataBase>
    …schema elements…
    </DataBase>

A <DataBase> element group several schemas.

Attributes:

  • None

Content Model:

  • One or more <Schema> elements

<Schema>

  • Example

    <Schema Name="dbo" Dialect="Microsoft SQL Server 2008">
    …table elements…
    </Schema>

A <Schema> element represents an SQL schema (or database for DBMS that doesnt use schemas).

Attributes:

  • Name - Name of the schema

  • Dialect - SQL dialect of the DBMS

Content Model:

  • Zero or more <Table> elements

<Table>

  • Example

    <Table Name="people">
    …column elements…
    </Table>

A <Table> element represents an SQL table or view.

Attributes:

  • Name - Name of the table or view

Content Model:

  • Zero or more <Column> elements

<Column>

  • Example

    <Column Name="LastName"
    OrdinalPostion="1">
    SqlType  element
    </Column>

A <Column> element represents a column in an SQL table or view.

Attributes:

  • Name- Name of the column

  • OrdinalPosition - Index of the column (for positional SQL access)

Content Model:

  • A single <SQLType> element with a mandatory first attribute named xsi:type, as described below.

Column Types are described in the tables below.

Table 20. Numeric Type
Name Description

xsi:type="BigInt"

  • Example

    <SqlType xsi:type="BigInt"
    NumericPrecision="19"
    NumericPrecisionRadix="10"/>

Represents an SQL BIGINT column type.

Attributes:

  • NumericPrecision - The number of digits, in the NumericPrecisionRadix base, that are stored

  • NumericPrecisionRadix - The base in which NumericPrecision counts digits

Content Model:

  • Empty

xsi:type="Int"

  • Example

    <SqlType xsi:type="Int"
    NumericPrecision="10"
    NumericPrecisionRadix="10"/>

Represents an SQL INT column type.

Attributes

  • NumericPrecision - The number of digits, in the NumericPrecisionRadix base, that are stored

  • NumericPrecisionRadix - The base in which NumericPrecision counts digits

Content Model

  • Empty

xsi:type="SmallInt"

  • Example

    <SqlType xsi:type="SmallInt"
    NumericPrecision="5"
    NumericPrecisionRadix="10"/>

Represents an SQL SMALLINT Column type.

Attributes

  • NumericPrecision - The number of digits, in the NumericPrecisionRadix base, that are stored

  • NumericPrecisionRadix - The base in which NumericPrecision counts digits

Content Model

  • Empty

xsi:type="Decimal"

  • Example

    <SqlType xsi:type="Decimal"
    NumericPrecision="11"
    NumericPrecisionRadix="10"
    NumericScale="2"/>

Represents an SQL DECIMAL column type.

Attributes

  • NumericPrecision - The number of digits, in the NumericPrecisionRadix base, that are stored

  • NumericPrecisionRadix - The base in which NumericPrecision counts digits

  • NumericScale - The number of digits, in the NumericPrecisionRadix base, that are considered as fractional.

Content Model

  • Empty

xsi:type="Float"

  • Example

    <SqlType xsi:type="Float"
    NumericPrecision="53"
    NumericPrecisionRadix="2"/>

Represents an SQL FLOAT column type.

Attributes

  • NumericPrecision - The number of digits, in the NumericPrecisionRadix base, that are stored

  • NumericPrecisionRadix - The base in which NumericPrecision counts digits

Content Model

  • Empty

Table 21. Character Types
Name Description

xsi:type="Char"

  • Example

    <SqlType xsi:type="Char"
    MaximumLength="10"/>

Represents an SQL CHAR column type.

Attributes:

  • MaximumLength - The maximum length of the column.

Content Model:

  • Empty

xsi:type="VarChar"

  • Example

    <SqlType xsi:type="VarChar"
    MaximumLength="2500"/>

Represents an SQL VARCHAR column type.

Attributes

  • MaximumLength - The maximum length of the column.

Content Model

  • Empty

xsi:type="NChar"

  • Example

    <SqlType xsi:type="NChar"
    MaximumLength="10"/>

Represents an SQL NCHAR column type.

Attributes

  • MaximumLength - The maximum length of the column.

Content Model

  • Empty

xsi:type="NVarChar"

  • Example

    <SqlType xsi:type="NVarChar"
    MaximumLength="20"/>

Represents an SQL NVARCHAR column type.

Attributes

  • MaximumLength - The maximum length of the column.

Content Model

  • Empty

xsi:type="Text"

  • Example

    `<SqlType  xsi:type="Text"/>`

Represents an SQL TEXT column type.

Table 22. Binary Types
Name Description

xsi:type="Binary"

  • Example

    <SqlType xsi:type="Binary"
    MaximumLength="50"/>

Represents an SQL BINARY column type.

Attributes

  • MaximumLength - The maximum length of the column.

Content Model

  • Empty

xsi:type="VarBinary"

  • Example

    <SqlType xsi:type="VarBinary"
    MaximumLength="50"/>

Represents an SQL VARBINARY column type.

Attributes

  • MaximumLength - The maximum length of the column.

Content Model

  • Empty

Table 23. Temporal Types
Name Description

xsi:type="DateTime"

  • Example

    <SqlType xsi:type="DateTime"
    DateTimePrecision="3"/>

Represents an SQL Server DATETIME column type.

Attributes:

  • DateTimePrecision - The precision of the column.

Content Model:

  • Empty

xsi:type="DateTime2"

  • Example

    <SqlType xsi:type="DateTime2"
    DateTimePrecision="7"/>

Represents an SQL Server DATETIME2 column type.

Attributes:

  • DateTimePrecision - The precision of the column.

Content Model:

  • Empty

xsi:type="Date"

  • Example

    <SqlType xsi:type="Date"/>

Represents an SQL DATE column type.

xsi:type="Time"

  • Example

<SqlType xsi:type="Time" DateTimePrecision="7"/>

Represents an SQL TIME column type.

Attributes:

  • DateTimePrecision - The precision of the column.

Content Model:

  • Empty

xsi:type="Timestamp"

  • Example

<SqlType xsi:type="Timestamp"/>

Represents an SQL TIMESTAMP column type.

Table 24. Unknow Types
Name Description

xsi:type="Unknown"

  • Example

    <SqlType xsi:type="Unknown"
    DataType="sql_variant"/>

Represents any other SQL column type.

Attributes:

  • DataType - The SQL type of the column

Content Model:

  • Empty

7.3.6. Specify custom rewriting rules

Raincode has provided you with an xml file containing predefined rewriting rules:

  • DB2ToSQLServerRewritingRules.xml

This xml file allows you to rewrite (customize) functions which cater for mapping most of the predefined functions of DB2 in order to predefine SQL Server functions and/or user-defined (from SQL Server’s point of view) functions that Raincode has provided (in StoredProcedures_Raincode.sql, see section Setting up a SQL Server instance). However, you must write some rewriting rules to map any other DB2 functions you may use (including any functions unknown to Raincode or your own user-defined DB2 functions). Once you have written your XML file describing your rewriting rules, you will then run the following option in Raincode Legacy Compilers:

:SQLRewritingRulesFile=<filename.xml>

The name of a file containing rules to rewrite SQL expressions. This option can be specified multiple times.

This example describes the transformation for SUBSTR which is illustrated just below:

plirc :SQLSourceDialect=db2 :SQL=sqlserver :SQLRewritingRulesFile=DB2ToSQLServerRewritingRules.xml

where DB2ToSQLServerRewritingRules.xml contains something like:

<RewritingRules FromDialect="DB/2" ToDialect="Microsoft SQL Server 2008">
  <RewritingRule>
    <!-- SUBSTR(<string> : string, <start> : integer)
      -->
    <FunctionUsages>
      <FunctionUsage Name="SUBSTR">
        <Arguments>
          <AnyCharStringArgument Name="string"/>
          <ExactlyTypedArgument Name="start">
            <SqlType xsi:type="Int"/>
          </ExactlyTypedArgument>
        </Arguments>
      </FunctionUsage>
    </FunctionUsages>
    <Action>
      <Rewrite xsi:type="RenameFunctor" NewFunctor="SUBSTR" NewSchemaName="dbo"/>
      <Rewrite xsi:type="InsertArgument" Image="DEFAULT" Position="3" Type="SPECIFIER"/>
    </Action>
  </RewritingRule>
  <!-- More rules -->
</RewritingRules>

The following are examples of the transformations performed by Raincode Legacy Compilers when converting DB/2 SQL code to SQL Server during schematic translation:

External custom rewriting rule:

DB2

SUBSTR(<str>, <int>, <int>)

becomes:

TSQL

commonschema.SUBSTR(<str>, <int>, <int>)

(This example uses a TSQL user-defined function to emulate DB2 SUBSTR and is part of a list of custom rewriting rules that come with Raincode Legacy Compilers). The XML file must have a root element of <RewritingRules>. The following table describes the syntax:

Table 25. Syntax of the rules rewriting file

Element name

Description

<RewritingRules>

A <RewritingRules> element groups a set of rules.

Attributes:

  • FromDialect - Name of the SQL dialect that this set of rewriting rule takes as input. Default: DB/2. Optional

  • ToDialect - Name of the SQL dialect that this set of rewriting rule produces as output. Default: SQL Server. Optional

Content Model:

  • Zero or more <RewritingRule> elements

<RewritingRule>

A <RewritingRule> element describes a single rewriting rule.

Attributes:

  • None

Content Model:

  • One <FunctionUsages> element followed by one <Action> element

<FunctionUsages>

A <FunctionUsages> element groups all the usages of a function to which a rule applies.

Attributes:

  • None

Content Model:

  • One or more <FunctionUsage> elements

<FunctionUsage>

A <FunctionUsage> element defines a pattern of function to be rewritten by the rule.

Attributes:

  • Name - Name of the function to rewrite. Mandatory

  • SchemaName - Schema of the function to rewrite. No default. Optional

Content Model:

  • One <Arguments> element

<Arguments>

An <Arguments> element specifies the patterns that each argument of a function call has to match for the rewriting rule to be applied.

Attributes:

  • None

Content Model:

  • Zero or more elements chosen from

    • <AnyNumericArgument>

    • <AnyCharStringArgument>

    • <AnyArgument>

    • <SpecifierArgument>

    • <ExactlyTypedArgument>

<AnyNumericArgument>

An <AnyNumericArgument> element defines a pattern that matches any numeric argument

Attributes:

  • Name - Name of the argument (unused, for documentation only). Optional

Content Model:

  • None

<AnyCharStringArgument>

An <AnyCharStringArgument> element defines a pattern that matches any character or string argument

Attributes:

  • Name - Name of the argument (unused, for documentation only). Optional

Content Model:

  • None

<AnyArgument>

An <AnyArgument> element defines a pattern that matches any argument

Attributes:

  • Name - Name of the argument (unused, for documentation only). Optional

Content Model:

  • None

<SpecifierArgument>

A <SpecifierArgument> element defines a pattern that matches a specific (keyword-like) argument

Attributes:

  • Name - Name of the argument (unused, for documentation only). Optional

  • Image - The value to be matched.

  • Mandatory

Content Model: * None

<ExactlyTypedArgument>

An <ExactlyTypedArgument> element defines pattern that matches any argument of the given type

Attributes:

  • Name - Name of the argument (unused, for documentation only).

  • Optional

Content Model:

  • One <SqlType> element with a mandatory first attribute named xsi:type, as defined in

<Action>

An <Action> element groups the steps to transform the matched function call

Attributes:

  • None

Content Model:

  • Either one or more <Rewrite> element with a mandatory first argument named xsi:type as described below or one <KeepAsIs> element.

<KeepAsIs>

A <KeepAsIs> element specifies that a specific function invocation doesnt need translation

Attributes:

* None

Content Model:

* None

<Rewrite xsi:type="InsertArgument">

A <Rewrite xsi:type="InsertArgument"> element specifies that the translated function will take an extra argument.

Attributes:

  • Position - The position at which the new argument is to be inserted. Mandatory

  • Image - The value to be inserted. Mandatory

  • Type - The type of value to insert (STRING, SPECIFER, INTEGER, etc.). Mandatory

<Rewrite xsi:type="ReplaceArgument">

A <Rewrite xsi:type="ReplaceArgument"> element specifies that the translated function will replace the specified argument with a constant.

Attributes:

  • Position - The position at which the argument is to be replaced. Mandatory

  • Image - The new value of the argument. Mandatory

  • Type - The type of the new value (STRING, SPECIFER, INTEGER, etc.). Mandatory

[source,xml]
<Rewrite  xsi:type="RenameFunctor">

A <Rewrite xsi:type="RenameFunctor"> element specifies that the translated function will have a different name and/or schema.

Attributes:

  • NewFunctor - The name of the new function (in the target dialect). Mandatory

  • NewSchema - The schema of the new function. Optional

7.3.7. Specify custom semantic rules

When the SQL code contains a call to functions which are unknown to Raincode compilers (e.g. user defined functions) or which are called with parameters of an unexpected type, it will be necessary to instruct the compiler about the resulting type in addition to any custom rewriting rule. This takes the form of an XML file passed to the compiler by a specific command-line option.

:SQLSemanticRulesFile=<filename.xml>

The root element must be <SemanticRules> and follow the syntax specified here.

Table 26. Syntax of the semantic rules file

Element name

Description

<SemanticRules>

A <SemanticRules> element groups a set of rules.

Content Model:

  • Exactly one <Functions> element

<Functions>

A <Functions> element groups rules related to SQL functions.

Content Model:

  • Zero or more <Function>.

<Function>

A <Function> element describes the rule of semantics for a function invocation. There can be several rules for the same function with different parameter types.

Attributes:

  • Name: The name of the function

Content Model:

  • Exactly one <Arguments> followed by one <ReturnType>.

<Arguments>

See <Arguments> in the table (Syntax of the rules rewriting file)

<ReturnType>

A <ReturnType> element describes the resulting type of a function invocation.

Content Model:

7.3.8. Script-based SQL transformations

In addition to predefined transformations to the SQL statements described in the sections above (for instance, when going from DB2 to SQLServer as the target database), the Raincode compilers also include a fully scriptable facility so that bespoke transformations can be integrated easily into the compilation process.

These transformations are expressed in the Raincode Scripting Language. While it is beyond the scope of this document to go through the intricacies of this language and its usage per se, a few essential elements are worth mentioning:

  • One or more procedures can be specified using the :RewriteSourceSQLProc and :RewriteSQLProc options, the former to alter the SQL statement before any built-in transformation, the latter to operate on the SQL statement after said built-in transformations.

  • These options must be given values of the form <module>.<procedure>, see Sample.

  • The procedures are called for each SQL statement encountered in the source file, and the node is replaced with the value returned by the procedures. As shown in the Sample, these procedures operate on the SQL statement at the structural level, instead of being limited to a purely text-based transformation.

  • If these procedures perform no modification at all, or modify the statement without synthesizing a new one, they must return the argument they had received.

  • Multiple rewriting procedures can be specified (by separating the procedure names by commas or using the command-line options repeatedly), in which case all these procedures will be called in sequence, each given the value returned by the previous one.

For instance, if the code below is stored in a file named TestRewriting.rcs, the procedure below can be applied by using a command-line option such as:

:RewriteSourceSQLProc=TestRewriting.RewriteDoubleExcl
Sample

It replaces the double exclamation point !! with a double bar ||.

MODULE TestRewriting;
PROCEDURE RewriteDoubleExcl (n);
  BEGIN
  FOR IN n.SubNodes | X IS SqlBinaryExpression DO
    IF X.Op = DoubleExclamation THEN
      X->Op := DoubleBar;
      END;
    END;
  RESULT := n;
  END;

7.3.9. Stand alone SQL Conversion

All SQL statements are not embedded in COBOL or PL/I programs. Some SQL statements are found in standalone scripts (Like JCL) or even built dynamically. Translation of such isolated SQL statements can be performed via Raincode Legacy Compiler.

Raincode Legacy compiler can take an input that contains SQL statements from the DB2 dialect and produce the version for SQLServer dialect. The result is printed on the console or can be saved in the files.

Input can be given as:

  • A file containing a single SQL statement.

  • If the file has multiple EXEC SQL blocks, each containing a single SQL statement. The SQL statements are enclosed between EXEC SQL and END-EXEC for COBOL programs and a semicolon for PL/I programs.

Below is an example code snippet for the COBOL and PL/I programs:

EXEC SQL
SELECT * FROM CHILDREN WHERE AGE = 10 FETCH FIRST 20 ROWS ONLY
END -EXEC
EXEC SQL
SELECT * FROM CHILDREN WHERE AGE = 11 FETCH FIRST 20 ROWS ONLY
END -EXEC
EXEC SQL
SELECT * FROM CHILDREN WHERE AGE = 12 FETCH FIRST 20 ROWS ONLY
END-EXEC

....

....

EXEC SQL
SELECT * FROM CHILDREN WHERE AGE = 10 FETCH FIRST 20 ROWS ONLY
;

EXEC SQL
SELECT * FROM CHILDREN WHERE AGE = 11 FETCH FIRST 20 ROWS ONLY
;

EXEC SQL
SELECT * FROM CHILDREN WHERE AGE = 12 FETCH FIRST 20 ROWS ONLY
;

....

....

By default converted SQL statement is output on the console if no option is selected.


C:\>cobrc :SQL=sqlserver :IgnoreClassification a.sql
1:a.sql

SELECT TOP(20)* FROM CHILDREN WHERE("AGE"=11)

To specify the output file for the converted SQL statement, use command-line options :Output and :OutputDir. If only the :OutputDir is specified, the output file name is appended by tran.sql extension to the radical of the input file name. For example, if the file name is script.sql then the output file name would be script.tran.sql.

Option :SQL=SqlServer is mandatory, if not mentioned, then SQL statements will not be translated

Use the command-line option :IgnoreClassification to disable the facility that determines the columns to be removed from the input when compiling. Otherwise, the compiler may warn that input is "unrecognized, could be a free from copybook".

The feature is developed for DML (Data Manipulation Language) statements. Some of them are:

  • SELECT

  • INSERT

  • UPDATE

  • DELETE

This feature can only process fully self-contained statements without host variables.

All the configurations and options applicable when converting SQL statements embedded into COBOL and PL/I program applies with the same behavior like type analysis and access to target schema (see section Access to a schema).

In a realistic setting some infrastructure is getting the SQL statement(s), storing them on a file, running the compiler with the appropriate option, getting the results from the output file and error handling etc.

To avoid the performance penalty incurred by running the compiler on atomic SQL statements, it is suggested to maintain a cache with past SQL statements and their converted counterparts. So that the same SQL statement can be used multiple times, avoiding the conversion of the same SQL statement again.

7.4. The virtues of static SQL

Programs can build SQL statements at runtime as character strings and send them to the database for execution, as shown in the COBOL sample below:

MOVE "INSERT INTO npeople_06(FirstName, LastName)
      VALUES ('ABC', 'XYZ')" TO WS-SQL.
EXEC SQL
 EXECUTE IMMEDIATE :WS-SQL
END-EXEC.

However, it is much more common for COBOL and PL/I programs to contain fully specified SQL statements enclosed between EXEC SQL and END-EXEC (for COBOL) or EXEC SQL and a semicolon (for PL/I), possibly referring to some host variable for parameters (:CUST-ID in the example below).

EXEC SQL
  DELETE FROM VCUST WHERE ID = :CUST-ID
END-EXEC.

Even when the SQL statements are expressed verbatim in the programs and thus fully known at compile-time the Raincode Legacy Compilers provide two mechanisms for their execution, namely static and dynamic SQL execution modes (see figure Raincode Legacy Compilers Mainframe SQL to .NET TSQL translation solution schematic):

image049
Figure 117. Raincode Legacy Compilers Mainframe SQL to .NET TSQL translation solution schematic

These modes are controlled by the compiler option GenerateStaticSQL and runtime option SQLExecutionMode. No change to the legacy source code is required.

7.4.1. Dynamic mode

In the dynamic execution mode, SQL statements are sent as character strings to the database during execution. This mode is simple to deploy, as it does not require any compile-time database interaction, but it incurs a performance penalty. Every SQL statement must be analyzed, validated and optimized whenever it is executed. It also increases the amount of data to transfer to the database since the full statement must be sent to the server for execution.

The dynamic execution mode may be preferred during development. It is more flexible, and top notch performance usually is not of paramount importance at this stage.

Except for security concerns (see section Security concerns), if the application is not performance critical or if its performance is not significantly impacted by the switch to static SQL, one may even stick to the dynamic mode when going to production for its simplicity.

7.4.2. Static mode

In the static execution mode, the SQL statements are sent to the database at hand at compile time to have an optimized query plan elaborated offline. Executing them amounts to just passing parameters and executing this query plan stored in the database. This mode is more involved than the dynamic execution mode, as described above. It requires a compile-time interaction with the database engine, but it also delivers better performance by not requiring the analysis and the elaboration of the query plan to happen at runtime. In practice, the static execution mode for SQL statements is handled differently depending on the target database at hand:

  • When targeting DB2 through HIS, the Raincode Legacy Compilers generate bind files that are then processed using the HIS utilities to elaborate the query plan on a given database instance.

  • When targeting DB2 through the IBM DB2 Connect or SQLServer, the compilers generate stored procedures that must be inserted into the target database instance. These stored procedures are never called explicitly by the developer. They are invoked under the hood by the runtime environment when the corresponding SQL statement is executed.

7.4.3. Security concerns

In addition to performance issues, there may be security reasons to favor the static execution mode over the dynamic execution mode. With static SQL, the database user impersonated by the running program needs only the authority to execute the stored procedures, while with dynamic SQL, the database user impersonated by the running program needs all accesses to the tables that are needed by the SQL queries to be performed. This may require giving dangerous rights to that database user. With static SQL, only the user creating the stored procedures (typically a database administrator) needs the potentially dangerous rights. Static SQL can thereby ease the implementation of specific security policies.

7.5. Setting up a SQL Server instance

When Raincode Legacy Compilers are used to target a SQL Server database, several stored procedures and user-defined functions have to be registered in the target database. These SQL artifacts are required to maintain the pieces of static SQL and to emulate some DB2 functions within SQL Server.

The Raincode Legacy Compilers come with a SQL script which defines all the required procedures and functions. This script has to be executed once before running a legacy program against the SQL Server run time. The procedures and functions have to be created in the schema where the data are stored; the script to be applied on the database is StoredProcedures_Raincode.sql, and it is typically run as

sqlcmd -i StoredProcedures_Raincode.sql

7.6. Miscellaneous code generation information

7.6.1. SQLCA structure

Embedded SQL statements use the SQL communication area (SQLCA) data structure to report run-time errors to applications. The definition of this structure can be included in the legacy source to access its fields. It can also be accessed from within any .NET language with the SqlCaWrapper class, which maps properties to each field of the structure.

7.6.2. Generate code for DB2HIS

Generating the code for DB2HIS [14] can be performed either by using synthetic cursors (that is, transforming SET statements on host variables and SELECT INTO statements declaring, opening, fetching and closing the cursor), or regular output parameters.

Generating code with synthetic cursors can be performed with the following command:

plirc :GenerateSyntheticCursors=TRUE :SQL=DB2HIS file.pl1

whereas generation without a synthetic cursor is performed by the following command:

plirc :SQL=DB2HIS file.pl1

7.6.3. Bind generated.db2.xml files with DB2 database

The bind file generated by the Raincode Legacy Compilers can be applied to the DB2 database using the default Microsoft tool.[15], which is Microsoft.HostIntegration.DataAccessLibrary.DataAccessControl.CreateCustomPackages, and is part of the Microsoft Host Integration Server dll.

7.6.4. Generate code for IBM DB2 Connect

Generating code for IBM DB2 Connect is performed by the following command:

plirc :SQL=DB2IBM file.pl1

7.6.5. Bind generated .sp.sql files with DB2 using IBM DB2 Connect

The bind file is a SQL script which should be applied to the database using the IBM DB2 connection string. The user should have the right to create and drop stored procedures. The .sp.sql file defines the stored procedures; it is run on the DBMS by the Database Administrator. For more details, see section The virtues of static SQL and Execute a program which connects to DB2 using IBM DB2 Connect.

7.6.6. Generate code for SQL Server based on DB2 source dialect

Generating code for SQL server [16] may require translation of SQL statements from DB2 syntax to TSQL (SQL Server). This involves knowledge of the SQL Server schema, which can be provided either with a connection string or a cache file.

Using the ODBC connection to the database, the command is as follows:


plirc :SQL=SQLServer :SQLServerConnectionString="DSN=MyDSN;
UId=login; Pwd=passwd" file.pl1

Using the .NET Framework Data Provider for SQL Server connection to the database, the command is as follows:


plirc :SQL=SQLServer :SQLServerConnectionString="Data Source=dbserver;
Initial Catalog=MyDatabase;UId=login; Pwd=passwd" file.pl1

A cache file can be provided to avoid connecting to the database:

plirc :SQL=SQLServer :SQLServerSchemaCacheFile=cache.xml file.pl1

When both the cache file and connection string are provided, the cache file is first checked, and if it is not available on the file system, the database connection is used instead, and the cache file is created with the database schema information. The :RetrieveSQLMetaData option can be used to enforce the cache file creation.

Retrieve schema cache file for SQL Server

There are two ways to retrieve a metadata cache file from SQL Server. If the metadata cache file cache.xml is not present on the file system, the following command will create it according to the SQL Server schema information,

plirc :SQL=SQLServer :SQLConnectionString="DSN=MyDSN;UId=login; Pwd=passwd" :SQLServerSchemaCacheFile=cache.xml file.pl1

otherwise, the :RetrieveSQLMetaData option can be used to enforce cache.xml update:

plirc :SQL=SQLServer :SQLConnectionString="DSN=MyDSN;UId=login; Pwd=passwd" :SQLServerSchemaCacheFile=cache.xml :RetrieveSQLMetaData=TRUE file.pl1

7.6.7. Generate code for SQL Server, based on SQL Server source dialect

When the SQL dialect [17] used in the legacy program’s source is compatible with TSQL, generating the code for SQL Server is performed using the following command:

plirc :SQL=SQLServer :SQLSourceDialect=SQLServer file.pl1

7.6.8. Bind generated .sp.sql files with SQL Server database

The bind file is a SQL script which should be applied to the database using the same connection string as the one provided during compilation. The user should have the right to create and drop stored procedures. The .sp.sql file defines the stored procedures; it is run on the DBMS by the Database Administrator (See sections The virtues of static SQL and Execute a program which connects to SQL Server).

7.6.9. Temporary tables support for SQL Server

SQL Server has no equivalent to a DB2 global temporary table in the SESSION database. We recommend creating an updatable view in the schema SESSION on SQL Server, backed by a table with an extra column to store the content of the @@SPID SQL Server variable. By providing the SQLServerCleanupProcedure parameter to the runner, the tables can be cleaned up on the session end.

7.6.10. Generate code for both SQL Server and DB2

It is possible to generate code for both DB2 and SQL Server databases (figure Raincode Legacy Compilers - Generate code for both SQL Server and DB2). In this case, the target database choice is performed at run time. The command is the following:

plirc :SQL=DB2 :SQL=SQLServer :SQLConnectionString= DSN=MyDSN;UId=login; Pwd=passwd  file.pl1
image050
Figure 118. Raincode Legacy Compilers - Generate code for both SQL Server and DB2

7.6.11. Generate code for Oracle, based on Oracle source dialect

When the SQL dialect [Note that, in this case, SQLCA values correspond to Oracle error codes] used in the legacy program’s source is compatible with Oracle, generating the code for Oracle is performed using the following command:

cobrc :SQL=ORACLE :SQLSourceDialect=ORACLE :SQLErrorCodeTarget=ORACLE file.cbl

8. Integrating legacy programs in a .NET environment

COBOL and PL/I programs compiled using the Raincode compilers can call each other transparently, but cannot call native .NET code nor being called by it as transparently, as the execution models are different. This section explains how to integrate legacy programs in your .NET ecosystem, so that your legacy code can interact gracefully with more modern developments.

8.1. 32 vs. 64-bit

Raincode Legacy Compilers supports the ability to produce .NET code in 32 or in 64 bits. However, this only refers to the .NET framework that it relates to, and the option used when building .NET assemblies. From a strict legacy program point of view; address spaces (See section AddressSpace) managed by Raincode Legacy Compilers execution contexts are still limited to 32 bits only. Going beyond this limit would invalidate all the mainframe code which implicitly or explicitly relies on the fact that pointers are stored on 32 bits only.

On the other hand, generating 64-bit code allows you to support a larger number of simultaneous threads (hence, Raincode Legacy Compilers execution contexts) within a single process, as it lifts the limit of 4 GB for the total of the allocated memory across all threads.

8.2. Interfacing .NET code and legacy programs

Raincode Legacy Compilers generated dlls are classes inheriting from the RainCodeLegacyRuntime.Module.Executable class (see section Module, Entry and Executable).

When the Raincode Legacy Runner rclrun starts a legacy program, it loads the program dll in memory (see section AssemblyLoading) and constructs its class. Similarly, when a program calls another program, it loads the dll of the latter if not already loaded and constructs its class.

Once a program dll is loaded, its class can be executed, invoking one of the Execute methods (see section Execution) exposed by RainCodeLegacyRuntime.Module.Executable.

All this happens behind the scene when a legacy application is started by the Raincode Legacy Runner rclrun. But it is also possible to mix legacy components with .NET components.

For example:

  • There is a need to write a custom runner in C#; in that case, the C# runner must be able to load and execute a legacy module, (see section Calling a legacy program from .NET code)

  • There is a need to write in C# a component which can be called from a legacy program; for a C# component to be callable from a legacy program, it must be a BaseModule (see section Calling .NET code from a legacy program); a C# component can in turn call a loaded legacy program

Data defined in the legacy programs AddressSpace can be passed to .NET components and vice-versa, (see section Passing parameters between legacy programs and .NET code)

For executable examples of C# code calling COBOL programs see Call Legacy and of COBOL programs calling C# code see Call Csharp

8.2.1. Example Interfacing .NET code and legacy programs

The following COBOL program will be used to show:

  • How to call .NET code from a legacy program

  • How to load and call a legacy program from .NET code

  • How to pass parameters between a legacy program and .NET code

   IDENTIFICATION DIVISION. 
   PROGRAM-ID.     HELPERS_GEN. 
   ENVIRONMENT DIVISION. 
   CONFIGURATION SECTION. 
   DATA DIVISION. 
   WORKING-STORAGE SECTION. 
   77 TMP PIC S9(8) USAGE IS DISPLAY 
   LINKAGE SECTION. 
   01 W-CNT PIC S9(8) USAGE IS PACKED-DECIMAL. 
   01 W-STRUCT. 
     10 W-MSG PIC X(20). 
     10 W-ARRAY OCCURS 10 TIMES. 
     15 W-NUMBER1 PIC S9(8) USAGE IS PACKED-DECIMAL. 
     15 W-SQUARE PIC S9(8) USAGE IS PACKED-DECIMAL. 
   01 W-MSG2 PIC X(20). 
   01 W-ARRAY2 OCCURS 15 TIMES. 
     10 W-NUMBER1 PIC S9(8) USAGE IS PACKED-DECIMAL. 
     10 W-SQUARE PIC S9(8) USAGE IS PACKED-DECIMAL. 
   01 W-ARRAY3 PIC S9(8) USAGE IS BINARY OCCURS 20 TIMES. 
   PROCEDURE DIVISION USING W-STRUCT W-MSG2 W-ARRAY2 W-ARRAY3 W-CNT. 
       MOVE W-CNT TO TMP 
       DISPLAY "COBOL " W-MSG2 " " TMP 
       CALL "HELP" USING W-STRUCT W-MSG2 W-ARRAY2 W-ARRAY3 W-CNT 
       GOBACK. 

8.2.2. Calling a legacy program from .NET code

Loading the Module

In a standard C# application, you can start using another .NET assembly’s code by adding a reference to the C# project. At runtime, the framework automatically loads the appropriate DLL (most of the time it will be loaded from the Global Assembly Cache (GAC) or from the same directory as your executable).

The Raincode Legacy Runtime has its own way of resolving assemblies, and does not rely on the automatic mechanism provided by the .NET framework. Therefore, the process is not the same when calling a legacy program. Instead, the runtime must be told where to look for the DLLs; this is done with:

AssemblyLoading.Loader.AddPath(ModuleDictionary.AssemblyCategory, "path_to_dlls");
ModuleDictionary.AssemblyCategory is the category the Raincode Legacy Runtime uses to load legacy assemblies. If you specify something else, the directory won’t be searched when loading a legacy assembly.
The rclrun arguments doing this is AddAssemblySearchDir.

Once the runtime knows where to look for the legacy assemblies, the assembly can be loaded using:

Executable executable = ModuleDictionary.FindExecutable(name);

This method will return null if the executable could not be found, or if the type Executable was not found inside the DLL (this can happen if the DLL is not a legacy assembly).

It is advisable to always check that the executable was found, either by checking manually, or by calling:

ModuleDictionary.EnsureModuleIsLoaded(name);

This will throw an exception if the module was not loaded.

Preparing the Context

The only component that is always required is the execution context (ExecutionContext). It must be instantiated before being able to run any program. It contains all the runtime information needed to properly run the program, like the AddressSpace (AddressSpace), the QIX runtime (see section CICS Support), the SQL runtime (see section SqlRuntime) , condition handling runtime (see section Exceptions) etc. It is used everywhere in the runtime; as such you will often see it as the first parameter in the API.

The easiest way is to use the default constructor:

var ec = new ExecutionContext();

Other overloads allow specification of the desired stack size, or even the size of the AddressSpace. However the default constructor should suit almost all needs.

Execution

The Executable class comes with the following execution method:

  • public void Execute(ExecutionContext ec, CallParameters parms): allows passing arguments and a return value; in case there are no parameters and return value, it can be called with the ExecutionContext only

Argument and return value passing is described in section Passing Parameters between legacy programs and .NET code

Assembly Mapping

If an assembly name is different than the executable name, ModuleDictionary must be told which assembly must be looked up; this is done with:

ModuleDictionary.MapAssembly("assemblyName.dll", doLoad);
Example

The following example shows how to call the HELPERS_GEN COBOL program once the execution context and the arguments have been created.

LoadExecutable("HELPERS_GEN").Execute(ctx, arguments);

Where LoadExecutable is a utility method, used throughout the samples listed in this section, and which ensures that a named executable is loaded, defined as:

private static Executable LoadExecutable(string name)
{
    // Optional: tell the Raincode runtime where to look for assemblies
    // AssemblyLoading.Loader.AddPath(ModuleDictionary.AssemblyCategory,
    //                                Path.Combine(Environment.CurrentDirectory,
    //                                @"..\out"));

    // The static ModuleDictionary class is responsible
    // for loading all legacy executables.
    var executable = ModuleDictionary.FindExecutable(name);

    // Throws an exception if the module could not be found
    ModuleDictionary.EnsureModuleIsLoaded(name);

    return executable;
}

For more examples see Getting Started document.

8.2.3. Calling .NET code from a legacy program

For a class T to be callable from a legacy program, it must inherit from BaseModule <T> and implement the method Run.

The BaseModule class has two constructors:

  • The default constructor has no arguments

  • The other constructor takes an integer argument representing the size of a static memory chunk to be allocated in the AddressSpace of the ExecutionContext passed to the Execute method.

The method Run has the following prototype:

protected override void Run(RainCodeLegacyRuntime.Core.ExecutionContext ec,
                            RainCodeLegacyRuntime.Core.MemoryArea workMem,
                            RainCodeLegacyRuntime.Module.CallParameters parms)

The parameters are:

  • ec: the ExecutionContext

  • workMem: the pointer to the static memory of the module allocated in the AddressSpace

  • parms: an object containing the parameter list.

The class T must also be annotated with the RaincodeExport attribute to make it identifiable by the module loader.

By default, the exported name is T, but it can also be explicitly specified, for example: [RaincodeExport("N")] specifies N as the exported name. When the class name is different than the exported name, the assembly containing the class has to be mapped explicitly (see section Assembly Mapping).

The following C# code is an example of a .NET module exporting name help which can be called by the CALL HELP statement of the COBOL program.


namespace RainCode.Samples
{
    using RainCodeLegacyRuntime.Core;
    using RainCodeLegacyRuntime.Descriptor;
    using RainCodeLegacyRuntime.Module;
    using RainCodeLegacyRuntime.Module.Attribute;
    using RainCodeLegacyRuntime.Types;
    using System;
    using Test;

    [RainCodeExport]
    class help : BaseModule<help>
    {
        protected override void Run(ExecutionContext ctx,
                                    MemoryArea staticMem,
                                    CallParameters parms)
        {
           // do something useful
        }
    }
}

For more examples see Getting Started document.

8.2.4. Passing parameters between legacy programs and .NET code

Legacy parameters or return value are MemoryAreas (MemoryArea).

As the direct manipulation of MemoryAreas is cumbersome, the Raincode Legacy Compilers can generate Helper classes which are C# classes allowing to:

Helper classes

Helper classes are generated C# classes that abstract away the representation details of COBOL and PL/I composite data structure, including mainframe-compatible data types, offsets and more. Having these helper classes generated by the compiler ensures that the offsets and other information they rely on is up to date at all time. See section (Repository) for the command-line options that control the generation of these helper classes

image070
Figure 119. Raincode Legacy Compilers - Helper Structures

To request Raincode Legacy Compilers to generate a helper class, use both of the following options:

  • :HelperStructure=st which describes the PL/I or COBOL structure that is the source for the generation of the helper class

  • :HelperClassName=cl which provides the generated HelperClass name.

For more details on helpers structures, see command line options

Raincode Legacy Compilers will look for a variable named st in the program at hand, and will generate a C# helper class named after cl. This helper class encapsulates the memory area, and contains setter and getters to access the various fields of the legacy program’s structure using native C# types, so that offsets and field sizes don’t have to be computed manually any longer. Instead, you rely on Raincode Legacy Compilers to compute accurate values for these attributes.

When the helper class generation is enabled, Raincode Legacy Compilers only generates the helper class; it does not actually compile the module at hand.

All numeric types are supported (they are expected to fit in a C# Decimal), as well as character string types. Bit strings made of a single bit are mapped to .NET bool.

The compiler command line arguments for generating helper classes for the parameters of the COBOL example are:

cobrc :HelperStructure=W-STRUCT :HelperClassName=Test.WSTRUCT  HELPERS_GEN.COB
cobrc :HelperStructure=W-MSG2 :HelperClassName=Test.WMSG2      HELPERS_GEN.COB
cobrc :HelperStructure=W-ARRAY2 :HelperClassName=Test.WARRAY2  HELPERS_GEN.COB
cobrc :HelperStructure=W-ARRAY3 :HelperClassName=Test.WARRAY3  HELPERS_GEN.COB
cobrc :HelperStructure=W-CNT :HelperClassName=Test.WCNT        HELPERS_GEN.COB

Each of these commands will generate a C# class in source form that must then be compiled using the C# compiler.

Parameter passing using helper classes
CallParameters

Parameters and return value are passed as a CallParameters object. (For more details refer section CallParameters)

For PL/I, it is strongly recommended to read CallingConvention and DescriptorFormat)

Example of CallParameters creation for COBOL:

The parameter list can be created using:

var parms = CallParameters.StackAllocateParameterList(ctx, 5);

where 5 is the arity of the called program, i.e. the number of parameters expected by the called program. The arity can be retrieved using attribute parms.Arity.

The method for setting parameter values out of a helper is SetParameter:

parms.SetParameter(ctx, n, helper)

where n is the parameter number ranging from 0 to parms.Arity – 1 and helper is the helper.

The method for retrieving parameter MemoryArea is GetParameterAddress:

parms.GetParameterAddress(ctx, n)

where n is the parameter number.

Retrieve parameter values

The following example shows how to access parameters from a .NET module. A helper class is created for every parameter to be accessed, e.g.:

Test.WCNT wcnt = new Test.WCNT(ctx, parms.GetParameterAddress(ec, 4));

Helper classes allow accessing scalar parameters and structure fields as a properties, e.g.: wcnt.W_CNT is an integer, wmsg2.W_MSG2 is a string. Array sizes are accessed through properties, e.g.: warray2.W_ARRAY2_Size, and array elements are accessed through getter functions, e.g.: warray3.GetW_ARRAY3_At(i). If an array element is a structure, its fields can in turn be accessed through properties, e.g.: warray2.GetW_ARRAY2_At(i).W_NUMBER1.

[RaincodeExport]
class help : BaseModule<help>
{
    protected override void Run(ExecutionContext ctx,
                                MemoryArea staticMem,
                                CallParameters parms)
    {
        Test.WSTRUCT wstruct = new Test.WSTRUCT(ctx, parms.GetParameterAddress(ctx, 0));
        Test.WMSG2 wmsg2 = new Test.WMSG2(ctx, parms.GetParameterAddress(ctx, 1));
        Test.WARRAY2 warray2 = new Test.WARRAY2(ctx, parms.GetParameterAddress(ctx, 2));
        Test.WARRAY3 warray3 = new Test.WARRAY3(ctx, parms.GetParameterAddress(ctx, 3));
        Test.WCNT wcnt = new Test.WCNT(ctx, parms.GetParameterAddress(ctx, 4));
        Console.WriteLine("C# again " + wmsg2.W_MSG2 + " " + wcnt.W_CNT);
        for (int i = 0; i < warray2.W_ARRAY2_Size; i++)
        {
            Console.Write(" " + warray2.GetW_ARRAY2_At(i).W_NUMBER1 +
                          ":" + warray2.GetW_ARRAY2_At(i).W_SQUARE);
        }
        Console.WriteLine();
        for (int i = 0; i < warray3.W_ARRAY3_Size; i++)
        {
            Console.Write(" " + warray3.GetW_ARRAY3_At(i));
        }
        Console.WriteLine();

        helpers.feedback(ctx, parms);
    }
}
Allocate parameter space and set values

The following example shows how to create and set parameters from a .NET module.

First, the parameters must be allocated in the memory of the legacy execution context. The helper classes support utility functions named StackAllocate and Allocate to perform this allocation.

The difference between StackAllocate and Allocate is:

  • With StackAllocate, the parameter memory is automatically recovered upon return from the legacy module;

  • With Allocate, the parameter memory is either automatically recovered when the execution context no longer exists, or when the helper function Deallocate is called.

Once the parameters are allocated, they can be set. Properties are used for scalars, e.g.: wcnt.W_CNT = 42, and also for structure fields, e.g.: wstruct.W_MSG = "Hello from C#".

Array elements are set through setter functions e.g.: warray3.SetW_ARRAY3_At(1, 0).

If an array element is a structure, its fields can in turn be set through properties, e.g.: warray2.GetW_ARRAY2_At(i).W_NUMBER1 = i.

A helper class also provides a Descriptor property which is an ITypeDescriptor (see ITypeDescriptor) describing the legacy type, and a ToTypedLValue method retrieving the parameter TypedLValue(see TypedLValue) from the helper.

Finally, the list of parameters is built to pass to the module execution function using utility methods StackAllocateParameterList or AllocateParameterList to perform this allocation, e.g.:

var parms=CallParameters.StackAllocateParameterList(ctx,n)

where ctx is the ExecutionContext and n is the Arity.

The difference between StackAllocateParameterList and AllocateParameterList is:

  • With StackAllocateParameterList, the parameter memory is automatically recovered upon return from the legacy module;

  • With AllocateParameterList, the parameter memory is either automatically recovered when the execution context no longer exists, or when the helper function Deallocate is called.

Example:

    using (wstruct = WSTRUCT.Allocate(ctx))
    using (wmsg2 =  WMSG2.Allocate(ctx))
    using (warray2 = WARRAY2.Allocate(ctx))
    using (warray3 = WARRAY3.Allocate(ctx))
    using (wcnt = WCNT.Allocate(ctx))
    // At this point, the helper structure are allocated
    // in the AddressSpace attached to the ctx ExecutionContext
    {
        // The helper structures are populated using the convenience
        // properties generated by the compiler.
        wstruct.W_MSG = "Hello from C#";
        for (int i = 0; i < wstruct.W_ARRAY1.Count; i++)
        {
           wstruct.W_ARRAY1[i] = 2 * i + 1;
        }
        for (int i = 0; i < warray2.W_ARRAY2_Size; i++)
        {
            warray2.GetW_ARRAY2_At(i).W_NUMBER1 = i;
            warray2.GetW_ARRAY2_At(i).W_SQUARE = i * i;
        }
        wmsg2.W_MSG2 = "Hello from C#";
        warray3.SetW_ARRAY3_At(1, 0);
        warray3.SetW_ARRAY3_At(1, 1);
        for (int i = 2; i < warray3.W_ARRAY3_Size; i++)
        {
            warray3.SetW_ARRAY3_At(warray3.GetW_ARRAY3_At(i - 2) +
                                   warray3.GetW_ARRAY3_At(i - 1), i);
        }
        wcnt.W_CNT = 42;
        // And finally, using the utility function LoadExecutable,
        // the compiled COBOL program is loaded, started with the
        // appropriate set of parameters
        var parms = CallParameters.StackAllocateParameterList(ctx, 5);
        parms.SetParameter(ctx, 0, wstruct);
        parms.SetParameter(ctx, 1, wmsg2);
        parms.SetParameter(ctx, 2, warray2);
        parms.SetParameter(ctx, 3, warray3);
        parms.SetParameter(ctx, 4, wcnt);
        LoadExecutable("HELPERS_GEN").Execute(ctx, parms);
   }

8.2.5. Populating from a JSON string

Helper classes can also be populated from a JSON string using the PopulateFromJSON method. This method behaves identically to JSON PARSE in COBOL, filling in the data structure described in the JSON. For more details on the exact behavior of JSON PARSE, we refer to the COBOL manual.

For example, a (partial) population of WSTRUCT code in the section Allocate parameter space and set values is as follows:

int status := wstruct.PopulateFromJSON(
“{ “ +
“  W_MSG : \“Hello from JSON\” , “ +
“  W_ARRAY : [ ” +
“    { W-NUMBER1 : 0 , W-SQUARE : 0 } , “ +
“    { W-NUMBER1 : 1 , W-SQUARE : 1 } , “ +
“    { W-NUMBER1 : 2 , W-SQUARE : 4 } , “ +
“ ] } ”
);

The PopulateFromJSON method returns a status code that is identical to the JSON-STATUS special register of COBOL. In this example, the status code will be eight because W_ARRAY will not be completely populated. Also, the PopulateFromJSON method will throw an exception if the operation was unsuccessful, and the exception code will be the code from the JSON-CODE special register.

8.3. Plugins

The RainCodeLegacyRuntime offers multiple customization points. To load a plugin at runtime, use the -Plugin argument (see rclrun command-line options)

8.3.1. ExecutionContext ConstructorPlugin

The ExecutionContext.ConstructorPlugin allows to create an instance of a subclass of the ExecutionContext (See ExecutionContext) with the following parameter:

  • ExecutionContextArgs: class containing all the arguments required to initialize an ExecutionContext

For an example showing the process of creating plugins, see Plugins Sample.

8.3.2. ExecutionContext initialization

If your custom ExecutionContext requires any initialization work after the base ExecutionContext has been initialized, the ExecutionContext.PreparePlugin can be used with the following arguments:

ExecutionContext: instance of the ExecutionContext to initialize.

If a ExecutionContext.ConstructorPlugin is provided, the instance will have the type returned by that plugin.

string: name of the main entry point

For an example showing the process of creating plugins, see Plugins Sample.

8.3.3. SQL Runtime

The SqlRuntime.ConstructorPlugin allows to create an instance of a subclass of SqlRuntime with the following parameters:

  • ExecutionContext: the current ExecutionContext

  • SqlArgs: contains all the parameters necessary to construct a SqlRuntime (connection string, DBMS)

The SqlArgs.Dbms property can have the following values:

  • SqlServer

  • DB2

  • DB2HIS

  • Oracle

The actual value depends on the arguments given to RCLRUN (see section rclrun command-line options)

For an example showing the process of creating plugins, see Plugins Sample.

8.3.4. Qix Factory

The QixFactory.ConstructorPlugin allows to create an instance of a subclass of QixFactory with the following parameters:

  • IQixContext: the equivalent of an execution context specialized for QIX operations

For an example showing the process of creating plugins, see Plugins Sample.

8.3.5. BaseDumpProcessor

When a program crashes, it is possible to obtain a report with the following information:

  • .NET exception that stopped the execution COBOL stack trace, with file and line number

  • the list of variables in all loaded modules and their value

The exact information available will depend on three compilation flags:

  • TrackPrograms: record the necessary information to generate a report

  • TrackStatements: record the necessary information to get the file and the line numbers in the report

  • Debug: generates the debug information required to get the list of variables and their value

As shown in the Example Report, these three compilation flags are the key to generating the dump information.

For a full dump with all the available information, programs must be compiled with -TrackPrograms, -TrackStatements, -Debug.
Example Report

A truncated example of the default report generated when all three flags are activated.

[ERROR] [ABENDDUMP]: RainCodeLegacyRuntime.Exceptions.RuntimeException: negative memory area size: -11821390
...
[ERROR] [ABENDDUMP]: Program threw exception in statement other.cob:19
[ERROR] [ABENDDUMP]: Call stack:
[ERROR] [ABENDDUMP]: 0: module OTHER OTHER.COB other.cob:19
[ERROR] [ABENDDUMP]: 1: module CRASH CRASH.COB crash.cob:25
[ERROR] [ABENDDUMP]: Run unit 0
[ERROR] [ABENDDUMP]: Module CRASH:
[ERROR] [ABENDDUMP]: RETURN-CODE:PIC S9(4) USAGE COMP:9000:0x2328
[ERROR] [ABENDDUMP]: SORT-RETURN:PIC S9(4) USAGE COMP:0000:0x0000
...
[ERROR] [ABENDDUMP]: Module OTHER:
[ERROR] [ABENDDUMP]: RETURN-CODE:PIC S9(4) USAGE COMP:9000:0x2328
[ERROR] [ABENDDUMP]: SORT-RETURN:PIC S9(4) USAGE COMP:0000:0x0000
...
[ERROR] [ABENDDUMP]: Module OTHER0:
[ERROR] [ABENDDUMP]: RETURN-CODE:PIC S9(4) USAGE COMP:9000:0x2328
[ERROR] [ABENDDUMP]: SORT-RETURN:PIC S9(4) USAGE COMP:0000:0x0000
...

Suppose you want to create a customized crash/abend information in XML, JSON or any other format. In that case, you need to implement the user-defined dump processor plugin and register it in the BaseDumpProcessor.ConstructPlugin.

A custom formatter must inherit from the BaseDumpProcessor classes:


public abstract class BaseDumpProcessor: IDisposable
{
    public static Plugin<ExecutionContext, BaseDumpProcessor> ConstructorPlugin = [...];

    public virtual void Start() {}
    public virtual void End() {}
    public virtual void Exception(Exception e) {}
    public virtual void CurrentStatement(string position) {}
    public virtual void StartCallStack(int stackSize) {}
    public virtual void CallStackEntry(Module.Module module, string position) {}
    public virtual void EndCallStack() {}
    public virtual void StartRunUnit(int moduleCount) {}
    public virtual void EndRunUnit() {}
    public virtual void StartModule(Module.Module module) {}
    public virtual void EndModule() {}
    public virtual void InvalidValue(InvalidValueData invalidValue) {}
    public virtual void Scalar(ScalarData scalar) {}
    public virtual void Level88(ScalarData scalar) {}
    public virtual void StartArray(ArrayData array) {}
    public virtual void EndArray() {}
    public virtual void StartStructure(StructureData structure) {}
    public virtual void EndStructure() {}
    protected virtual void Dispose(bool disposing) {}
}

The call sequence is:

  • Start

    • Exception

    • CurrentStatement

    • StartCallStack

      • CallStackEntry (repeated for each COBOL module on the call stack)

    • EndCallStack

    • StartRunUnit (repeated for each active run unit)

      • StartModule (repeated for each loaded module)

        • InvalidValue-Scalar-Level88-StartArray/EndArray-StartStructure/EndStructure (repeated for each variable)

        • EndModule

      • EndRunUnits

  • End

  • Dispose(true)

For a sample implementing a simple XML version of the report, see BaseDumpProcessor.

Currenltly available in COBOL.

8.3.6. Caching SQL Tables

For details on how to use a plugin for caching SQL tables, refer to section Processing singleton returning statements

8.4. Interceptors

Controlled by compiler command-line option:AllowCallInterception, the Raincode compilers allow for user-defined interceptors. They are objects that can be attached to programs (COBOL and PL/I alike) and which will be given control to invoke these programs, so that these invocations can be altered by user-written code.

By default, in absence of a defined interceptor, the program is invoked without further ado. However one can intercept all calls by defining such an interceptor.

One of the possible uses (and the one that triggered this extension to the Raincode Compilers) is to , extract input parameters, check in the cache for a matching past invocation with the same input parameter, and return the cached value if available, without calling the program at all.

8.4.1. Interceptor Definition

An interceptor is a property of Executable implementing the interface CallIntercept:

  public interface CallIntercept
  {
        void Intercept(Executable.Intercepted c);
  }

Where the struct Intercepted gives access to e.g. the executable execution context and arguments.

Example interceptor class:


    class Interceptor : CallIntercept
    {
        public void Intercept(Executable.Intercepted c)
        {
            Do something useful using c.Ec and c.Args.
        }
    }

8.4.2. Interceptor Initialization

An interceptor is initialized by instantiating the interceptor class and setting the executable property.

private void InitInterceptor(Executable Exec)
{
  Exec.Interceptor = new Interceptor();
}

The initialization method may be invoked for the first time the Executable is loaded in memory when the application is started as follows:

ModuleDictionary.CustomExecutableLoadedEvent += InitInterceptor;

8.4.3. Interceptor Example

The following interceptor below implements a cache for a COBOL program with the following assumptions:

  • It has one integer input parameter, e.g. a PIC S9(8) USAGE COMP.

  • It has one output parameter with a length 4

  • It has a functional behavior, i.e. for any input value, it always returns the same output value

  • The interceptor checks if the input value is already present as a key in a cache dictionary. If yes, it returns the corresponding value, if not, it executes the intercepted COBOL program, stores the result in the dictionary and returns it.

public class CacheResultsInterceptor : CallIntercept
{
      private Dictionary<int, byte[]> cache = new Dictionary<int, byte[]>();

       public void Intercept(Executable.Intercepted c)
       {
            int key = c.Args.GetParameterAddress(c.Ec, 0).LoadMainframeInt32();
            MemoryArea result = c.Args.GetParameterAddress(c.Ec, 1).Substr(0, 4);
            if (cache.TryGetValue(key, out var value))
            {
                System.Console.WriteLine("found in cache {0}", key);
                MemoryArea.CopyFromBytes(value, result);
            }
            else
            {
                c.Execute();
                cache.Add(key, result.ToBytes());
            }
       }
}

8.5. SQL Table Caching


According to Wikipedia : In computing, a cache /kæʃ/ KASH,[18] is a hardware or software component that stores data so future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation, or the duplicate of data stored elsewhere.


Caches are not rocket science, and have been applied for years in many kinds of applications, mainframe-based or not. When the problem at hand is the cost of I/O (and more to the point, the cost of distributed I/O with its attached latencies), keeping copies of the data closer to the application can have a huge impact on performance.

Despite their usefulness, caches are no silver bullets, and raise issues of their own :

  • Cache invalidation must be addressed (because slow moving data does not equate to non moving data). The logic by which cached data is no longer valid and must be flushed, is environment and application-specific. It can be controlled by time (assuming that data that has not been refreshed for a given of time should no longer be considered accurate), by triggers in a database or by the application logic.

  • Even more importantly, when embedded in the application, the caching logic increases its complexity, makes it more complex and more fragile, and it is barely a solution if it is to be applied accross the board on any sizable porfolio.

The Raincode compilers support caching without requiring any significant change to the application. Caching operates mostly at the infrastructure level, leaving the application code untouched. This allows running the same code on and off the mainframe, with the caches active in the rehosted version of the application.

8.5.1. Enablement rather than a complete solution

The schemes presented here enable caching, but do not constitute complete working solutions.

They provide information that can control whether one can cache, and if so, what should be cached, but do not include actual implementations for the cache itself, because it may have to be designed very differently depending on the case at hand:

  • It can be as short-lived as the execution of a given program, in which case the cache is just a dictionary attached to the current thread

  • It can be shared across threads, in a more permanent way, using a third-party product

  • It may be based on a faster, more local database engine.

  • Etc.

In fact, it is unlikely that any generic implementation provided out of the box, no matter how smart and optimized, would be adequate for any given case, which is why the various facilities described in this document focus on the identification of cases where caches can be used, but the implementation of the said caches (together with its invalidation policies, etc.) is left to the user.

On the other hand, having this cache implemented at the infrastructure rather than application level allows one to use C#, which is obviously more suitable an implementation language for such technical matter than COBOL or PL/I would be, if only because of the availability of sophisticated hashing-based dictionary classes in the runtime library.

8.5.2. SQL and singletons

The Raincode compilers recognize specific SQL statements as candidates for caching and allows for the user to intercept such calls and implement a caching mechanism at the infrastructure level, without altering the existing source code.

In practice, this capability is based on the compile-time recognition of singleton-returning SQL statements (as to require no possibly time-consuming runtime analysis of the SQL statements). Such statements are typical of excessively chatty applications and are good candidates for caching. They can be qualified as follows:

  • They are stand-alone SELECT statements with an INTO clause to fetch data, as opposed to opening cursors and fetching data from them.

  • They select one or more plain columns from a single table, and perform no joins nor any other computation on the database side before returning data.

  • They come with a WHERE clause structured as a conjunction of equalities, each comparing a column to a host variable.

They are thus typical statements that fetch a single record based on its assumed primary key, and are typical of chatty applications, where such data could often be fetched as part of a larger SQL statement using joins.

For instance, the following statement is recognized as singleton-returning:

SELECT
  FNAME, LNAME, AGE, ADDR
INTO
  :FNAME, :LNAME, :AGE, :ADDR
FROM CUST
WHERE
  DEPTD_ID = :DEPTID AND
  CUST_ID = :CUSTID

while the following are not:

Defining a cursor

DECLARE CURSOR MCURS AS
SELECT
  FNAME, LNAME, AGE, ADDR
FROM CUST
WHERE
  DEPTD_ID = :DEPTID AND
  CUST_ID = :CUSTID

Performing a computation on the data before fetching it

SELECT
  FNAME, LNAME, ABS(AGE), ADDR
FROM CUST
WHERE
  DEPTD_ID = :DEPTID AND
  CUST_ID = :CUSTID

Comparing to constants in the WHERE clause

SELECT
  FNAME, LNAME, AGE, ADDR
FROM CUST
WHERE
  DEPTD_ID = 77 AND
  CUST_ID = :CUSTID

Singleton-returning statements are recognized as potential targets for caching, as they are expected to return a single row from a table based on what must then be a primary key (the details about a currency based on some currency code, for instance). These statements can thus be implemented by querying a record tuple from a cache with the same primary key, rather than sending the request to the database server.

Identifying such singletons-returning queries is only the first step: depending on the table they access, caching may or may not be applicable, based on user-defined logic as defined below.

Processing singleton returning statements

Caching can be achieved by creating a plugin which, when registered, sets the SqlRuntime delegate ExecuteCommandInterceptor:

public delegate SqlCode ExecuteCommandDelegate(Command Command);
public ExecuteCommandDelegate ExecuteCommandInterceptor = (Command Command) => Command.Execute();

The behavior is expected to be as follows:

  • If the needed row is in the cache, then copy it into the host variables, else call Command.Execute, and populate the cache from the host variables.

  • Determining if a query is a candidate for caching is achieved by calling the GetSingletonTable method as defined for the SqlImmediateCommand class. This method just queries an attribute, the analysis required to check whether a given SQL command returns a singleton is entirely performed at compile time.

  • If it is a singleton returning statement, query the table it accesses and decide, based on an application-level criteria, that said table is a good candidate for caching (quite obviously, because of size and/or cache invalidation, all tables are not necessarily good candidates for caching).

  • The SqlImmediateCommand method GetSingletonInputVariables then gives you access to the column names (in the right sequence) that are used in the corresponding WHERE clause, to allow you to distinguish

SELECT
  FNAME, LNAME, AGE, ADDR
INTO
  :FNAME, :LNAME, :AGE, :ADDR
FROM CUST
WHERE
  DEPTD_ID = :DEPTID AND
  CUST_ID = :CUSTID

from

SELECT
  FNAME, LNAME, AGE, ADDR
INTO
  :FNAME, :LNAME, :AGE, :ADDR
FROM CUST
WHERE
  CUST_ID = :CUSTID AND
  DEPTD_ID = :DEPTID
  • If these column names (even if in a different order) match the key as designed for the caching of the attached table, you can then query the cache, take the tuple it returns and fetch data according to the output column names – using , SqlImmediateCommand method GetSingletonOutputVariables to allow you to support:


SELECT
  FNAME, LNAME, AGE, ADDR
INTO
  :FNAME, :LNAME, :AGE, :ADDR
FROM CUST
WHERE
  DEPTD_ID = :DEPTID AND
  CUST_ID = :CUSTID

as well as

SELECT
  AGE, LNAME
INTO
  :AGE, :LNAME
FROM CUST
WHERE
  DEPTD_ID = :DEPTID AND
  CUST_ID = :CUSTID
  • If any of these conditions is not fulfilled, one must revert to the default behavior, and query the database [19].

This logic is summarized in the flowchart below

flowchart singleton returning
Figure 120. Flowchart Singleton returning statements

For an example showing the process of processing singleton returning statements, see SQL Table Caching Sample.

8.5.3. Using the repository for inventories

The repository populated at compile-time with information extracted from the source file indicates whether any given query has been recognized as returning a singleton or not. You may thus query your repository database to evaluate the relevance of this technique to your environment before embarking in the implementation of such caches.

Sql singleton
Figure 121. SQL-Singleton Inventories

8.5.4. Externalized logic

This is very much a do-it-yourself mechanism. It does not implement the cache for you, nor does it decide for which of the tables such a cache would prove valuable, but it allows you to intercept SQL statements which would typically benefit from such a cache and allow you to act accordingly. It does so without requiring you to change your application at the COBOL or SQL level. It only requires you to identify the tables that would benefit from caching, and the mechanism will be enabled, even if it involves hundreds of such singleton-returning SQL statements scattered over millions of lines of code.

8.5.5. Low hanging fruits

The mechanism presented here only works for such singleton-returning queries, as the corresponding cache system can be implemented easily using simple data stores implementing associative tables. In contrast, more complex conditions than conjunctions of equalities or joins would require non-trivial analysis at runtime to reproduce the expected behavior.

9. Raincode Legacy Compilers internals

This section offers an insight into how Raincode Legacy Compilers actually works to make your .NET migration a success.

9.1. Compilation process: code generation, assembly and verification

The Raincode Legacy Compilers compilation process is divided in several phases:

For PL/I programs:

  • The source code is first preprocessed to expand INCLUDE statements; all INCLUDE statements in EXEC SQL/EXEC CICS are expanded.

  • The resulting PL/I program is then read, analyzed and type checked, and the EXEC SQL/CICS statements are also analyzed and checked.

For COBOL programs:

  • The source code is first preprocessed to expand COPY/REPLACING statements; all INCLUDE statements in EXEC SQL/EXEC CICS are expanded.

  • The resulting COBOL program is then read, analyzed and fields checked, the EXEC SQL/CICS statements are also analyzed and checked.

The following apply to all Raincode Legacy Compilers supported legacy programs:

  • Based on the above, an IL assembler source file is generated

  • The standard assembler ILASM is run on the assembler source file to produce a DLL or an EXE file

  • Unless verification is disabled, the resulting executable or dynamic link library is checked for consistency using the standard PEVERIFY utility.

9.2. PL/I pre-processing

Preprocessing is the process by which files are transformed into PL/I programs that can be directly handled by the compiler. The content of the files passed to the compiler is handled by the main preprocessing pipeline, composed of an arbitrary number of steps. Some of these steps may trigger inclusion of other files. In that case, a secondary pipeline associated to that particular main pipeline step will handle the included files (and all files recursively included under control of that main step).

9.2.1. Configuration

Preprocessing pipelines are configured with the :AddPreprocessor command-line option. If the option is not used in a compiler invocation, it takes its defaults of "&SQL,MACRO,OUT(EXT(.w))".

If the option is used one or more times, each use, in command-line order, will add one or more steps to the pipelines. The value of the :AddPreprocessor option is a comma-separated list of step configurations. Each step configuration is formed of an optional scope indicator, step type and optional parenthesized step-specific configuration. The step-specific configuration is a comma-separated list of key-value pairs where the parenthesized value immediately follows the key.

Without a scope indicator, the step is simply added to the end of the main pipeline. With the !; indicator, the step is added to the end of the include pipeline. With the &; indicator, the step is added to the end of both the main and include pipeline. The include pipeline associated to a main step is the include pipeline as it was defined when the main step was added.

9.2.2. Pre-processors

The following pre-processors are included with Raincode Legacy Compilers.

Include pre-processor

The step type is INC. It has one parameter with key ID. Each line in the input program starting with the value of the ID parameter is interpreted as a directive to include the file named on the remainder of the line or up to the first semicolon, whichever comes first. This processing occurs recursively for included files.

Output pre-processor

The step type is OUT. It has one parameter with key EXT. Unless the :KeepPreProFiles command-line option is given, this step does nothing. If it is given, this step doesn’t modify the program but makes a copy of it in the output directory with the extension given by the value of the EXT parameter.

SQL pre-processor

The step type is SQL. It has no parameters. This step expands EXEC SQL INCLUDE statements and handles SQL constructs in which scoping is different from PL/I scoping. As the compiler itself is handling SQL statements, this step doesn’t remove most EXEC SQL statements and further pre-processing steps must therefore be careful to keep them correct.

Macro pre-processor

The step type is MACRO. It has no parameters. This step does the standard PL/I macro processing.

External pre-processor

The step type is EXT. It has one parameter with key CMD. This steps writes the current program text to disk, with %LINE directives, execute an external tool then reads the new program text, interpreting %LINE directives. The external tool is invoked using a command-line given by the CMD parameter where {in} is replaced by the file name produced by the compiler and {out} is replaced by the file name that the external program is expected to produce.

9.3. Essential classes and structures

This section describes the basic classes and structures found in the legacy program run time that must be used whenever you want to integrate the output of Raincode Legacy Compilers with your system.

9.3.1. AddressSpace

RainCodeLegacyRuntime.Core.AddressSpace is a class that encapsulates a byte array, and which represents the address space in which a legacy program can execute. The encapsulated byte array is never referred to directly, to allow for reallocation if the address space gets too small to accommodate a growing number of dynamic allocations.

9.3.2. MemoryArea

RaincCodeLegacyRuntime.Core.MemoryArea structures are used as an abstraction for pointers, referring to a specific offset in an address space, together with an integer length. For instance, a PL/I CHAR () variable is denoted by a MemoryArea without requiring any additional data item, as its length is fully determined. MemoryArea structures, as opposed to classes, to avoid the overhead of dynamic allocation for a data type, are used pervasively in virtually all aspects of Raincode Legacy Compilers. The methods available for memory areas cover issues such as:

  • Copying from one memory area to another

  • Setting individual bytes in the memory area

  • Extracting a substring of a memory area

  • Setting or getting the memory area with conversions from and to a number of native data types.[20] (integers with different endiannesses, floating point, etc.).

9.3.3. BitString

RainCodeLegacyRuntime.Types.BitString structures denote bit strings. They include a memory area as described above, together with a bit offset and a bit length, to indicate where the bit string starts in the MemoryArea and how many useful bits there are. This allows for the convenient representation of a statement such as:

SUBSTR(A,7,13)=B;

where the substring on the left side of the assignment is compiled into a BitString expression. BitString provides methods to:

  • Copy a bit string in another

  • Test any bit of the bit string

  • Set or clear any bit of the bit string.

9.3.4. ITypeDescriptor

ITypeDescriptor is the common interface for a scaffolding of classes that describe types at runtime. When combined with a MemoryArea (See MemoryArea) in a TypedLValue (See TypedLValue), an ITypeDescriptor can be used to dynamically type a memory area.

Type descriptors are managed by static dictionaries, so that the same descriptor is never allocated twice. Type equivalence, when expressed in terms of the corresponding type descriptors, can be asserted by reference equality. In other words, to get the descriptor for static character strings for a given number of characters, one does not create a new instance of the StaticCharacterStringType class, but rather, calls the static StaticCharacterStringType.Get(int) function, which manages the dictionary and ensures that the descriptor for the static character string for any given length is only allocated once process-wide, even if multiple ExecutionContext (See ExecutionContext) are active simultaneously in separate threads.

The functions to use to get descriptors for specific types are listed in the table below:

Function Type
StaticCharacterStringType.Get(int len)

Static character string

VaryingCharacterStringType.Get(int len)

PL/I varying character strings

PictureTypeDescriptor.Get(string pic)

COBOL display usage numeric values, based on the expanded representation of the picture clause (where 9(3) is represented as 999)

FixedBinaryDescriptor.Get(int digits, int scale)

Fixed binary values, with total number of digits and a scale (0 for integer values)

FixedDecimalDescriptor.Get( int digits, int scale, Storage  store)

Fixed decimal values, where the Storage enumeration defines the various physical properties modes supported for this type:

  • BCD_PLI

  • COMP3

  • COMP3_UNSIGNED

  • COMP

  • COMP_UNSIGNED

  • COMP5

  • COMP5_UNSIGNED

  • COMP_NOTRUNC

  • COMP_UNSIGNED_NOTRUNC

  • COMP5_NOTRUNC

  • COMP5_UNSIGNED_NOTRUNC

  • DISPLAY_UNSIGNED

  • DISPLAY_SIGNED_TRAILING

  • DISPLAY_SIGNED_LEADING

  • DISPLAY_SEP_SIGNED_TRAILING

  • DISPLAY_SEP_SIGNED_LEADING

PointerTypeDescriptor.Get

Pointers (There is only one pointer type. Hence, this function takes no parameter and always returns the same instance).

9.3.5. TypedLValue

RainCodeLegacyRuntime.Core.TypedLValue structures combine a BitString with a type descriptor, to produce a form of universal reference, annotated with a type descriptor. You can thus see a BitString as a primitive form of generalization of memory areas (as they also include a bit offset and length); and the TypedLValue as a further generalization that adds a type descriptor.

image052
Figure 122. Combine a BitString with a type descriptor to create a universal reference

9.3.6. ExecutionContext

The execution context (RainCodeLegacyRuntime.Core.ExecutionContext class, in .NET) represents the run-time environment, containing all the information required to actually run a compiled legacy program. It is available as an implicit parameter to all the functions that Raincode Legacy Compilers generate, and contains all the thread-specific information, including:

  • The address space which is thus not shared across multiple execution contexts

  • The QIX configuration

  • The SQL configuration

  • The IO subsystem configuration.

Separate threads having distinct execution contexts are guaranteed to run without interfering with each other (except for the Module and Entry table, see section RaincodeLegacyRuntimeQixContext).

Stack management

In addition to the standard .NET stack, the execution context maintains a stack for automatic variables.

In previous versions of the Raincode compiler runtime, this stack was allocated statically with a user-defined size (and a default value of 4 MB).

However, this scheme sometimes proved impractical, as one must care for large number of different transactions, and consequently, allocate a stack large enough to accommodate the transaction with the highest stack memory requirement, even if this transaction is used in a small fraction of the cases. This can be a suboptimal usage of resources.

To better care for this variation in stack usage, this static allocation has been replaced by a page-based mechanism.

stack 1

As shown on the diagram here above, the plain memory stack is replaced by a statically allocated stack of 128 pointers to pages, where allocations are then performed trivially. The pages have some available memory, referred to as slack (in grey in the diagram).

From the client code’s perspective, things are rather transparent. All it takes is a call to a utility function

public MemoryArea StackAllocate(int size)

which allocates size bytes on the stack, initializes them to 0 and returns the corresponding MemoryArea.

To perform this allocation, if size bytes (or more) are available in the slack of the topmost page, it is allocated there without further ado, and the slack is reduced accordingly.

Otherwise, a new page is allocated on the heap. Small allocations (where size is smaller than the standard page size, set by default to 256KB but which is a user-definable parameter) cause the page to be allocated with this standard page size, the unused part of the page being then turned to slack, while larger allocations require a separate page of size bytes (with no slack), as shown in the diagram where one of the pages is noticeably larger than the others. For backward compatibility, the public stack pointer is made available as an integer attribute named StackPtr.[21], where the most significant 8 bits indicate the page number, and the least significant 24 bits give the offset within the page. Deallocating on the stack thus requires nothing more than setting this attribute to a previously saved value (typically, on function entry).

More specifically, it is the generated code’s responsibility to restore this stack pointer upon method exit or when implementing a non-local GO TO statement that exits the current scope.

It is important however to realize that restoring a stack pointer which was saved upon scope entry is performed by a direct assignment, and will thus as such never cause a page deallocation. It is only when performing new allocations that pages above the current page stack pointer will be deallocated.

stack

As shown on the diagram above, pages above the top page stack are deallocated, leaving one extra page just in case (but it is then set to be made entirely of slack), to avoid oscillating situations where pages are allocated and deallocated in fast sequence because one happens to be crossing the boundaries of a full page repeatedly.

By setting the default page size to a large value such as 4MB, one reverts to the original behavior, as the first allocated page is then large enough to accommodate most if not all stack usages.

Memory allocation

The execution context includes a full featured allocator to provide dynamic allocations in the attached address space.[22]; this feature is PL/I specific.

public MemoryArea Allocate(int size)
public void Deallocate(MemoryArea m)

Note that, this memory allocator does not include a garbage collector. It is the caller’s responsibility to explicitly deallocate memory when it is no longer used.

9.3.7. Logging

Logging is controlled in the static class Raincode.Core.Logging.Logger, through the property:

public static Level LogLevel { get; set; }

Level is one of:

public enum Level : byte
{
    SILENT = 0,
    ERROR = 1,
    WARNING = 2,
    INFO = 3,
    DEBUG = 4,
    TRACE = 5,
    PROGRAM_OUTPUT = 6
    DIAGONSTIC = 7
}

This can be set using the LogLevel command-line option for rclrun.

To log something, the method is used :

public static void Log(Level level, (1)
    LogSource source, (2)
    string format,
    params object[] args)
1 The first argument is the log level
2 The second argument is an an instance of LogSource:
public class LogSource
{
    public string Name { get; private set; }
    public LogSource(string name);
}

The LogSource class is useful to attach a source to the logs, to help identify the part of the code that emitted the log.

A ConsoleLogger class is provided which outputs the logs the standard output. To customize the handling of logs, it is possible to create a custom logger, inheriting from the AbstractLogger class:

public abstract class AbstractLogger
{
    protected LogSource Filter { get; private set; }
    public AbstractLogger();
    public AbstractLogger(LogSource filter);
    protected abstract void LoggingFacilityLogged(LogEventArgs e);
}

LogEventArgs is an instance of:

public class LogEventArgs : EventArgs
{
    public static bool ShowTimeStamp;
    public static bool ShowThreadId;
    public static bool ShowLogSource;
    public static bool ShowLevel;

    public LogEventArgs(Level level, LogSource logSource, string msg);

    public Level Level { get; }
    public string Message { get; }
    public DateTime TimeStamp { get; }
    public LogSource LogSource { get; }
    public string ThreadId { get; }
    public string Header { get; }

    public override string ToString();
}

The custom logger must also register to the Logger class; this is done using the following method:

Logger.CreateAndRegister<ConsoleLogger>();

As the name implies, it will create an instance of the logger class specified in the generic parameter, and register it. When something is logged using one of the Log method on the Logger class, the method LoggingFacilityLogged will be called on every registered loggers.

9.3.8. RaincodeLegacyRuntimeQixContext

The RaincodeLegacyRuntimeQixContext contains the QIX run-time environment (channels, containers, QIX activation records). It is accessible from the ExecutionContext through the RaincodeQixContext property. It is automatically created when QixMode is set to true. It is currently not possible to extend the RaincodeLegacyRuntimeQixContext.

9.3.9. Module, Entry and Executable

Module is the base class for compiled legacy programs. Modules can also contain additional entries, represented at run time by instances of the Entry class. Entry and Module share a common Executable base class. Executable provides a number of overloaded versions of the Execute function:

public void Execute(ExecutionContext ec, CallParameters parms)

Executable also provides a CallingConvention (see CallingConvention) property, giving the calling convention that an Executable expects.

The Executable abstracts the difference between calling a module or one of its entries. The Module class also exposes a read-only integer resource, named SqlStatementCount , which indicates the number of SQL statements found in the module at hand.

9.3.10. CallParameters

The CallParameters class encapsulates a parameter list and the information relative to the calling convention. It exposes the following public members:

  • MemoryArea ParameterList

  • int Arity

  • CallingConvention CallingConvention

  • DescriptorFormat DescriptorFormat

  • ITypeDescriptor[] Descriptors

When creating a CallParameters instance for the purpose of calling a COBOL or PL/I module, only the ParameterList and Arity attributes should be set. For an HLASM module, only ParameterList needs to be set.

The code generated by the compiler will fill all those fields, except Descriptors, which is only filled if the flag RegisterParamTypeDescriptors is set. This allows to write a custom Module that can be called from both PL/I and COBOL, using each language’s respective default calling convention.

While those attributes are all public for convenience and flexibility, it is usually better to use the following methods to create a parameter list or to retrieve the parameter address:

  • static CallParameters StackAllocateParameterList(ExecutionContext ec, int arity): creates an instance of CallParameters with enough space for arity parameters in the ParameterList. That space is allocated on the stack (see ExecutionContext and Memory allocation).

  • static CallParameters AllocateParameterList(ExecutionContext ec, int arity): same as above, but allocates the ParameterList on the heap. You are responsible for freeing it if necessary.

  • MemoryArea GetParameterAddress(ExecutionContext ec, int index): returns the address of a parameter. For COBOL, it will always be the address where the parameter data lives. For PL/I, what the returned address points to depends on the calling convention (see CallingConvention).

  • void SetParameterAddress(ExecutionContext ec, int index, MemoryArea parmAddr): sets the address of a parameter in the ParameterList.

  • void SetParameter(ExecutionContext ec, int index, BaseHelper helper): same as above, but accepts a helper structure directly.

For COBOL, the above methods can be used without any special considerations. For PL/I, it is strongly advised to read CallingConvention and DescriptorFormat.

Arity

The Arity attribute is simply the number of arguments. Note that the return value, if any, is the last argument and is part of the arity.

In the following code:

P0: PROC(a, b, c);
  DCL (a, b, c) FIXED BIN(31);
END;

P1: PROC(a, b) returns(FIXED BIN(31));
  DCL (a, b) FIXED BIN(31);
END;

both P0 and P1 have an arity of 3. From the calling convention point of view, these 2 procedures are strictly equivalent. Therefore, it is possible (though obviously not advisable) to invoke P1 as follows:

DCL P1 EXTERNAL ENTRY(FIXED BIN(31),
                      FIXED BIN(31),
                      FIXED BIN(31));
DCL (v0, v1, v2) fixed bin(31);
CALL P1(v0, v1, v2);
ParameterList

The ParameterList is a list of pointers to parameter related data. The target of each of these pointers depend on both the calling convention and the data type of the parameter.

CallingConvention

RainCodeLegacyRuntime currently supports two calling conventions:

  • NoDescriptor: each pointer in the parameter list points directly to the parameter data (synonym: Cobol)

  • Descriptor: depending on the data type, the pointer in the parameter list points to:

    • for arrays and structures, a pair of pointers. The first pointer points to the parameter data, the second one to a descriptor (see DescriptorFormat)

    • for strings, a pair composed of a pointer and string descriptor (see DescriptorFormat)

    • for other data types, the parameter data

The DescriptorList convention, while already present in the CallingConvention enum, is not yet available.

The COBOL compiler only uses the NoDescriptor (or Cobol) calling convention. The PL/I compiler uses the Descriptor calling convention by default, unless OPTIONS(COBOL) or OPTIONS(ASM) is specified on an entry or procedure, in which case NoDescriptor is used.

Note that the extra indirection for strings and aggregate allows for code like this to work (although it is not advisable to do this; argument type should match between a caller and a callee):

P0: PROC;
  DCL P1 EXT ENTRY;
  DCL 1 S
    , 2 F0 FIXED BIN(31)
    , 2 F1 CHAR(8)
    ...
  CALL P1(S);
END;
...
P1: PROC(P);
  DCL P POINTER;
  DCL 1 S BASED(P)
    , 2 F0 FIXED BIN(31)
    , 2 F1 CHAR(8)
    ...
END;
In some cases, when called from PL/I using the Descriptor calling convention, and the caller passed a reference to a nested structure, the pointer to the parameter data needs to be adjusted with the offset of the first field, given in the descriptor.
The compiler only takes care of using the right calling convention. When calling a COBOL module from PL/I or vice versa, you have to make sure that the parameter data are aligned the same way. The compiler will NOT realign data to match the alignment rules of the callee’s language.

Finally, for both PL/I and COBOL, the last entry in the parameter list has its high bit set.

DescriptorFormat

Since neither HLASM nor COBOL use descriptors, this is useful mainly for PL/I.

Only one format, cmpatv2 is currently supported.

String (both CHAR and BIT) descriptors are 32 bits integers:

DescriptorFormat
Figure 123. DescriptorFormat

The V flag means varying.

The bit offset is only used for BIT strings.

Array descriptors have the following layout, where each element is a 32 bits integer:

ArrayDescriptor
Figure 124. ArrayDescriptor

The stride is the number of bytes separating two successive elements in the array. The RVO (Relative Virtual Origin) is the sum of the products of the strides by their corresponding lower bound.

Structure descriptors are a list of offsets, where each offset is followed by a field descriptor if necessary. If a field is an array of strings, the array descriptor precedes the string descriptor. Note that at that point, structures are flattened; the structure hierarchy is not present anymore, and array bound information is duplicated for each leaf. Consider the following example structure:

DCL 1 S
  , 2 F0 FIXED BIN(31)
  , 2 F1 CHAR(8)
  , 2 F2(5)
    , 3 F2_0 FIXED BIN(31)
    , 3 F2_1 CHAR(8);

The descriptor will have the following layout, where each element is a 32 bits integer:

structuredescriptor
Figure 125. StructureDescriptor

The full descriptor for this structure is thus 56 bytes long.

Descriptors

The Descriptors field is usually null. Using the flag:

RegisterParamTypeDescriptors

the compiler will generate extra code to fill this array with ITypeDescriptor (see ITypeDescriptor) instances.

Note that, in general, the amount of code required to generate those type descriptors is not negligible. It is therefore recommended to use this option only if absolutely necessary. It is usually better to use helper structures (see Parameter passing using helper classes).

9.3.11. AssemblyLoading

The Raincode.Core.AssemblyLoading.AssemblyLoading static class is the entry point to the assembly loading facility. AssemblyLoading exposes a Loader property:

public static AbstractAssemblyLoader

AbstractAssemblyLoader is the base class for custom assembly loaders. Custom assembly loaders should implement the methods:

protected abstract IEnumerable<object> GetAssemblyKeys(string category, AssemblyName name);
protected abstract Assembly DoLoadAssembly(object key, AssemblyName name);

protected abstract void DoAddPath(string category, string p);

GetAssemblyKeys searches an assembly given a category and a name, and returns keys to be used by DoLoadAssembly.

DoLoadAssembly loads the assembly designated by the key. The name may be used for logging.

DoAddPath is used to add search paths (or equivalent). Programmatically adding a search path or loading an assembly is done using the following methods on an AbstractAssemblyLoader:


public Assembly LoadAssembly(string category, AssemblyName name);
public Assembly LoadAssembly(string category, AssemblyName name, AssemblyLoaderDelegate loadAssemblyDelegate);

public void AddPath(string category, string path);

LoadAssembly takes an assembly name and an optional delegate. The delegate is:

public delegate bool AssemblyLoaderDelegate(Assembly assembly);

This delegate can be used to check if the assembly matching the name is really the one needed. For instance, when looking for an assembly containing a PL/I or COBOL module, this delegate is used to check that the assembly actually contains one.

The default AbstractAssemblyLoader is an instance of :

public class FileAssemblyLoader : AbstractAssemblyLoader {}
FileAssemblyLoader

The FileAssemblyLoader class implements an AbstractAssemblyLoader that simply looks for a file on the filesystem. The paths checked for the existence of an assembly are:

  • The current path ( .)

  • The path where the entry assembly was loaded from.[23]

  • The value of the RCDIR environment variable, split into using semicolon characters

  • The paths specified using the AddPath function

Searching for an assembly is done in the following way:

  • The loader looks in each path for an assembly matching the given assembly name.

  • If a matching assembly is found, the loadAssemblyDelegate is invoked to check if it is the right one. If yes, then the search is stopped and the found assembly is returned. If a loadAssemblyDelegate is not provided, the first assembly matching the name is returned.

  • Otherwise, the loader simply goes to the next path, until the path list is exhausted.

9.3.12. ModuleDictionary

Most of the facilities provided by Raincode Legacy Compilers are somewhat connected to the execution context (See section ExecutionContext), which provides thread isolation.

On the other hand, ModuleDictionary is a static class, which implements the module table. It is global to the process, and thus shared among all of its threads. It is the data structure that keeps track of the modules and the entries which have been loaded and which can be called immediately.

The global nature of the module dictionary implies that two threads cannot run two different programs with the same name within the same process.

The ModuleDictionary static class keeps track of a list of loaded modules as well as a list of callable Executables, as seen in the following figure, where Module B’s additional entry point is available in this way.

image053
Figure 126. ModuleDictionary is a static class implements the module table

ModuleDictionary exposes several functions:

public static Executable FindExecutable(string name) (1)
public static Module FindModule(string name) (2)
1 finds a named legacy program executable (module or entry) and attempts to load the module on the fly by calling loadModule if it is not already present in the ModuleDictionary;
2 does the same, but is restricted to Module`s only. PL/I’s `FETCH statement translates to a call to findExecutable which does load the module if needed.

9.3.13. BaseRunner

The BaseRunner class serves as the entry point for the execution of any legacy program compiled as a DLL. Its purpose includes parsing command-line arguments, setting the proper legacy program environment (QIX and SQL run time mainly), and starting the execution of a module. All the options available in rclrun (See section rclrun command-line options) are directly supported by BaseRunner. The set of supported options can be extended by inheriting this class and overriding the appropriate methods. You can also extend the set of supported QIX run times, QIX counter handlers, SQL run time and redirect the output to any .NET TextWriter.

Supporting additional writers

The method

public virtual TextWriter getWriter(string a);

is used to recognize the string attached to the writer option. By default, it only recognizes console which corresponds to the standard output stream of your application (.NET Console.Out).

9.4. SQL support

9.4.1. Architecture

This section describes the scaffolding of classes that implement the SQL run time architecture, with a focus on the features that can be used to redefine the various behaviors. There are four classes you must re-implement to use a custom SQL environment (See Scaffolding of classes that implement the SQL run time architecture).

image054
Figure 127. Scaffolding of classes that implement the SQL run time architecture

The main entry point for the SQL run time is the SqlRuntime class. It is connected to the execution context (See section ExecutionContext), and maintains the transaction and connection to the database. It also provides factory services to create Cursor and Command instances.

A Cursor object represents a cursor as declared, open and fetched from within a legacy program. On the other hand, Command represents an atomic operations that can be executed by executing a Delete , Insert or Update SQL statement.

Direct SQL select statements, without an explicit cursor declaration, as in:

EXEC SQL
    SELECT SUM(AMOUNT)
    FROM INVOICE
    WHERE CODE= :W_CODE

can be run as commands, or turned automatically into a cursor, where Raincode Legacy Compilers automatically generates the sequence of statements declaring, opening, fetching and closing the cursor, using the GenerateSyntheticCursor Raincode Legacy Compilers command-line option.

Each Cursor is attached to one or more Fetch instances, that relate to SQL FETCH statements as found in a legacy program.

In the vast majority of the cases, there will be a single Fetch object for every cursor, but the ability to support multiple cursors addresses oddball cases where there is more than one SQL FETCH statement for a cursor, as in:

EXEC SQL DECLARE ZCURSOR CURSOR FOR SELECT...
OPEN ZCURSOR;
FETCH ZCURSOR INTO :VAR_A, :VAR_B;
...
FETCH ZCURSOR INTO :VAR_X, :VAR_Y;

Cursor, Command and Fetch objects have access to three lists of host variables: one is the list of variables as they appear in the original statement, one for input host variables and one for output host variables. These lists are accessible using read-only properties:

public List<HostVariable> HostVariables {get {...} }
public List<HostVariable> InputHostVariables { get { } }
public List<HostVariable> OutputHostVariables {get { } }

Cursor and Command instances representing SQL DELETE, INSERT or UPDATE statements only have input variables, Fetch instances only have output variables, and Command instances representing direct SQL SELECT statements can have both input and output host variables.

The HostVariable class comes with a number of subclasses, depending on the type of the host variable at hand: CharStringHostVariable, FixedDecHostVariable, FixedBinHostVariable, etc.[24]

Unlike the other classes described in this section, the HostVariable classes are not meant to be subclassed when targeting a new database implementation. They are created directly by Raincode Legacy Compilers depending on the host variable type at hand, and it would be cumbersome to try to replace, for example, every CharStringHostVariable instance by a MyCharStringHostVariable instance, as they are not created from a single central place.

On the other hand, these classes provide fairly straightforward services only, transferring SQL data to and from legacy program host variables, and should not require any ad hoc customization whatsoever.

9.4.2. Three existing implementations

The Raincode Legacy Compilers distribution comes with three derived implementations of this scaffolding of classes implementing SQL support:

  • SqlServerRuntime, SqlServerCommand, SqlServerCursor, SqlServerFetch to support Microsoft SQL Server®

  • DB2HisRuntime, DB2HisCommand, DB2HisCursor, DB2HisFetch to support DB/2 through HIS (a.k.a. Host Integration Server Integration Server) for static SQL support.

  • DB2Runtime, DB2Command, DB2Cursor, DB2Fetch to support DB/2 through IBM’s .NET connector.

Depending on the command-line options, rclrun creates an instance of the SqlServerRuntime, DB2HisRuntime, or the DB2Runtime, and attaches it to the ExecutionContext before starting the execution of the legacy program at hand.

9.4.3. SqlRuntime

When implementing a custom SqlRuntime class, you must redefine two factory methods responsible for the creation of Cursor and Command instances.

public abstract Cursor allocateNewSqlCursor(Module module,
        int seqNr,
        int sig,
        string stmt);
public abstract Command allocateNewSqlCommand(Module module,
        int seqNr,
        int sig,
        string stmt);

The .NET objects that describe these cursors and commands are cached, so that they are only elaborated at most once for every execution context where they are used. There is thus no need to provide a recycling mechanism of any kind in an alternate implementation.

The instances created by these factory methods (See sections Cursor and Fetch) must include the bulk of the intelligence required to communicate with the database. There are only two truly database-dependent facilities that must be provided by the SqlRuntime class, namely the commit and rollback methods.[25]:

public abstract void commit(MemoryArea sqlca);
public abstract void rollBack(MemoryArea sqlca);

9.4.4. Cursor

The Cursor class denotes a SQL cursor.

Functions

It provides a factory function of its own, to allocate a Fetch (See section Fetch) instance attached to the cursor at hand:

public abstract Fetch allocateFetch(int sig,
        MemoryArea sqlca);

When implementing a custom Cursor class, you must thus redefine this allocateFetch function so that it returns a similarly derived custom Fetch instance.

The two other functions that must be redefined are doOpen and doClose (they are called by functions named respectively open and close, which perform additional checks before proceeding, such as ensuring that a cursor is properly closed before opening it again, for instance).

public abstract bool doOpen(MemoryArea sqlca); (1)
public abstract void doClose(MemoryArea sqlca);(2)
1 doOpen must transfer to the database, the values of the input host variables which are attached to the cursor, and actually open the cursor using the target database’s native API. It returns a Boolean value to indicate whether opening the cursor was successful.
2 Similarly, doClose is called when the cursor is to be closed.
  • When executing an explicit EXEC SQL CLOSE statement

  • Before opening an already opened cursor that has not been closed explicitly

  • Implicitly when direct SQL SELECT INTO statements are converted using a synthetic cursor, with a sequence open, fetch and close generated automatically by Raincode Legacy Compilers.

Properties

The Cursor class also provides several properties:

public System.Data.IDataReader Reader {
    get { }
    protected set { }
}

Reader maintains the IDataReader associated with the cursor at hand. IDataReader is the standard .NET interface which is meant to be implemented by all the database drivers, and which can be used to operate on the data fetched from the database.

The predefined behavior of the Fetch class (See section Fetch) relies on this reader extensively to move data to the host variables of the legacy program at hand.

public bool SyntheticCursor { get { } }

SyntheticCursor indicates whether the cursor is synthetic, defined implicitly by Raincode Legacy Compilers to support direct SELECT INTO statements. It is used, among other things, to produce an error message if a direct SELECT INTO statement returns more than one row, as shown in this document (See section Fetch):

public string Name {get { } }

Name returns the name of the cursor. In case of a synthetic cursor, the returned name is of the form CURSOR_XX where XX is some form of disambiguating numeric value. Otherwise, this property returns the cursor’s original name as declared in the legacy source program.

Server-side cursors and client-side cursors

In the SQL Server implementation of and the SQL runtime, there are 2 types of cursors available:

  • client-side cursors

  • server-side cursors

Client-side cursors are used whenever it is not necessary to explicitly use a cursor in the database. Client-side cursors rely on an ADO.Net DataReader, and can therefore only go forward. They are less resource hungry and faster than server-side cursors.

Server-side cursors are used whenever a cursor is declared FOR UPDATE or WITH HOLD. In those cases, the compiler automatically decides to use server-side cursors.

If cursors are declared WITH HOLD but are not really depending on the semantics, the compiler has the IgnoreWithHold option that can be used to ignore the clause.

9.4.5. Command

The Command class is much simpler than the Cursor class and it provides a single function to redefine in case of custom redefinition,

public abstract void doExecute(MemoryArea sqlca);

which must be redefined to transfer the parameters to the database, perform the operation, return data (if applicable) and return error codes.

9.4.6. Fetch

Fetch is a class that implements the behavior of the SQL FETCH statement. It provides a single public function:

public abstract SqlCode fetchNext();

fetchNext reads the next record in the result set, transfers the data from the database back to the associated host variables, and sets the error codes accordingly.

If the target database at hand provides an IDataReader based API, the default implementation of this function can be used, as is. It gets this reader from the cursor which the Fetch instance is connected to, and uses it as an abstract device to connect read data independently from the database server.

9.4.7. SQLCA data structure

All methods used for querying the database take a MemoryArea as parameter, which contains the SQLCA data structure. You can easily manipulate this structure from within any .NET language with the SqlCaWrapper class, which maps properties to each field of the structure.

9.4.8. Using meta-comments to annotate SQL constructs

Meta-comments (see section Meta Comments) can be used to mark some of the SQL constructs with information that can then be retrieved from within a SQL implementation.

Marking binary host variables

When retrieving data from a relational database, the source data type as returned by the database, and the target data type as defined in the calling COBOL or PL/I program determine the treatments and conversions that are performed.

For instance, when fetching a date column, it will be converted to a character string automatically if needed. The same goes for numeric values, etc. This scheme falls short when processing binary data. On the mainframe, they can be stored on character data fields without further ado, as the program and the database essentially run on the same encoding. On the other hand, when running on modern distributed systems, it is a totally different matter, as the default behavior is to use the code page at hand to perform (not always reversible) transformations before storing binary data extracted from the database into variables defined in the program at hand.

Legacy languages such as PL/I and COBOL do not provide a built-in way to indicate that a given variable must be considered as binary data, and whatever comes from the database should then be stored raw, without additional processing of any kind.

To address this issue, the Raincode Legacy Compilers support a meta-comment named SQLTYPE as shown in the examples below:


/*RC:SQLTYPE=BINARY*/
DCL HBIN CHAR(100);
/*RC:SQLTYPE=VARBINARY*/
DCL 1 HVAR,
	2 HVAR_LEN FIXED BIN(31),
	2 HVAR_BUFF CHAR(100);

This facility is available in COBOL as well:

**RC:SQLTYPE:BINARY
	05 W-HBIN PIC X(100). 
**RC:SQLTYPE:VARBINARY
	05 W-HVAR. 
	10 W-HVAR-LEN COMP PIC 9(9). 
	10 W-HVAR-BUFF PIC X(100). 
  • The values supported for the SQLTYPE meta-comment are BINARY, VARBINARY ,DATE,TIME and TIMESTAMP.

  • If one uses BINARY, the host variable must be declared as a static character string.

  • If one uses VARBINARY, the host variable must be declared as a structure made of two fields, a length defined as a binary value on 32 bit, and a character buffer.

  • If one uses DATE, the host variable must be declared as a static character string with length 10.

    /*RC:SQLTYPE=DATE*/
    , 2 sqlDate char(10)
  • If one uses TIME, the host variable must be declared as a static character string with length 8.

    /*RC:SQLTYPE=TIME*/
    , 2 sqlTime char(8)
  • If one uses TIMESTAMP, the host variable must be declared as a static character string with length 26.

    /*RC:SQLTYPE=TIMESTAMP*/
    , 2 sqlTimeStamp char(26)
  • The compiler will ensure that the meta-comment is consistent with the type of the variable it applies to.

  • The other meta-comments described in this section are not processed by the compiler itself. They are attached to the runtime objects that represent SQL host variables and SQL comments, for some treatment by a bespoke version the runtime environment. In contrast, the SQLTYPE meta-comment described in this paragraph influences the code generation directly.

As a form of syntactic sugar, and for COBOL only, the sample declarations shown above can also be written using SQL extensions for variable declarations, as:

	 05 W-HBIN SQL TYPE IS BINARY (100).
	 05 W-HVAR SQL TYPE IS VARBINARY(100). 

For the variable denoted as VARBINARY, the two subfields denoting the length and the buffer, with resp. the -LEN and -BUFF postfix are created automatically, with the appropriate type attributes.

Marking host variables

This mechanism can be used to annotate the host variables so that the annotation can possibly be used by a customized SQL runtime at hand.

You must use a meta-comment prefixed with SQLHOST before the declaration of the host variable. When the declaration is used as a parameter (in or out) for a SQL statement, the corresponding host variable holds the content of the meta-comment. Multiple meta-comments can be attached to a single variable declaration, as they are made accessible as a list of strings by the GetMetas method.

For instance, in the PL/I code below,

/*RC:SQLHOST:BITMAP*/
DCL PICT CHAR(1024);
EXEC SQL SELECT PHOTO INTO :PICT FROM CUST WHERE NAME = :NAME;

the host variable instance for the PICT declaration as used by the SQL statement will hold a BITMAP string value in the list of strings returned by GetMetas. It is then the responsibility of the SQL run time at hand to do something about these annotations.

Marking SQL statements

Any SQL statement can be marked in the same way, using one or more meta-comments prefixed by SQLHOST: as in:

/*RC:SQLHOST:NOERROR*/
EXEC SQL DELETE FROM CUST WHERE ID = :ID;

These annotations can be used both for cursors (with a separate DECLARE, OPEN, FETCH and CLOSE statement) and for self-contained statements (INSERT, UPDATE, DELETE, SELECT INTO). A GetMetas method is available for both SQL cursors and SQL commands in the Raincode Legacy Compilers run time.

However, when using cursors, both the meta-comments attached to the DECLARE and to the OPEN statements will be attached to the cursor, while any meta-comment attached to the FETCH or CLOSE statements will be ignored.

9.5. CICS support

Raincode Legacy Compilers support CICS in a way that is very similar to what is described in this document for SQL (See section SQL support). A factory creates instances of CICS statements that can be overridden to implement custom behavior. The Raincode Legacy Compilers support out of the box a small subset of CICS statements. Any statement that is not implemented will throw a NotImplementedException and a custom implementation must be provided. See Implemented commands on how to extend the CICS runtime.

9.5.1. Extending CICS support

The CICS runtime consists of a factory class and instance classes for each type of command.

Instance classes

Each CICS command has a corresponding class in the RaincodeQixRuntimeInterface.QixInstances namespace. Each of those class has properties for all parameters the command supports, and a reference to the current DFHEIBLK and the current ExecutionContext (in the form of an IQixContext). For instance, the ABEND command supports an input parameter ABCODE, and a CANCEL and NODUMP flag.

The AbendInstance class provided has an ABCODE string property, and a Flags property of type QixFlags which contains boolean values for CANCEL and NODUMP. Note that the QixFlags class contains properties for the complete set of flags supported in CICS commands.

All instance classes have an Execute method that must be overridden in subclasses to provide a custom implementation, otherwise, a NotImplementedException is thrown.

Furthermore, instance classes all inherit from BaseQixInstance, giving access to the current DFHEIBLK through a DfheiblkHelper. See DFHEIBLK for more about the DFHEIBLK.

Factory

Once the CICS command has been implemented using a custom instance class, the factory must be updated accordingly, for the custom class to be picked up at runtime.

The base factory is RaincodeQixRuntimeInterface.QixInstances.QixFactory and instantiates the default instance classes for all CICS commands. The RainCodeLegacyRuntime already extends this factory to provided support for some CICS commands. See Implemented commands for the list of implemented commands. To provide your own factory, simply inherit from the RainCodeLegacyRuntime.QIX.Interface.Factory and override the appropriate CreateXXXX methods to instantiate your own instance class. For instance, if you need to provide your own implementation for the CICS ABEND command:

public override AbendInstance createAbend { return new Abend; }

Once the factory has been implemented accordingly, the last step is to tell the Raincode runtime to actually use it. This is done by assigning the Factory property of the QixInterface in the ExecutionContext before executing your transaction:

ExecutionContext.QixInterface.Factory=newMyFactory;

When programmatically creating QIX commands, it is recommended to always use the factory, and avoid explicitly instantiate them with the new operator.

9.5.2. Implemented commands

The following CICS commands are supported out of the box by the RainCodeLegacyRuntime, and can be extended if needed. The classes are located in the RainCodeLegacyRuntime.QIX.Interface.Statements namespace.

More statements are supported by the compiler but do not provide an implementation and will throw a NotImplementedException at runtime.

  • ABEND

  • ADDRESS

    • EIB

    • COMMAREA

    • CWA

  • ASKTIME

  • ASSIGN

    • APPLID

    • FACILITY

    • NETNAME

    • STARTCODE

    • SYSID

    • USERID

  • DEFINE COUNTER

  • DELAY

    • FOR

    • INTERVAL

  • DELETE FILE

  • DELETE COUNTER

  • DELETEQ TS

  • ENDBR

  • FORMATTIME

    • DAYCOUNT

    • DAYOFMONTH

    • DAYOFWEEK

    • MONTHOFYEAR

    • YEAR

    • DATE

    • FULLDATE

    • TIME

    • DDMMYY

    • DDMMYYYY

    • MMDDYYY

    • MMDDYYYY

    • YYDDMM

    • YYMMDD

    • YYYYDDMM

    • YYYYMMDD

    • YYDDD

    • YYYYDDD

    • DATEFORM

  • FREEMAIN

  • GET CONTAINER

  • GET COUNTER

  • GETMAIN

  • HANDLE

    • ABEND

    • AID

    • CONDITION

  • IGNORE CONDITION

  • LINK

  • POP HANDLE

  • PUSH HANDLE

  • PUT CONTAINER

  • READ

  • READNEXT

  • READPREV

  • READQ TS

  • RESETBR

  • RETURN

  • REWRITE

  • STARTBR

  • SYNCPOINT

  • SYNCPOINT ROLLBACK

  • UNLOCK

  • WRITE

  • WRITEQ TS

  • XCTL

This implementation can be used, as is, for systems that make a very limited use of CICS, or can be used as the foundation for a more comprehensive CICS implementation.

9.5.3. DFHEIBLK

All the instance classes have access to the current DFHEIBLK.[26] through an instance of RaincodeQixRuntimeInterface.DfheiblkHelper. The underlying data structure serves as the CICS control block, and holds the same physical structure as on the mainframe to ensure compatibility with existing code. The same DFHEIBLK can be accessed in the RaincodeLegacyRuntimeQixContext.

9.5.4. QIX constants and error codes

QIX condition and error codes are defined in constants found in RaincodeQixInterface.Constants. These constants are used by the provided base QIXimplementation and should be used by all overriding classes too.

9.5.5. Counters

The QixBaseCounterHandler is the base class for handling CICS counters. It implements the following functionalities:

  • DEFINE COUNTER

  • GET COUNTER

  • DELETE COUNTER.

through the following methods,

public virtual void DeleteCounter(MemoryArea dfheiblk,
        string counterName);
public virtual void DefineCounter(MemoryArea dfheiblk,
        string counterName,
        long initVal,
        long minVal,
        long maxVal);
public virtual long GetCounter(MemoryArea dfheiblk,
        string counterName,
        long increment,
        bool wrap);

which are called from the QIX run time. This component can be replaced at run time, if the default mechanism is not suitable.

Default Implementation

The QixBaseCounterHandler serves as the base class for any QIX counter handler. It is implemented using a SQL Server database to persist the counters states. It uses an IDbConnection provided by the RaincodeLegacyRuntimeQixContext class, which is open when accessing a QIX counter feature for the first time. It uses a single SQL Server table for storing all necessary values (see table Structure of the QIX_COUNTERS table) for persisting the counters (name, minimum, maximum and current value). If the table is not present prior to the execution of a counter related feature, this table will be created.

When defining a new counter, a record is inserted in the database with the provided counter properties. When the current value of the counter is required, this record is fetched from the database, the counter value incremented, and saved back to the database. Deleting a counter then translates simply to a record deletion from the database.

Table 27. Structure of the QIX_COUNTERS table

Column name

Type and properties

QC_NAME

CHAR(16) NOT NULL PRIMARY KEY

QC_MIN_VALUE

INT NOT NULL

QC_MAX_VALUE

INT NOT NULL

QC_CURRENT_VALUE

INT NOT NULL

The QIX_COUNTERS table will be created on the fly when executing the first counter-related statement, so you must ensure that you have a valid instance of SQL Server running, and must provide a valid connection. The prefixed table name should prevent any name conflict with any other preexisting table.

At run time, errors and conditions are set properly in the RESP and RESP2 fields of the DFHEIBLK structure (See section Extending CICS support). In case of a missing or invalid connection to the database, the program will end with a CICS ABEND condition.

Table RESP and RESP2 values shows the possible RESP and RESP2 values (symbolic names):

Table 28. RESP and RESP2 values

Field type

Field

Value

RESP

DEFINE COUNTER

INVREQ

RESP

GET COUNTER

INVREQ, SUPPRESSED

RESP

DELETE COUNTER

INVREQ

RESP2

DEFINE COUNTER

202, 406, 407

RESP2

GET COUNTER

101, 201, 406

As there are no symbolic names provided for these error codes, table Error codes gives a description for each error code. Note that some error codes can be reused in a different context with a different meaning.

Table 29. Error codes

Error code

Description

101

Counter at limit: the counter reached its limit and the WRAP option was set to FALSE

201

Counter with given name not found

202

Counter already exists with this name

406 - GET COUNTER

Invalid increment, the increment is bigger than the counter range

406 - DEFINE COUNTER

Invalid initial value. Either value is smaller than lower bound, or greater than upper bound + 1

407

Invalid lower or upper bound. Either upper bound is smaller than lower bound or one of the bounds is negative or initial value is negative

Using the default counter handler

When using the QixBaseCounterHandler, legacy programs making use of CICS counters require a SQL Server and a database connection for the QIX run time. The connection string can be given to rclrun (See section rclrun command-line options) using the :qixconnectionstring argument, or set programmatically by using the ConnectionString property of the RaincodeLegacyRuntimeQixContext instance at hand:

public string ConnectionString { get; set; }

This property is used on demand, when first requiring access to this connection. Changing the connection string after the connection has been made will not affect the connection and all features relying on this connection will continue to use the previously opened connection.

Using a custom counter handler

By overriding the QixBaseCounterHandler, you can implement your own component for handling counters using any mechanism considered suitable. Although this class is not abstract, all public methods must be re-implemented in order to avoid unstable behavior, as the base class relies on a database, which might not be the case of the overriding class. The implementer must also properly take care of all error codes.

The QIX counter handler can be set at run time through the public QixCounterHandler property in the RaincodeLegacyRuntimeQixContext class.

Note that if no custom handler is set, the QixBaseCounterHandler will be used by default.

9.5.6. Temporary storage queues support

The Raincode runtime comes with an implementation of temporary storage queues in memory. This is suitable when the application is not running in a distributed environment. Like for all QIX commands, it is possible to override the default behavior either by overriding classes ReadQTSInstance, WriteQTSInstance and DeleteQTSInstance, or by implementing a set of higher level interfaces.

Overriding QIX commands instances

This is the standard way of extending/overriding QIX, but also the most difficult when it comes to providing a full implementation, putting more responsibilities on the implementer. For instance, care must be taken to set EIBRESP values correctly as well as setting output values correctly and allocating required memory.

Implementing ITSQueueManager and ITSQueue

The Raincode Legacy Runtime comes with a skeleton implementation, taking care of setting correct EIBRESP values, and copying the data properly. It is possible to avoid having to re-implement ReadQTSInstance, WriteQTSInstance and DeleteQTSInstance by providing an implementation for interfaces ITSQueueManager and ITSQueue. These interfaces provide a higher level of abstraction over directly overriding commands instances, and is therefore easier to implement.

The actual implementation can be set using the TemporaryStorageQueueManager property of the RaincodeQixContext in the ExecutionContext. Make sure that QIX was enabled before trying to set it, using

ec.QixMode = true

where ec is the instance of your ExecutionContext.

9.5.7. Transient data queues support

The Raincode runtime comes with a limited implementation of transient data queues in memory. This is suitable as long as the application is not running in a distributed environment, and such queues don’t need to be recoverable. The default implementation also creates queues on the fly, whereas TD queues are supposed to be configured beforehand.

Like for all QIX commands, it is possible to override the default behavior either by overriding classes ReadQTDInstance, WriteQTDInstance and DeleteQTDInstance, or by implementing a set of higher level interfaces, similar to the ones provided for temporary storage queues.

Implementing ITDQueueManager and ITDQueue

Implement these two interfaces to provide a custom implementation for transient data queues, without having to worry about conditions and copying the data. The actual implementation can be set using the TransientDataQueueManager property of the RaincodeQixContext in the ExecutionContext. Make sure that QIX was enabled before trying to set it, using

ec.QixMode = true

where ec is the instance of your ExecutionContext.

9.6. Exceptions

Raincode Legacy Compilers supports .NET exceptions in different ways. A number of serious execution errors, such as a stack overflow, result in an exception being thrown to stop the application at hand. It can also recover from exceptions thrown from library code, using the standard PL/I ON ERROR mechanism. It must be noted however that PL/I’s conditions do not map seamlessly to .NET exceptions, as conditions must be able to return to the place where the exception occurred, while .NET exception exit the scope where the exception is caught.

Raincode Legacy Compilers also uses exceptions to implement non-standard control flow, label variables, etc. These exceptions are thus raised and caught by Raincode Legacy Compilers generated code, and should not be considered as exceptions in the sense of potentially abnormal behaviors. The exceptions that implement these control flow primitives all inherit from

RainCodeLegacyRuntime.Exceptions.ControlException:

  • RainCodeLegacyRuntime.Exceptions.NonLocalGotoException is thrown when a nonlocal GO TO statement is performed, jumping to a label defined in a separate legacy program procedure, or when a dynamic GO TO statement to a label variable is executed

  • RainCodeLegacyRuntime.Exceptions.Qix.ReturnException implements the CICS RETURN statement, unfolding a stack that can contain an unbound number of called programs

  • RainCodeLegacyRuntime.Exceptions.Qix.XctlException implements the CICS XCTL statement, which is similar in behavior to a CICS RETURN followed by a CICS CALL

  • RainCodeLegacyRuntime.Exceptions.Qix.AbendException implements the CICS ABEND statement.

9.7. Structure Descriptors

When compiling a module, in addition to the usual generation of executable code, the Raincode compilers can produce structure descriptors.

A structure descriptor is essentially an XML files that describe a COBOL or PL/I data structure in terms of its fields, data types, offsets, size, etc.

It explicits the work performed by the compilers to compute offsets, padding and alignment according to the rules at hand (depending on the context, the language, etc.) so that an external tool can deal with these structures without having to reproduce this intelligence.

Since they are in an open format, they can be consumed by Raincode tools as well as user-written utilities.

<?xml version="1.0" encoding="ASCII"?>
<StructDescriptor Name="WS" Size="643">
    <SourceInfo>
        <FileSourceInfo FileName="TEST_REQ1.cbl" LineNr="60" Tool="Raincode COBOL compiler" Version="3.0.0.66">
        </FileSourceInfo>
    </SourceInfo>
    <Fields>
        <Field Name="WS-COL1" TotalSize="2">
            <Type>
                <FixedDecimalTypeInfo Storage="COMP" Digits="4">
                </FixedDecimalTypeInfo>
            </Type>
        </Field>
        <Field Name="WS-COL2" TotalSize="2" Offset="2">
            <Type>
                <FixedDecimalTypeInfo Storage="COMP" Digits="4">
                </FixedDecimalTypeInfo>
            </Type>
        </Field>
        <Field Name="WS-NB-LIGNE" TotalSize="2" Offset="4">
            <Type>
                <FixedDecimalTypeInfo Storage="COMP" Digits="4">
                </FixedDecimalTypeInfo>
            </Type>
        </Field>
        <Field Name="WS-NB-LIGNE-2" TotalSize="3" Offset="6">
            <Type>
                <FixedDecimalTypeInfo Storage="COMP3" Digits="4">
                </FixedDecimalTypeInfo>
            </Type>
        </Field>
        <Field Name="WS-NB-LIGNE-3" TotalSize="2" Offset="9">
            <Type>
                <FixedDecimalTypeInfo Storage="COMP5" Digits="4">
                </FixedDecimalTypeInfo>
            </Type>
        </Field>
        <Field Name="WS-NB-LIGNE-4" TotalSize="8" Offset="11">
            <Type>
                <CobolDisplayNumericTypeInfo Picture="S9999.99">
                </CobolDisplayNumericTypeInfo>
            </Type>
        </Field>
        <Field Name="WS-NB-LIGNE-5" TotalSize="4" Offset="19">
            <Type>
                <PointerTypeInfo>
                </PointerTypeInfo>
           </Type>
        </Field>
        <Field Name="SUB-REC" TotalSize="620" Offset="23">
            <Type>
                <ArrayTypeInfo LowerBound="1" UpperBound="10">
                    <ElementType>
                        <CompositeTypeInfo>
                           <Field Name="TIMEST-DEB" TotalSize="26">
                                <Type>
                                    <FixedStringTypeInfo CharCount="26">
                                    </FixedStringTypeInfo>
                                </Type>
                            </Field>
                            <Field Name="ZIN-DATE" TotalSize="10" Offset="26">
                                <Type>
                                    <FixedStringTypeInfo CharCount="10">
                                    </FixedStringTypeInfo>
                                </Type>
                            </Field>
                            <Field Name="TIMEST-FIN" TotalSize="26" Offset="36">
                                <Type>
                                    <FixedStringTypeInfo CharCount="26">
                                    </FixedStringTypeInfo>
                                </Type>
                            </Field>
                        </CompositeTypeInfo>
                    </ElementType>
                </ArrayTypeInfo>
            </Type>
        </Field>
    </Fields>
</StructDescriptor>

Appendix A: Useful information

This appendix includes some additional useful information concerning Raincode Legacy Compilers.

A.1. Good to know

This section summarizes a number of useful tips, often details where the Devil usually lies that were more convenient to place here, for various reasons.

When executing a compiled program, the program crashes because it does not find RainCodeLegacyRuntime.dll
This is the standard DLL which is required for any compiled legacy program to execute. It should be made available in the GAC (Global Assembly Cache), as explained in the procedure detailed in this document (See section RCLRUN).
Raincode Legacy Compilers stops without notice, and without compiling anything
This may be due to the fact that you are using Raincode Legacy Compilers incremental mode, which causes modules already referred to in the repository not to be compiled (see the Compilation section for more).
Raincode Legacy Compilers issues a warning message "PE Verification will not be performed"
This is caused by the fact that the path to PEVerify is not given. This warning has no impact on the functionality of the compiler. However, please note that the use of PEVerify is strongly encouraged.
Raincode Legacy Compilers issues an error message "Internal error <nr>"
Internal errors relate to cases where you encounter unexpected behavior, or an unsupported feature of Raincode Legacy Compilers. Please report such occurrences to Raincode.
Raincode Legacy Compilers issues "Verification errors"
A .NET portable executable (DLL or EXE) can be verified for consistency, using a standard Microsoft tool named peverify. This consistency check is performed automatically by Raincode Legacy Compilers, and errors, if any, are reported. These are fatal errors that must be reported to Raincode, as the generated executables are typically unusable.
What happens with .NET Exceptions raised during the execution of a compiled program ?
Exceptions can be thrown because of a programming error, or by some library function. Such exceptions are caught by explicit PL/I ON clauses if present. Otherwise, they are captured at the procedure level, and the ExecutionContext.stop function is called. All exceptions are captured this way, except for specific exceptions designed to implement legacy program control flow, inheriting from Exceptions.PliControlException., Exceptions: Exceptions.PliControlException " which are rethrown systematically until being caught. These control flow extensions are used, among others, to implement the CICS RETURN, CICS XCTL and CICS ABEND statements.
How can I force a legacy program to stop from within a library function?
The ExecutionContext.stop() function stops the current legacy program thread, by running a CICS ABEND function with Abcode PLISTOP if running as a QIX process, or stopping plainly otherwise.
What is a repository in the context of Raincode Legacy Compilers?
A repository is a database describing a number of legacy program artifacts gathered by Raincode Legacy Compilers, which does not use this database; it simply populates it with information that can be used and analyzed by separate processes. The repository keeps track of programs, procedures, CICS statements and more. It is described in more detail in this document (See section Raincode Legacy Compilers command-line options).
Is it possible to disable the peverify step of the compilation process?
Yes. The :VerifyIL command-line option defaults to TRUE, and can be used to disable the .NET verification process. However, given the limited impact of this process on the compile-time performance, it is strongly advised not to disable it, since a program that fails this check may have a totally unpredictable behavior, making debugging and troubleshooting abnormally difficult. Any error reported as part of this verification process should be reported as described in this document (See section Code generation).
What is the meaning of warning EXTENDED FLOAT ARITHMETIC TRUNCATED TO DOUBLE PRECISION ONLY ?
This warning occurs when there is usage of a float variable with a necessary storage greater than 8 bytes. There is no available .NET type to store such a long value; therefore it is truncated to a double. Nevertheless, the allocated storage space remains 16 bytes but only 8 are used.
Can two execution contexts share an address space ?
No. Each address space is meant to be used by one execution context, and each execution context must use one address space. They are represented as separate classes for clearer separation of concern while formally; both concepts could be represented by a single class.
What is the difference between COBOL and PL/I in Raincode Legacy Compilers?
For COBOL only: when compiling a program that contains at least one SQL statement, Raincode Legacy Compilers declare a variable named SQL-INIT-FLAG. This variable is declared for compatibility purposes only. It has no effect on the generated code’s behavior.
What is a wired builtin?
A wired builtin is a PL/I builtin that has a specific behavior that goes beyond a plain function. For instance, SUBSTR and STRING can be used as left-side for assignments, with very specific type-dependent semantics, up to a point where they must be implemented natively by Raincode Legacy Compilers
This document does not list the available builtins
Running the Raincode Legacy Compilers with the :Builtins command-line option produces the lists of all available builtins, indicating their name, their nature (wired or internal) and their status (Inactive, On demand or Implied). For more information on the way Raincode Legacy Compilers deals with builtins, see the relevant information in this document (See section Raincode Legacy Compilers command-line options ).
When running a program accessing a SQL Server database, I get an InvalidOperationException with the error There is already an open DataReader associated with this command which must be closed first
This occurs when multiple cursors are open at the same time. To enable opening more than one cursor at a given time, you must enable the MARS (Multiple Active Result Sets) feature. To do so, append MultipleActiveResultSets=True to the connection string.
Getting error rc.exe not found while installing the Raincode legacy compliers installation
This error occurs when you miss selecting the Windows 10 SDK from the individual component while installing Visual Studio on your system.

A.2. Best practices

This section describes how the Raincode Legacy Compilers can best be used from a project management and architectural point of view. Some of the items described here can be considered as generic good practice that would apply in virtually any environment, while some are more specific to the technicalities of the Raincode tools.

A.2.1. Optimize debugged code, rather than debug optimized code

Talking about generic good practice, this is as generic as it gets. In migration projects, as in any other project, first focus on getting the functionality right, before looking at whether the performance is adequate. This generic best practice is applied in many ways:

  • Don’t optimize too early

  • Don’t optimize blindly

  • Don’t optimize unless you need to

  • Don’t optimize your software if you can achieve your performance target by using cheaper (or even, sometimes, not so cheap) hardware

  • Don’t go to the burden of going for static SQL before ensuring that the simpler, easier to deploy, dynamic SQL cannot meet the target performance figures.

A.2.2. Prepare your environment

Mainframe compilers can be configured to be very tolerant of errors. If your code depends on being compiled with severe errors made non-fatal, the risks are high that there will be compilation and/or execution problems. If possible, the code should compile without errors. Reaching a warning-free state for the sake of migration isn’t necessary.

Long code freezes are generally not possible. Plan for regular or incremental exports of the codebase and test data.

Validation of the programs needs care. Databases needs to be restored to a proper state, input datasets need to be available. Intermediate states may need to be exported from the mainframe to break data dependencies and allow testing even while some programs may not yet compile.

Be careful of dependencies. It is not unknown on mainframes for programs to not be recompiled when a shared dependency (copybook/include) is modified. This can make some programs non-compilable or change their behavior compared to the one running on the mainframe. Dealing with this problem may require recompilation on the mainframe or export of the complete source history.

Export the metadata with the raw source. Last modification date or last modifier name can be invaluable information to understand a program. Avoid changing the code during the export. A reversible mapping to an extended ASCII character set should be enough. In particular, if the source is maintained in an environment that supports substitutions, file inclusion, or explicit extra metadata (language, compiler options, etc.), Don’t remove them or substitute similar COBOL or PL/I constructs during the export. Such transformations can easily be done later and should the extra information be needed or the transformation adjusted, it won’t require re-exporting everything.

A.2.3. Keep compilation under control

Keep your sources, configuration files, custom tools and scripts in a version control system. Ideally, it should be able to version whole trees and to give strong names to revisions.

Write response files for Raincode compilers. Use at least one for common options, one per group of programs needing similar compiler options, one for each subsystem (list of related programs to compile.) Response files can also include other response files.

Log all output of the compiler. Raincode recommends that you also log the command-line used, the content or version of response files (including those indirectly used) , the version of the compiler that was used (you may use the :BuildInfo option for this) and the version of the source files. Should you need our help, reproducible problems are crucial for a fast fix.

Use a compilation database. The database provides extra information and is easier to query than the logs. Compile from the original sources. If your sources need custom processing before being compiled, use a script to combine the two steps to avoid missing updates to some files.

A.2.4. Stay focused

Migrating a codebase forces you to look at lots of programs. It is tempting to refactor old code and cleanup architectures that have evolved significantly. Raincode strongly recommends you avoid non-critical changes unrelated to the migration before the new system is put in production.

Keep metrics on program status. Programs that were compiled and tested should continue to be built and tested regularly to detect regressions. Programs that are deemed unnecessary to migrate should be removed from the build and categorized as such.

A.2.5. Use home-grown runners

The Raincode Legacy compilers come with a fully functional runner (see section rclrun command-line options) that can be used to run compiled COBOL and PL/I programs while controlling the environment in which they execute.

It is our experience, though, that most deployments of the Raincode Legacy compilers have ended using a bespoke runner, generally inheriting from the BaseRunner class from the runtime library (See ModuleDictionary ), so that one can control how programs are started, how resources are allocated, where basic parameters comes from (such as the CICS COMMAREA) and more.

A.2.6. Subclass execution context to pass information across COBOL, PL/I or C# programs

When information must be passed from the execution environment (runner or variation thereof) to primitives, the preferred way is to subclass the ExecutionContext class and extend it to keep track of whatever information must be maintained.

The execution contexts are passed along to virtually all primitives, builtins, hooks for CICS or SQL implementations and more. One can then cast them to the appropriate subclass to access the information. There are other ways of making data available across the architecture. For instance, when data must be made available to COBOL and PL/I programs without having the possibility to send it through parameters, the ExecutionContext supports the ability to define shared variables and give them values. These variables can be accessed from within COBOL and PL/I programs in the same manner as COBOL and PL/I shared variables. If the final recipient of this information is not a COBOL or PL/I program, subclassing the execution context is obviously a more elegant solution.

runner link

A.2.7. In COBOL, use :NoTrunc for optimal performance when dealing with binary values

The default behavior for COBOL programs is to implement the exact rounding and truncation implied by the PICTURE clauses attached to variable declarations. However, when dealing with technical, non-financial data (indexes, counters, etc.) implemented in binary data formats (COMP and COMP-5), these roundings and truncations often make little semantic sense while they can have a serious impact on the resulting performance.

The :NoTrunc command-line option simplifies the arithmetic of such data items, as COMP and COMP-5 variables are then truncated according to their storage, independently of their explicit PICTURE clauses. In other words, a COMP-5 PIC 9(6) will be stored as a signed 32 bits data item, and will be truncated as a signed 32 bits data item even though its declaration states that its range goes from 0 to 999999.

The :NoTrunc command-line option can have a spectacular effect on performance for applications that depend heavily on binary data values.

A.2.8. Do not abuse threading

ExecutionContexts (See ExecutionContext) provide guaranteed isolation between threads, and can be used to have multiple PL/I or COBOL programs running simultaneously. However, multi-threaded systems have caveats of their own, as they have to fight for resources and even more importantly, can be stopped during garbage collections phases, which at some point must pause all threads.

Therefore, except for cases where the multiple threads must exchange information under strong performance requirements, it may be safer not to overuse threads, and use separate processes (each running a limited number of threads) instead. It may require more memory however memory is affordable these days.

Using separate processes, with separate address spaces, will allow the garbage collector to run on one process without impacting others, instead of blocking all threads in a multi-threaded architecture, even if for a small amount of time.

Besides, a multi-process architecture (as opposed to a multi-threaded architecture) is easier to deploy on truly distributed systems, yielding more scalability than multi-threaded systems that are bound to a single address space.

A.2.9. Alter the source code in last resort

Although the Raincode legacy compilers aim at compiling your existing COBOL and PL/I code with no changes whatsoever, there are some cases (hopefully, few) where this is simply impossible.

As an example, the SQL preprocessor on the mainframe will accept statements with incorrect syntax such as unbalanced parenthesis in some expressions. While Raincode compilers aim at this level of compatibility, it cannot be guaranteed in all cases, due to differences in parsing technologies. In some extreme cases, code will have to be corrected.

However, alterations of the existing source code should be considered as a last resort approach. Using the various preprocessing facilities provided as part of the Raincode legacy compilers, the ability to define builtin (or intrinsic) functions and other similar features, one can often manage and simulate the original environment to have programs compiling and executing without changes.

When no such facility can do the trick, before starting to change the programs (even more so if the change is pervasive, complicated or entropic), please contact Raincode to see whether the compiler can be altered or extended to support the offending source code.

A.2.10. Maintain a single source

If the existing source code must be altered, it is very important to keep the alteration compatible with both environments (the original mainframe environment on the one hand and the Raincode legacy compilers on the other). The following techniques can be used. They are listed with the most maintainable first.

Alter the code to be acceptable to both compilers. Most of the needed changes will be to code that doesn’t strictly conform to the language definition. Simply fixing errors is often enough to make code portable between the two compilers.

Use conditional compilation in the problematic source files. This keeps both versions of the code close to each other, reducing the risks that maintenance will be done on only one version and avoiding common code duplication. Extract the problematic parts to another file (copybook/include) that will be provided in two different versions (one per compiler). This is the most generic but least maintainable solution and should only be considered if the previous solutions are impractical. Measures must be taken to ensure that the two versions are kept synchronized. In any case, forking the portfolio into two mutually incompatible branches should be avoided at all cost.

A.2.11. Use C# instead of COBOL or PL/I for system issues

The Raincode legacy compilers are meant to compile your existing application code. Any infrastructure code around it should be developed and maintained using a more modern and native .NET language such as C#, F# or VB.NET.

A.2.12. Raincode Language Service Visual Studio extension is not Functional

Raincode Language Service Visual Studio extension is not functional when the installation of Visual Studio© is minimal (i.e. no workload is selected in the workload selection tab of Visual Studio© installer).

Run the Visual Studio© Installer and select the .NET desktop development and click the Modify button.

vsplugininstall

Appendix B: List of Warnings

You may use the :Warnings compilation flag to toggle warnings or to turn them into errors. See Repository.

B.1. Common Warnings

Number Description

80

String truncation may occur

90

Invalid module name

100

Name mismatch between module and main procedure name

120

Not supported: external symbol mapping

130

Interface Manager warning

140

Unnamed field in structure is not expanded

200

Indicator size does not match the size of the data structure

201

No SQL target selected

Error by default

210

Undefined cursor name

211

Undefined cursor name

Error by default

220

SQL: Cursor will be held for SQL Server

230

BEGIN/END DECLARE SECTION ignored

240

No definition found

250

SQL Server: Not supported FOR clause

Error by default

260

Mismatch between number of columns and host variables

Error by default

270

Alias for INTO target changed in ORDER BY

280

SQL Server: Not supported cursor option

290

SQL target does not support rowset DELETE/UPDATE

300

Unrecognized CICS command

Error by default

301

Unsupported CICS parameter

Error by default

310

Invalid CICS flag

Error by default

311

Invalid CICS parameters

Error by default

350

SQL Server: Locks are not retained on rollback

Error by default

360

SQL Server: Meta data unavailable for rewriting

370

SQL Server: Unsupported include columns in MERGE

380

DECLARE TABLE ignored

381

SQL: DECLARE STATEMENT ignored

390

SQL: Incorrect number of operations on tables

Error by default

700

Passing variable between ( ), name will be used. Specify variable between to use content of variable

B.2. cobrc.exe Warnings

Number Description

1010

Name clash with a previous field

1011

Name clash with a previous field (filler)

1015

Ambiguous resolution

Error by default

1016

Comparison result may depend on collating sequence

Disabled by default

1040

Deprecated Special Register

1050

Duplicate declaration

1020

EXIT should be the sole statement in the paragraph

1030

Unrecognizable IDENTIFICATION DIVISION. Using source file name radical

1060

Suppressed implicit CICS parameter

1070

Ignored VALUE clause because of conflicting REDEFINES

1080

Dubious paragraph, possible parsing error

1100

No PICTURE clause found. Assuming PICTURE X(1)

1200

Suspicious consecutive NOT found

Error by default

1210

Not implemented level 88 helper

1300

Condition is mixed with abreviation condition

Error by default

1400

Operator are not valid in parenthesised abbreviation condition

Error by default

1500

Missing END PROGRAM

1510

Program name in END PROGRAM does not match the name in the PROGRAM-ID clause

Error by default

1061

Parameter is too small to hold the output data

2000

Strict IBM: Zero length string, one space is assumed

Error by default

2010

Strict IBM: Missing period after data declaration

Error by default

2020

Strict IBM: Empty USING clause

Error by default

2030

Strict IBM: Erroneous VALUE clause

Error by default

2040

Strict IBM: Erroneous PICTURE clause

Error by default

2050

Strict IBM: Identifier exceeds 30 characters

Error by default

2060

Strict IBM: Should start in zone A

Error by default

2070

Strict IBM: Should start in zone B

Error by default

2080

Strict IBM: missing operator in condition

Error by default

2090

Strict IBM: string literal must be closed

Error by default

2100

NOT keyword found with >= or ⇐

Error by default

2180

Period missing after the paragraph’s name

2181

Period is required at the end of top level statement list

2182

Strict IBM : WITH is invalid in initialize

Error by default

1090

the value is too long in respect to the size of the variable

3000

Error by default

2120

Numeric function call not allowed in MOVE

Error by default

2130

Special register is a non-IBM extension

Error by default

2110

Unsupported extension

Error by default

4000

Unsupported I/O option

2140

FROM is before INTO in SQL SELECT

Error by default

2150

Missing DATA DIVISION keywords

Error by default

2160

Missing CONFIGURATION SECTION keywords

Error by default

2170

Invalid argument

Error by default

4200

Data field storage changed to COMP

4242

4300

XML parse VALIDATING not implemented

1680

END clause not authorized on RANDOM access mode

Error by default

1651

Obsolete statement ignored

1690

Deceptive indenting: statement following improperly terminated nested IF may not be executed

B.3. plirc.exe Warnings

Number Description

1010

Multi-line string

1020

Missing parameter

1030

Assuming CONNECTED, because it is a parameter in a NODESCRIPTOR entry

1040

Parameter name appears more than once, only the first occurence is used

Error by default

1050

Local ambiguity in data reference

1060

Poorly qualified symbol

1070

Implicit declaration

1071

Implicit EXTERNAL ENTRY declaration

Error by default

1090

No defining declaration

1100

Too many initializers

1110

OFFSET attribute is ignored

1120

BLOCKSIZE option is ignored

1130

BUFFERS option is ignored

1135

MEDIUM option is ignored

1140

RELEASE statements are ignored

1160

Predefined option ignored

1170

Extended float arithmetic truncated to double precision only

1185

Mixed numeric/non numeric comparison

Disabled by default

1200

Bit string promotion to float

1210

The FLOAT builtin always casts to native 64bits floats

1220

Invalid array of CHAR, may NOT behave correctly

1230

Dynamically validated array compliance

1240

Using a whitespace string literal as an empty string literal

1250

FETCH statement with a TITLE clause

1270

Invalib bounds for SUBSTR

1280

Constants in LABEL attribute are ignored

1290

No common field found for BY NAME operation

Error by default

1300

Arity error

Error by default

1320

fetch/release argument is not an external entry constant

1330

Unsupported PROCESS statement

1340

Data reference subscript in put data item will not be printed

1370

HEX string literal expects an even number of characters

Error by default

1380

Unpredictable results might occur if the base variable is not on a byte boundary

1390

Index out of bound

Error by default

1400

LIKE on DEFINE STRUCTURE ignored

1570

Variable is non-assignable

Error by default

2010

Unlikely variable declaration

2020

CSTG returns a constant value

2030

Oddly placed declaration or procedure

3100

Missing INTO clause in CICS RECEIVE statement

9010

Malformed DO-group (Implicit END)

9011

Malformed DO-group (Missing semicolon)

9012

Malformed SELECT statement

9015

Malformed declaration (Missing semicolon)

9016

Malformed definition (Missing semicolon)

9017

Malformed assignment (Missing semicolon)

9018

DO-loop will execute only once (a semicolon after the DO may be missing)

9020

Additional star argument ignored

9030

RETURN statement should take an argument

9031

RETURN statement should not take an argument

9040

The procedure returns a value, cannot be called by a CALL

9050

Float types may not specify precision in MULTIPLY/ADD/DIVIDE/SUBTRACT

9100

ALLOCATE overrides the declaration’s static type

9110

Missing closing parenthese

9130

Mutually exclusive attributes in declaration

9140

Inserting missing comma

9150

Malformed WHEN clause (Missing right parenthesis)

9160

Invalid option in IO statement; using KEY option

9170

Attributes for based variable invalid in ALLOCATE

10010

Unused procedure

10020

Unused variable

10030

Unused parameter

Appendix C: List of Hlasmrc instructions, Macros and SVCs

C.1. Hlasmrc Instructions

There are 955 instructions in total.

There are 420 implemented instructions:

Mnemonic

Description

A

ADD (32)

AD

ADD NORMALIZED (long HFP)

ADB

ADD (long BFP)

ADBR

ADD (long BFP)

ADR

ADD NORMALIZED (long HFP)

AE

ADD NORMALIZED (short HFP)

AEB

ADD (short BFP)

AEBR

ADD (short BFP)

AER

ADD NORMALIZED (short HFP)

AFI

ADD IMMEDIATE (32)

AG

ADD (64)

AGF

ADD (64←32)

AGFI

ADD IMMEDIATE (64←32)

AGFR

ADD (64←32)

AGH

ADD HALFWORD (64←16)

AGHI

ADD HALFWORD IMMEDIATE (64)

AGHIK

ADD IMMEDIATE (64←16)

AGR

ADD (64)

AGRK

ADD (64)

AGSI

ADD IMMEDIATE (64←8)

AH

ADD HALFWORD

AHI

ADD HALFWORD IMMEDIATE (32)

AHIK

ADD IMMEDIATE (32←16)

AHY

ADD HALFWORD

AL

ADD LOGICAL (32)

ALC

ADD LOGICAL WITH CARRY (32)

ALCG

ADD LOGICAL WITH CARRY (64)

ALCGR

ADD LOGICAL WITH CARRY (64)

ALCR

ADD LOGICAL WITH CARRY (32)

ALFI

ADD LOGICAL IMMEDIATE (32)

ALG

ADD LOGICAL (64)

ALGF

ADD LOGICAL (64←32)

ALGFI

ADD LOGICAL IMMEDIATE (64←32)

ALGFR

ADD LOGICAL (64←32)

ALGR

ADD LOGICAL (64)

ALGRK

ADD LOGICAL (64)

ALR

ADD LOGICAL (32)

ALRK

ADD LOGICAL (32)

ALY

ADD LOGICAL (32)

AP

ADD DECIMAL

AR

ADD (32)

ARK

ADD (32)

ASI

ADD IMMEDIATE (32←8)

AU

ADD UNNORMALIZED (short HFP)

AUR

ADD UNNORMALIZED (short HFP)

AW

ADD UNNORMALIZED (long HFP)

AWR

ADD UNNORMALIZED (long HFP)

AY

ADD (32)

BAKR

BRANCH AND STACK

BAL

BRANCH AND LINK

BALR

BRANCH AND LINK

BAS

BRANCH AND SAVE

BASR

BRANCH AND SAVE

BASSM

BRANCH AND SAVE AND SET MODE

BC

BRANCH ON CONDITION

BCR

BRANCH ON CONDITION

BCT

BRANCH ON COUNT (32)

BCTG

BRANCH ON COUNT (64)

BCTGR

BRANCH ON COUNT (64)

BCTR

BRANCH ON COUNT (32)

BRAS

BRANCH RELATIVE AND SAVE

BRC

BRANCH RELATIVE ON CONDITION

BRCL

BRANCH RELATIVE ON CONDITION LONG

BRCT

BRANCH RELATIVE ON COUNT (32)

BRCTG

BRANCH RELATIVE ON COUNT (64)

BRCTH

BRANCH RELATIVE ON COUNT HIGH (32)

BSM

BRANCH AND SET MODE

BXH

BRANCH ON INDEX HIGH (32)

BXLE

BRANCH ON INDEX LOW OR EQUAL (32)

C

COMPARE (32)

CD

COMPARE (long HFP)

CDB

COMPARE (long BFP)

CDBR

COMPARE (long BFP)

CDR

COMPARE (long HFP)

CDS

COMPARE DOUBLE AND SWAP (32)

CDSG

COMPARE DOUBLE AND SWAP (64)

CDSY

COMPARE DOUBLE AND SWAP (32)

CE

COMPARE (short HFP)

CEB

COMPARE (short BFP)

CEBR

COMPARE (short BFP)

CER

COMPARE (short HFP)

CFI

COMPARE IMMEDIATE (32)

CG

COMPARE (64)

CGF

COMPARE (64←32)

CGFI

COMPARE IMMEDIATE (64←32)

CGFR

COMPARE (64←32)

CGFRL

COMPARE RELATIVE LONG (64←32)

CGH

COMPARE HALFWORD (64←16)

CGHI

COMPARE HALFWORD IMMEDIATE (64←16)

CGHSI

COMPARE HALFWORD IMMEDIATE (64←16)

CGR

COMPARE (64)

CGRL

COMPARE RELATIVE LONG (64)

CH

COMPARE HALFWORD (32←16)

CHHSI

COMPARE HALFWORD IMMEDIATE (16)

CHI

COMPARE HALFWORD IMMEDIATE (32←16)

CHSI

COMPARE HALFWORD IMMEDIATE (32←16)

CHY

COMPARE HALFWORD (32←16)

CKSM

CHECKSUM

CL

COMPARE LOGICAL (32)

CLC

COMPARE LOGICAL (character)

CLCL

COMPARE LOGICAL LONG

CLFHSI

COMPARE LOGICAL IMMEDIATE (32←16)

CLFI

COMPARE LOGICAL IMMEDIATE (32)

CLG

COMPARE LOGICAL (64)

CLGF

COMPARE LOGICAL (64←32)

CLGFI

COMPARE LOGICAL IMMEDIATE (64←32)

CLGFR

COMPARE LOGICAL (64←32)

CLGFRL

COMPARE LOGICAL RELATIVE LONG (64←32)

CLGHRL

COMPARE LOGICAL RELATIVE LONG (64←16)

CLGHSI

COMPARE LOGICAL IMMEDIATE (64←16)

CLGR

COMPARE LOGICAL (64)

CLGRL

COMPARE LOGICAL RELATIVE LONG (64)

CLHF

COMPARE LOGICAL HIGH (32)

CLHHR

COMPARE LOGICAL HIGH (32)

CLHHSI

COMPARE LOGICAL IMMEDIATE (16)

CLHLR

COMPARE LOGICAL HIGH (32)

CLHRL

COMPARE LOGICAL RELATIVE LONG (32←16)

CLI

COMPARE LOGICAL (immediate)

CLIH

COMPARE LOGICAL IMMEDIATE HIGH (32)

CLIY

COMPARE LOGICAL (immediate)

CLM

COMPARE LOGICAL CHAR. UNDER MASK (low)

CLMH

COMPARE LOGICAL CHAR. UNDER MASK (high)

CLMY

COMPARE LOGICAL CHAR. UNDER MASK (low)

CLR

COMPARE LOGICAL (32)

CLRL

COMPARE LOGICAL RELATIVE LONG (32)

CLY

COMPARE LOGICAL (32)

CP

COMPARE DECIMAL

CR

COMPARE (32)

CRL

COMPARE RELATIVE LONG (32)

CS

COMPARE AND SWAP (32)

CSG

COMPARE AND SWAP (64)

CSY

COMPARE AND SWAP (32)

CVB

CONVERT TO BINARY (32)

CVBY

CONVERT TO BINARY (32)

CVD

CONVERT TO DECIMAL (32)

CVDY

CONVERT TO DECIMAL (32)

CY

COMPARE (32)

D

DIVIDE (32←64)

DD

DIVIDE (long HFP)

DDB

DIVIDE (long BFP)

DDBR

DIVIDE (long BFP)

DDR

DIVIDE (long HFP)

DE

DIVIDE (short HFP)

DEB

DIVIDE (short BFP)

DEBR

DIVIDE (short BFP)

DER

DIVIDE (short HFP)

DL

DIVIDE LOGICAL (32←64)

DLG

DIVIDE LOGICAL (64←128)

DLGR

DIVIDE LOGICAL (64←128)

DLR

DIVIDE LOGICAL (32←64)

DP

DIVIDE DECIMAL

DR

DIVIDE (32←64)

ED

EDIT

EDMK

EDIT AND MARK

EREG

EXTRACT STACKED REGISTERS (32)

EREGG

EXTRACT STACKED REGISTERS (64)

EX

EXECUTE

EXRL

EXECUTE RELATIVE LONG

FIDR

LOAD FP INTEGER (long HFP)

HDR

HALVE (long HFP)

IC

INSERT CHARACTER

ICM

INSERT CHARACTERS UNDER MASK (low)

ICMH

INSERT CHARACTERS UNDER MASK (high)

ICMY

INSERT CHARACTERS UNDER MASK (low)

ICY

INSERT CHARACTER

IIHF

INSERT IMMEDIATE (high)

IIHH

INSERT IMMEDIATE (high high)

IIHL

INSERT IMMEDIATE (high low)

IILF

INSERT IMMEDIATE (low)

IILH

INSERT IMMEDIATE (low high)

IILL

INSERT IMMEDIATE (low low)

IPM

INSERT PROGRAM MASK

L

LOAD (32)

LA

LOAD ADDRESS

LAA

LOAD AND ADD (32)

LAAG

LOAD AND ADD (64)

LARL

LOAD ADDRESS RELATIVE LONG

LAY

LOAD ADDRESS

LB

LOAD BYTE (32)

LBR

LOAD BYTE (32)

LCDR

LOAD COMPLEMENT (long HFP)

LCR

LOAD COMPLEMENT (32)

LD

LOAD (long)

LDR

LOAD (long)

LDY

LOAD (long)

LE

LOAD (short)

LER

LOAD (short)

LEY

LOAD (short)

LG

LOAD (64)

LGB

LOAD BYTE (64)

LGBR

LOAD BYTE (64)

LGF

LOAD (64←32)

LGFI

LOAD IMMEDIATE (64←32)

LGFR

LOAD (64←32)

LGFRL

LOAD RELATIVE LONG (64←32)

LGH

LOAD HALFWORD (64)

LGHI

LOAD HALFWORD IMMEDIATE (64)

LGHR

LOAD HALFWORD (64)

LGHRL

LOAD HALFWORD RELATIVE LONG (64←16)

LGR

LOAD (64)

LGRL

LOAD RELATIVE LONG (64)

LH

LOAD HALFWORD (32)

LHH

LOAD HALFWORD HIGH (32←16)

LHI

LOAD HALFWORD IMMEDIATE (32)

LHR

LOAD HALFWORD (32)

LHRL

LOAD HALFWORD RELATIVE LONG (32←16)

LHY

LOAD HALFWORD (32)

LLC

LOAD LOGICAL CHARACTER (32)

LLCH

LOAD LOGICAL CHARACTER HIGH (32←8)

LLCR

LOAD LOGICAL CHARACTER (32)

LLGC

LOAD LOGICAL CHARACTER (64)

LLGCR

LOAD LOGICAL CHARACTER (64)

LLGF

LOAD LOGICAL (64←32)

LLGFR

LOAD LOGICAL (64←32)

LLGH

LOAD LOGICAL HALFWORD (64)

LLGHR

LOAD LOGICAL HALFWORD (64)

LLGHRL

LOAD LOGICAL HALFWORD RELATIVE LONG (64←16)

LLGT

LOAD LOGICAL THIRTY ONE BITS

LLGTR

LOAD LOGICAL THIRTY ONE BITS

LLH

LOAD LOGICAL HALFWORD (32)

LLHH

LOAD LOGICAL HALFWORD HIGH (32←16)

LLHR

LOAD LOGICAL HALFWORD (32)

LLHRL

LOAD LOGICAL HALFWORD RELATIVE LONG (32←16)

LLIHF

LOAD LOGICAL IMMEDIATE (high)

LLIHH

LOAD LOGICAL IMMEDIATE (high high)

LLIHL

LOAD LOGICAL IMMEDIATE (high low)

LLILF

LOAD LOGICAL IMMEDIATE (low)

LLILH

LOAD LOGICAL IMMEDIATE (low high)

LLILL

LOAD LOGICAL IMMEDIATE (low low)

LM

LOAD MULTIPLE (32)

LMY

LOAD MULTIPLE (32)

LNDR

LOAD NEGATIVE (long HFP)

LNR

LOAD NEGATIVE (32)

LPDR

LOAD POSITIVE (long HFP)

LPGFR

LOAD POSITIVE (64←32)

LPGR

LOAD POSITIVE (64)

LPR

LOAD POSITIVE (32)

LR

LOAD (32)

LRL

LOAD RELATIVE LONG (32)

LT

LOAD AND TEST (32)

LTDR

LOAD AND TEST (long HFP)

LTR

LOAD AND TEST (32)

LY

LOAD (32)

M

MULTIPLY (64←32)

MAD

MULTIPLY AND ADD (long HFP)

MADR

MULTIPLY AND ADD (long HFP)

MD

MULTIPLY (long HFP)

MDB

MULTIPLY (long BFP)

MDBR

MULTIPLY (long BFP)

MDE

MULTIPLY (short to long HFP)

MDEB

MULTIPLY (short to long BFP)

MDEBR

MULTIPLY (short to long BFP)

MDER

MULTIPLY (short to long HFP)

MDR

MULTIPLY (long HFP)

MEE

MULTIPLY (short HFP)

MEEB

MULTIPLY (short BFP)

MEEBR

MULTIPLY (short BFP)

MEER

MULTIPLY (short HFP)

MFY

MULTIPLY (64←32)

MGH

MULTIPLY HALFWORD (64←16)

MGHI

MULTIPLY HALFWORD IMMEDIATE (64)

MH

MULTIPLY HALFWORD (32)

MHI

MULTIPLY HALFWORD IMMEDIATE (32)

MHY

MULTIPLY HALFWORD (32)

ML

MULTIPLY LOGICAL (64←32)

MLG

MULTIPLY LOGICAL (128←64)

MLGR

MULTIPLY LOGICAL (128←64)

MLR

MULTIPLY LOGICAL (64←32)

MP

MULTIPLY DECIMAL

MR

MULTIPLY (64←32)

MVC

MOVE (character)

MVCIN

MOVE INVERSE

MVCL

MOVE LONG

MVGHI

MOVE (64←16)

MVHHI

MOVE (16←16)

MVHI

MOVE (32←16)

MVI

MOVE (immediate)

MVIY

MOVE (immediate)

MVN

MOVE NUMERICS

MVO

MOVE WITH OFFSET

MVZ

MOVE ZONES

N

AND (32)

NC

AND (character)

NG

AND (64)

NGR

AND (64)

NGRK

AND (64)

NI

AND (immediate)

NIHF

AND IMMEDIATE (high)

NIHH

AND IMMEDIATE (high high)

NIHL

AND IMMEDIATE (high low)

NILF

AND IMMEDIATE (low)

NILH

AND IMMEDIATE (low high)

NILL

AND IMMEDIATE (low low)

NIY

AND (immediate)

NR

AND (32)

NRK

AND (32)

NY

AND (32)

O

OR (32)

OC

OR (character)

OG

OR (64)

OGR

OR (64)

OGRK

OR (64)

OI

OR (immediate)

OIHF

OR IMMEDIATE (high)

OIHH

OR IMMEDIATE (high high)

OIHL

OR IMMEDIATE (high low)

OILF

OR IMMEDIATE (low)

OILH

OR IMMEDIATE (low high)

OILL

OR IMMEDIATE (low low)

OIY

OR (immediate)

OR

OR (32)

ORK

OR (32)

OY

OR (32)

PACK

PACK

PR

PROGRAM RETURN

RLL

ROTATE LEFT SINGLE LOGICAL (32)

RLLG

ROTATE LEFT SINGLE LOGICAL (64)

S

SUBTRACT (32)

SAM24

SET ADDRESSING MODE (24)

SAM31

SET ADDRESSING MODE (31)

SAM64

SET ADDRESSING MODE (64)

SD

SUBTRACT NORMALIZED (long HFP)

SDB

SUBTRACT (long BFP)

SDBR

SUBTRACT (long BFP)

SDR

SUBTRACT NORMALIZED (long HFP)

SE

SUBTRACT NORMALIZED (short HFP)

SEB

SUBTRACT (short BFP)

SEBR

SUBTRACT (short BFP)

SER

SUBTRACT NORMALIZED (short HFP)

SG

SUBTRACT (64)

SGF

SUBTRACT (64←32)

SGFR

SUBTRACT (64←32)

SGR

SUBTRACT (64)

SGRK

SUBTRACT (64)

SH

SUBTRACT HALFWORD

SHY

SUBTRACT HALFWORD

SL

SUBTRACT LOGICAL (32)

SLA

SHIFT LEFT SINGLE (32)

SLAG

SHIFT LEFT SINGLE (64)

SLAK

SHIFT LEFT SINGLE (32)

SLDA

SHIFT LEFT DOUBLE

SLDL

SHIFT LEFT DOUBLE LOGICAL

SLFI

SUBTRACT LOGICAL IMMEDIATE (32)

SLG

SUBTRACT LOGICAL (64)

SLGF

SUBTRACT LOGICAL (64←32)

SLGFI

SUBTRACT LOGICAL IMMEDIATE (64←32)

SLGFR

SUBTRACT LOGICAL (64←32)

SLGR

SUBTRACT LOGICAL (64)

SLGRK

SUBTRACT LOGICAL (64)

SLL

SHIFT LEFT SINGLE LOGICAL (32)

SLLG

SHIFT LEFT SINGLE LOGICAL (64)

SLLK

SHIFT LEFT SINGLE LOGICAL (32)

SLR

SUBTRACT LOGICAL (32)

SLRK

SUBTRACT LOGICAL (32)

SP

SUBTRACT DECIMAL

SPM

SET PROGRAM MASK

SR

SUBTRACT (32)

SRA

SHIFT RIGHT SINGLE (32)

SRAG

SHIFT RIGHT SINGLE (64)

SRAK

SHIFT RIGHT SINGLE (32)

SRDA

SHIFT RIGHT DOUBLE

SRDL

SHIFT RIGHT DOUBLE LOGICAL

SRK

SUBTRACT (32)

SRL

SHIFT RIGHT SINGLE LOGICAL (32)

SRLG

SHIFT RIGHT SINGLE LOGICAL (64)

SRLK

SHIFT RIGHT SINGLE LOGICAL (32)

SRP

SHIFT AND ROUND DECIMAL

ST

STORE (32)

STC

STORE CHARACTER

STCH

STORE CHARACTER HIGH (8)

STCK

STORE CLOCK

STCM

STORE CHARACTERS UNDER MASK (low)

STCMH

STORE CHARACTERS UNDER MASK (high)

STCMY

STORE CHARACTERS UNDER MASK (low)

STCY

STORE CHARACTER

STD

STORE (long)

STDY

STORE (long)

STE

STORE (short)

STEY

STORE (short)

STG

STORE (64)

STGRL

STORE RELATIVE LONG (64)

STH

STORE HALFWORD

STHH

STORE HALFWORD HIGH (16)

STHRL

STORE HALFWORD RELATIVE LONG

STHY

STORE HALFWORD

STM

STORE MULTIPLE (32)

STMG

STORE MULTIPLE (64)

STMH

STORE MULTIPLE HIGH

STMY

STORE MULTIPLE (32)

STRL

STORE RELATIVE LONG (32)

STY

STORE (32)

SU

SUBTRACT UNNORMALIZED (short HFP)

SUR

SUBTRACT UNNORMALIZED (short HFP)

SVC

SUPERVISOR CALL

SW

SUBTRACT UNNORMALIZED (long HFP)

SWR

SUBTRACT UNNORMALIZED (long HFP)

SY

SUBTRACT (32)

TAM

TEST ADDRESSING MODE

TM

TEST UNDER MASK

TMHH

TEST UNDER MASK (high high)

TMHL

TEST UNDER MASK (high low)

TMLH

TEST UNDER MASK (low high)

TMLL

TEST UNDER MASK (low low)

TMY

TEST UNDER MASK

TR

TRANSLATE

TRT

TRANSLATE AND TEST

TS

TEST AND SET

UNPK

UNPACK

X

EXCLUSIVE OR (32)

XC

EXCLUSIVE OR (character)

XG

EXCLUSIVE OR (64)

XGR

EXCLUSIVE OR (64)

XGRK

EXCLUSIVE OR (64)

XI

EXCLUSIVE OR (immediate)

XILF

EXCLUSIVE OR IMMEDIATE (low)

XIY

EXCLUSIVE OR (immediate)

XR

EXCLUSIVE OR (32)

XRK

EXCLUSIVE OR (32)

XY

EXCLUSIVE OR (32)

ZAP

ZERO AND ADD

There are 401 notImplemented instructions:

Mnemonic

Description

ADTR

ADD (long DFP)

ALGHSIK

ADD LOGICAL WITH SIGNED IMMEDIATE (64←16)

ALGSI

ADD LOGICAL WITH SIGNED IMMEDIATE (64←8)

ALHSIK

ADD LOGICAL WITH SIGNED IMMEDIATE (32←16)

ALSI

ADD LOGICAL WITH SIGNED IMMEDIATE (32←8)

ALSIH

ADD LOGICAL WITH SIGNED IMMEDIATE HIGH (32)

ALSIHN

ADD LOGICAL WITH SIGNED IMMEDIATE HIGH (32)

AXBR

ADD (extended BFP)

AXR

ADD NORMALIZED (extended HFP)

AXTR

ADD (extended DFP)

BPP

BRANCH PREDICTION PRELOAD

BPRP

BRANCH PREDICTION RELATIVE PRELOAD

BRASL

BRANCH RELATIVE AND SAVE LONG

BRXH

BRANCH RELATIVE ON INDEX HIGH (32)

BRXLE

BRANCH RELATIVE ON INDEX LOW OR EQ. (32)

BSA

BRANCH AND SET AUTHORITY

BSG

BRANCH IN SUBSPACE GROUP

CDFBR

CONVERT FROM FIXED (32 to long BFP)

CDFR

CONVERT FROM FIXED (32 to long HFP)

CDFTR

CONVERT FROM FIXED (32 to long DFP)

CDGTR

CONVERT FROM FIXED (64 to long DFP)

CDLFBR

CONVERT FROM LOGICAL (32 to long BFP)

CDLFTR

CONVERT FROM LOGICAL (32 to long DFP)

CDSTR

CONVERT FROM SIGNED PACKED (64 to long DFP)

CDTR

COMPARE (long DFP)

CDUTR

CONVERT FROM UNSIGNED PACKED (64 to long DFP)

CDZT

CONVERT FROM ZONED (to long DFP)

CEDTR

COMPARE BIASED EXPONENT (long DFP)

CEFBR

CONVERT FROM FIXED (32 to short BFP)

CEFR

CONVERT FROM FIXED (32 to short HFP)

CELFBR

CONVERT FROM LOGICAL (32 to short BFP)

CELGBR

CONVERT FROM LOGICAL (64 to short BFP)

CEXTR

COMPARE BIASED EXPONENT (extended DFP)

CFC

COMPARE AND FORM CODEWORD

CFDBR

CONVERT TO FIXED (long BFP to 32)

CFDR

CONVERT TO FIXED (long HFP to 32)

CFDTR

CONVERT TO FIXED (long DFP to 32)

CFEBR

CONVERT TO FIXED (short BFP to 32)

CFER

CONVERT TO FIXED (short HFP to 32)

CFXBR

CONVERT TO FIXED (extended BFP to 32)

CFXR

CONVERT TO FIXED (extended HFP to 32)

CFXTR

CONVERT TO FIXED (extended DFP to 32)

CGDTR

CONVERT TO FIXED (long DFP to 64)

CHF

COMPARE HIGH (32)

CHHR

COMPARE HIGH (32)

CHLR

COMPARE HIGH (32)

CHRL

COMPARE HALFWORD RELATIVE LONG (32←16)

CIB

COMPARE IMMEDIATE AND BRANCH (32←8)

CIH

COMPARE IMMEDIATE HIGH (32)

CIJ

COMPARE IMMEDIATE AND BRANCH RELATIVE (32←8)

CIT

COMPARE IMMEDIATE AND TRAP (32←16)

CLCLE

COMPARE LOGICAL LONG EXTENDED

CLCLU

COMPARE LOGICAL LONG UNICODE

CLFDBR

CONVERT TO LOGICAL (long BFP to 32)

CLFDTR

CONVERT TO LOGICAL (long DFP to 32)

CLFEBR

CONVERT TO LOGICAL (short BFP to 32)

CLFIT

COMPARE LOGICAL IMMEDIATE AND TRAP (32←16)

CLFXBR

CONVERT TO LOGICAL (extended BFP to 32)

CLFXTR

CONVERT TO LOGICAL (extended DFP to 32)

CLGDBR

CONVERT TO LOGICAL (long BFP to 64)

CLGDTR

CONVERT TO LOGICAL (long DFP to 64)

CLGEBR

CONVERT TO LOGICAL (short BFP to 64)

CLGIB

COMPARE LOGICAL IMMEDIATE AND BRANCH (64←8)

CLGIJ

COMPARE LOGICAL IMMEDIATE AND BRANCH RELATIVE (64←8)

CLGIT

COMPARE LOGICAL IMMEDIATE AND TRAP (64←16)

CLGRB

COMPARE LOGICAL AND BRANCH (64)

CLGRJ

COMPARE LOGICAL AND BRANCH RELATIVE (64)

CLGRT

COMPARE LOGICAL AND TRAP (64)

CLGT

COMPARE LOGICAL AND TRAP (64)

CLGXBR

CONVERT TO LOGICAL (extended BFP to 64)

CLGXTR

CONVERT TO LOGICAL (extended DFP to 64)

CLIB

COMPARE LOGICAL IMMEDIATE AND BRANCH (32←8)

CLIJ

COMPARE LOGICAL IMMEDIATE AND BRANCH RELATIVE (32←8)

CLRB

COMPARE LOGICAL AND BRANCH (32)

CLRJ

COMPARE LOGICAL AND BRANCH RELATIVE (32)

CLRT

COMPARE LOGICAL AND TRAP (32)

CLST

COMPARE LOGICAL STRING

CLT

COMPARE LOGICAL AND TRAP (32)

CMPSC

COMPRESSION CALL

CPSDR

COPY SIGN (long)

CPYA

COPY ACCESS

CRB

COMPARE AND BRANCH (32)

CRJ

COMPARE AND BRANCH RELATIVE (32)

CRT

COMPARE AND TRAP (32)

CSDTR

CONVERT TO SIGNED PACKED (long DFP to 64)

CSST

COMPARE AND SWAP AND STORE

CSXTR

CONVERT TO SIGNED PACKED (extended DFP to 128)

CU14

CONVERT UTF-8 TO UTF-32

CU24

CONVERT UTF-16 TO UTF-32

CU41

CONVERT UTF-32 TO UTF-8

CU42

CONVERT UTF-32 TO UTF-16

CUDTR

CONVERT TO UNSIGNED PACKED (long DFP to 64)

CUSE

COMPARE UNTIL SUBSTRING EQUAL

CUTFU

CONVERT UTF-8 TO UNICODE

CUUTF

CONVERT UNICODE TO UTF-8

CUXTR

CONVERT TO UNSIGNED PACKED (extended DFP to 128)

CXBR

COMPARE (extended BFP)

CXFBR

CONVERT FROM FIXED (32 to extended BFP)

CXFR

CONVERT FROM FIXED (32 to extended HFP)

CXFTR

CONVERT FROM FIXED (32 to extended DFP)

CXGTR

CONVERT FROM FIXED (64 to extended DFP)

CXLFBR

CONVERT FROM LOGICAL (32 to extended BFP)

CXLFTR

CONVERT FROM LOGICAL (32 to extended DFP)

CXLGBR

CONVERT FROM LOGICAL (64 to extended BFP)

CXLGTR

CONVERT FROM LOGICAL (64 to extended DFP)

CXR

COMPARE (extended HFP)

CXSTR

CONVERT FROM SIGNED PACKED (128 to extended DFP)

CXTR

COMPARE (extended DFP)

CXUTR

CONVERT FROM UNSIGNED PACKED (128 to ext. DFP)

CXZT

CONVERT FROM ZONED (to extended DFP)

CZDT

CONVERT TO ZONED (from long DFP)

CZXT

CONVERT TO ZONED (from extended DFP)

DDTR

DIVIDE (long DFP)

DIDBR

DIVIDE TO INTEGER (long BFP)

DIEBR

DIVIDE TO INTEGER (short BFP)

DXBR

DIVIDE (extended BFP)

DXR

DIVIDE (extended HFP)

DXTR

DIVIDE (extended DFP)

EAR

EXTRACT ACCESS

ECAG

EXTRACT CACHE ATTRIBUTE

ECTG

EXTRACT CPU TIME

EEDTR

EXTRACT BIASED EXPONENT (long DFP to 64)

EEXTR

EXTRACT BIASED EXPONENT (extended DFP to 64)

EFPC

EXTRACT FPC

EPAIR

EXTRACT PRIMARY ASN AND INSTANCE

EPAR

EXTRACT PRIMARY ASN

EPSW

EXTRACT PSW

ESAIR

EXTRACT SECONDARY ASN AND INSTANCE

ESAR

EXTRACT SECONDARY ASN

ESDTR

EXTRACT SIGNIFICANCE (long DFP)

ESTA

EXTRACT STACKED STATE

ESXTR

EXTRACT SIGNIFICANCE (extended DFP)

ETND

EXTRACT TRANSACTION NESTING DEPTH

FIDBR

LOAD FP INTEGER (long BFP)

FIDTR

LOAD FP INTEGER (long DFP)

FIEBR

LOAD FP INTEGER (short BFP)

FIER

LOAD FP INTEGER (short HFP)

FIXBR

LOAD FP INTEGER (extended BFP)

FIXR

LOAD FP INTEGER (extended HFP)

FIXTR

LOAD FP INTEGER (extended DFP)

FLOGR

FIND LEFTMOST ONE

HER

HALVE (short HFP)

IAC

INSERT ADDRESS SPACE CONTROL

IEDTR

INSERT BIASED EXPONENT (64 to long DFP)

IEXTR

INSERT BIASED EXPONENT (64 to extended DFP)

IPK

INSERT PSW KEY

IVSK

INSERT VIRTUAL STORAGE KEY

KDB

COMPARE AND SIGNAL (long BFP)

KDBR

COMPARE AND SIGNAL (long BFP)

KDTR

COMPARE AND SIGNAL (long DFP)

KEB

COMPARE AND SIGNAL (short BFP)

KEBR

COMPARE AND SIGNAL (short BFP)

KIMD

COMPUTE INTERMEDIATE MESSAGE DIGEST

KLMD

COMPUTE LAST MESSAGE DIGEST

KM

CIPHER MESSAGE

KMAC

COMPUTE MESSAGE AUTHENTICATION CODE

KMC

CIPHER MESSAGE WITH CHAINING

KMCTR

CIPHER MESSAGE WITH COUNTER

KMF

CIPHER MESSAGE WITH CFB

KMO

CIPHER MESSAGE WITH OFB

KXBR

COMPARE AND SIGNAL (extended BFP)

KXTR

COMPARE AND SIGNAL (extended DFP)

LAAL

LOAD AND ADD LOGICAL (32)

LAALG

LOAD AND ADD LOGICAL (64)

LAE

LOAD ADDRESS EXTENDED

LAEY

LOAD ADDRESS EXTENDED

LAM

LOAD ACCESS MULTIPLE

LAMY

LOAD ACCESS MULTIPLE

LAN

LOAD AND AND (32)

LANG

LOAD AND AND (64)

LAO

LOAD AND OR (32)

LAOG

LOAD AND OR (64)

LAT

LOAD AND TRAP (32L←32)

LAX

LOAD AND EXCLUSIVE OR (32)

LAXG

LOAD AND EXCLUSIVE OR (64)

LCDBR

LOAD COMPLEMENT (long BFP)

LCDFR

LOAD COMPLEMENT (long)

LCEBR

LOAD COMPLEMENT (short BFP)

LCER

LOAD COMPLEMENT (short HFP)

LCXBR

LOAD COMPLEMENT (extended BFP)

LCXR

LOAD COMPLEMENT (extended HFP)

LDE

LOAD LENGTHENED (short to long HFP)

LDEB

LOAD LENGTHENED (short to long BFP)

LDEBR

LOAD LENGTHENED (short to long BFP)

LDER

LOAD LENGTHENED (short to long HFP)

LDETR

LOAD LENGTHENED (short to long DFP)

LDGR

LOAD FPR FROM GR (64 to long)

LDXBR

LOAD ROUNDED (extended to long BFP)

LDXR

LOAD ROUNDED (extended to long HFP)

LDXTR

LOAD ROUNDED (extended to long DFP)

LEDBR

LOAD ROUNDED (long to short BFP)

LEDR

LOAD ROUNDED (long to short HFP)

LEDTR

LOAD ROUNDED (long to short DFP)

LEXBR

LOAD ROUNDED (extended to short BFP)

LEXR

LOAD ROUNDED (extended to short HFP)

LFAS

LOAD FPC AND SIGNAL

LFPC

LOAD FPC

LGAT

LOAD AND TRAP (64)

LGDR

LOAD GR FROM FPR (long to 64)

LLGFAT

LOAD LOGICAL AND TRAP (64←32)

LLGTAT

LOAD LOGICAL THIRTY ONE BITS AND TRAP (64←31)

LNDBR

LOAD NEGATIVE (long BFP)

LNDFR

LOAD NEGATIVE (long)

LNEBR

LOAD NEGATIVE (short BFP)

LNER

LOAD NEGATIVE (short HFP)

LNXBR

LOAD NEGATIVE (extended BFP)

LNXR

LOAD NEGATIVE (extended HFP)

LOC

LOAD ON CONDITION (32)

LOCG

LOAD ON CONDITION (64)

LOCGR

LOAD ON CONDITION (64)

LOCR

LOAD ON CONDITION (32)

LPD

LOAD PAIR DISJOINT (32)

LPDBR

LOAD POSITIVE (long BFP)

LPDFR

LOAD POSITIVE (long)

LPDG

LOAD PAIR DISJOINT (64)

LPEBR

LOAD POSITIVE (short BFP)

LPER

LOAD POSITIVE (short HFP)

LPXBR

LOAD POSITIVE (extended BFP)

LPXR

LOAD POSITIVE (extended HFP)

LRV

LOAD REVERSED (32)

LRVH

LOAD REVERSED (16)

LRVR

LOAD REVERSED (32)

LTDBR

LOAD AND TEST (long BFP)

LTDTR

LOAD AND TEST (long DFP)

LTEBR

LOAD AND TEST (short BFP)

LTER

LOAD AND TEST (short HFP)

LTXBR

LOAD AND TEST (extended BFP)

LTXR

LOAD AND TEST (extended HFP)

LTXTR

LOAD AND TEST (extended DFP)

LXD

LOAD LENGTHENED (long to extended HFP)

LXDB

LOAD LENGTHENED (long to extended BFP)

LXDBR

LOAD LENGTHENED (long to extended BFP)

LXDR

LOAD LENGTHENED (long to extended HFP)

LXDTR

LOAD LENGTHENED (long to extended DFP)

LXE

LOAD LENGTHENED (short to extended HFP)

LXEB

LOAD LENGTHENED (short to extended BFP)

LXEBR

LOAD LENGTHENED (short to extended BFP)

LXER

LOAD LENGTHENED (short to extended HFP)

LXR

LOAD (extended)

LZDR

LOAD ZERO (long)

LZER

LOAD ZERO (short)

LZXR

LOAD ZERO (extended)

MADB

MULTIPLY AND ADD (long BFP)

MADBR

MULTIPLY AND ADD (long BFP)

MAE

MULTIPLY AND ADD (short HFP)

MAEB

MULTIPLY AND ADD (short BFP)

MAEBR

MULTIPLY AND ADD (short BFP)

MAER

MULTIPLY AND ADD (short HFP)

MAY

MULTIPLY and ADD UNNORMALIZED (long to ext. HFP)

MAYH

MULTIPLY AND ADD UNNRM. (long to ext. high HFP)

MAYHR

MULTIPLY AND ADD UNNRM. (long to ext. high HFP)

MAYL

MULTIPLY AND ADD UNNRM. (long to ext. low HFP)

MAYLR

MULTIPLY AND ADD UNNRM. (long to ext. low HFP)

MAYR

MULTIPLY and ADD UNNORMALIZED (long to ext. HFP)

MC

MONITOR CALL

MDTR

MULTIPLY (long DFP)

MS

MULTIPLY SINGLE (32)

MSD

MULTIPLY AND SUBTRACT (long HFP)

MSDB

MULTIPLY AND SUBTRACT (long BFP)

MSDBR

MULTIPLY AND SUBTRACT (long BFP)

MSDR

MULTIPLY AND SUBTRACT (long HFP)

MSE

MULTIPLY AND SUBTRACT (short HFP)

MSEB

MULTIPLY AND SUBTRACT (short BFP)

MSEBR

MULTIPLY AND SUBTRACT (short BFP)

MSER

MULTIPLY AND SUBTRACT (short HFP)

MSFI

MULTIPLY SINGLE IMMEDIATE (32)

MSGFI

MULTIPLY SINGLE IMMEDIATE (64←32)

MSR

MULTIPLY SINGLE (32)

MSTA

MODIFY STACKED STATE

MSY

MULTIPLY SINGLE (32)

MVCDK

MOVE WITH DESTINATION KEY

MVCK

MOVE WITH KEY

MVCLE

MOVE LONG EXTENDED

MVCLU

MOVE LONG UNICODE

MVCOS

MOVE WITH OPTIONAL SPECIFICATIONS

MVCP

MOVE TO PRIMARY

MVCS

MOVE TO SECONDARY

MVCSK

MOVE WITH SOURCE KEY

MVPG

MOVE PAGE

MVST

MOVE STRING

MXBR

MULTIPLY (extended BFP)

MXD

MULTIPLY (long to extended HFP)

MXDB

MULTIPLY (long to extended BFP)

MXDBR

MULTIPLY (long to extended BFP)

MXDR

MULTIPLY (long to extended HFP)

MXR

MULTIPLY (extended HFP)

MXTR

MULTIPLY (extended DFP)

MY

MULTIPLY UNNORMALIZED (long to ext. HFP)

MYH

MULTIPLY UNNORM. (long to ext. high HFP)

MYHR

MULTIPLY UNNORM. (long to ext. high HFP)

MYL

MULTIPLY UNNORM. (long to ext. low HFP)

MYLR

MULTIPLY UNNORM. (long to ext. low HFP)

MYR

MULTIPLY UNNORMALIZED (long to ext. HFP)

NIAI

NEXT INSTRUCTION ACCESS INTENT

NTSTG

NONTRANSACTIONAL STORE

PC

PROGRAM CALL

PCC

PERFORM CRYPTOGRAPHIC COMPUTATION

PFD

PREFETCH DATA

PFDRL

PREFETCH DATA RELATIVE LONG

PFPO

PERFORM FLOATING-POINT OPERATION

PKA

PACK ASCII

PKU

PACK UNICODE

PLO

PERFORM LOCKED OPERATION

POPCNT

POPULATION COUNT

PPA

PERFORM PROCESSOR ASSIST

PT

PROGRAM TRANSFER

PTFF

PERFORM TIMING FACILITY FUNCTION

PTI

PROGRAM TRANSFER WITH INSTANCE

QADTR

QUANTIZE (long DFP)

QAXTR

QUANTIZE (extended DFP)

RISBG

ROTATE THEN INSERT SELECTED BITS

RISBGN

ROTATE THEN INSERT SELECTED BITS

RISBHG

ROTATE THEN INSERT SELECTED BITS HIGH

RISBLG

ROTATE THEN INSERT SELECTED BITS LOW

RNSBG

ROTATE THEN AND SELECTED BITS

ROSBG

ROTATE THEN OR SELECTED BITS

RP

RESUME PROGRAM

RRDTR

REROUND (long DFP)

RRXTR

REROUND (extended DFP)

RXSBG

ROTATE THEN EXCLUSIVE OR SELECTED BITS

SAC

SET ADDRESS SPACE CONTROL

SACF

SET ADDRESS SPACE CONTROL FAST

SAR

SET ACCESS

SDTR

SUBTRACT (long DFP)

SFASR

SET FPC AND SIGNAL

SFPC

SET FPC

SHHHR

SUBTRACT HIGH (32)

SHHLR

SUBTRACT HIGH (32)

SLB

SUBTRACT LOGICAL WITH BORROW (32)

SLBR

SUBTRACT LOGICAL WITH BORROW (32)

SLDT

SHIFT SIGNIFICAND LEFT (long DFP)

SLHHHR

SUBTRACT LOGICAL HIGH (32)

SLHHLR

SUBTRACT LOGICAL HIGH (32)

SLXT

SHIFT SIGNIFICAND LEFT (extended DFP)

SLY

SUBTRACT LOGICAL (32)

SPKA

SET PSW KEY FROM ADDRESS

SQD

SQUARE ROOT (long HFP)

SQDB

SQUARE ROOT (long BFP)

SQDBR

SQUARE ROOT (long BFP)

SQDR

SQUARE ROOT (long HFP)

SQE

SQUARE ROOT (short HFP)

SQEB

SQUARE ROOT (short BFP)

SQEBR

SQUARE ROOT (short BFP)

SQER

SQUARE ROOT (short HFP)

SQXBR

SQUARE ROOT (extended BFP)

SQXR

SQUARE ROOT (extended HFP)

SRDT

SHIFT SIGNIFICAND RIGHT (long DFP)

SRNM

SET BFP ROUNDING MODE (2 bit)

SRNMB

SET BFP ROUNDING MODE (3 bit)

SRNMT

SET DFP ROUNDING MODE

SRST

SEARCH STRING

SRSTU

SEARCH STRING UNICODE

SRXT

SHIFT SIGNIFICAND RIGHT (extended DFP)

SSAIR

SET SECONDARY ASN WITH INSTANCE

SSAR

SET SECONDARY ASN

STAM

STORE ACCESS MULTIPLE

STAMY

STORE ACCESS MULTIPLE

STCKE

STORE CLOCK EXTENDED

STCKF

STORE CLOCK FAST

STFH

STORE HIGH (32)

STFLE

STORE FACILITY LIST EXTENDED

STFPC

STORE FPC

STOC

STORE ON CONDITION (32)

STOCG

STORE ON CONDITION (64)

STRV

STORE REVERSED (32)

STRVH

STORE REVERSED (16)

SXBR

SUBTRACT (extended BFP)

SXR

SUBTRACT NORMALIZED (extended HFP)

SXTR

SUBTRACT (extended DFP)

TABORT

TRANSACTION ABORT

TAR

TEST ACCESS

TBDR

CONVERT HFP TO BFP (long)

TBEDR

CONVERT HFP TO BFP (long to short)

TBEGIN

TRANSACTION BEGIN

TBEGINC

TRANSACTION BEGIN

TCDB

TEST DATA CLASS (long BFP)

TCEB

TEST DATA CLASS (short BFP)

TCXB

TEST DATA CLASS (extended BFP)

TDCDT

TEST DATA CLASS (long DFP)

TDCET

TEST DATA CLASS (short DFP)

TDCXT

TEST DATA CLASS (extended DFP)

TDGDT

TEST DATA GROUP (long DFP)

TDGET

TEST DATA GROUP (short DFP)

TDGXT

TEST DATA GROUP (extended DFP)

TEND

TRANSACTION END

THDER

CONVERT BFP TO HFP (short to long)

THDR

CONVERT BFP TO HFP (long)

TP

TEST DECIMAL

TRAP2

TRAP

TRAP4

TRAP

TRE

TRANSLATE EXTENDED

TROO

TRANSLATE ONE TO ONE

TROT

TRANSLATE ONE TO TWO

TRTE

TRANSLATE AND TEST EXTENDED

TRTO

TRANSLATE TWO TO ONE

TRTR

TRANSLATE AND TEST REVERSE

TRTRE

TRANSLATE AND TEST REVERSE EXTENDED

TRTT

TRANSLATE TWO TO TWO

UNPKA

UNPACK ASCII

UNPKU

UNPACK UNICODE

UPT

UPDATE TREE

There are 134 refused instructions:

Mnemonic

Description

AHHHR

ADD HIGH (32)

AHHLR

ADD HIGH (32)

AIH

ADD IMMEDIATE HIGH (32)

ALHHHR

ADD LOGICAL HIGH (32)

ALHHLR

ADD LOGICAL HIGH (32)

BRXHG

BRANCH RELATIVE ON INDEX HIGH (64)

BRXLG

BRANCH RELATIVE ON INDEX LOW OR EQ. (64)

BXHG

BRANCH ON INDEX HIGH (64)

BXLEG

BRANCH ON INDEX LOW OR EQUAL (64)

CDGBR

CONVERT FROM FIXED (64 to long BFP)

CDGR

CONVERT FROM FIXED (64 to long HFP)

CDLGBR

CONVERT FROM LOGICAL (64 to long BFP)

CDLGTR

CONVERT FROM LOGICAL (64 to long DFP)

CEGBR

CONVERT FROM FIXED (64 to short BFP)

CEGR

CONVERT FROM FIXED (64 to short HFP)

CGDBR

CONVERT TO FIXED (long BFP to 64)

CGDR

CONVERT TO FIXED (long HFP to 64)

CGEBR

CONVERT TO FIXED (short BFP to 64)

CGER

CONVERT TO FIXED (short HFP to 64)

CGHRL

COMPARE HALFWORD RELATIVE LONG (64←16)

CGIB

COMPARE IMMEDIATE AND BRANCH (64←8)

CGIJ

COMPARE IMMEDIATE AND BRANCH RELATIVE (64←8)

CGIT

COMPARE IMMEDIATE AND TRAP (64←16)

CGRB

COMPARE AND BRANCH (64)

CGRJ

COMPARE AND BRANCH RELATIVE (64)

CGRT

COMPARE AND TRAP (64)

CGXBR

CONVERT TO FIXED (extended BFP to 64)

CGXR

CONVERT TO FIXED (extended HFP to 64)

CGXTR

CONVERT TO FIXED (extended DFP to 64)

CRDTE

COMPARE AND REPLACE DAT TABLE ENTRY

CSCH

CLEAR SUBCHANNEL

CSP

COMPARE AND SWAP AND PURGE

CSPG

COMPARE AND SWAP AND PURGE

CVBG

CONVERT TO BINARY (64)

CVDG

CONVERT TO DECIMAL (64)

CXGBR

CONVERT FROM FIXED (64 to extended BFP)

CXGR

CONVERT FROM FIXED (64 to extended HFP)

DSG

DIVIDE SINGLE (64)

DSGF

DIVIDE SINGLE (64←32)

DSGFR

DIVIDE SINGLE (64←32)

DSGR

DIVIDE SINGLE (64)

ESEA

EXTRACT AND SET EXTENDED AUTHORITY

HSCH

HALT SUBCHANNEL

IDTE

INVALIDATE DAT TABLE ENTRY

IPTE

INVALIDATE PAGE TABLE ENTRY

ISKE

INSERT STORAGE KEY EXTENDED

LASP

LOAD ADDRESS SPACE PARAMETERS

LBH

LOAD BYTE HIGH (32←8)

LCGFR

LOAD COMPLEMENT (64←32)

LCGR

LOAD COMPLEMENT (64)

LCTL

LOAD CONTROL (32)

LCTLG

LOAD CONTROL (64)

LFH

LOAD HIGH (32)

LFHAT

LOAD HIGH AND TRAP (32H←32)

LLGFRL

LOAD LOGICAL RELATIVE LONG (64←32)

LMD

LOAD MULTIPLE DISJOINT

LMG

LOAD MULTIPLE (64)

LMH

LOAD MULTIPLE HIGH

LNGFR

LOAD NEGATIVE (64←32)

LNGR

LOAD NEGATIVE (64)

LPQ

LOAD PAIR FROM QUADWORD

LPSW

LOAD PSW

LPSWE

LOAD PSW EXTENDED

LPTEA

LOAD PAGE TABLE ENTRY ADDRESS

LRA

LOAD REAL ADDRESS (32)

LRAG

LOAD REAL ADDRESS (64)

LRAY

LOAD REAL ADDRESS (32)