Author Archives: Bob carten

Example Screen

Deep Dive: Integrating OpenInsight with External REST APIs Using PowerShell and RTI_JSONX

Author: Bob Carten

Categories: Advanced Integration, Basic+, PowerShell, REST API, JSON Architecture

As corporate ecosystems grow, the need to bridge OpenInsight applications with modern, third-party web services becomes inevitable. In the Revelation Software OpenInsight world, we face a choice: do we try to manage complex HTTP protocols, certificate handshakes, and raw multi-part binary streaming natively in Basic+, or do we lean into modern execution pipelines?

Recently, while modernizing an OpenInsight 10 document management workflow to sync index data with an external ImageQuest REST API, I developed a hybrid architecture that leverages the native strengths of OpenInsight, Basic+ RTI_JSONX, and Windows PowerShell runspaces via rti_powerscript.

This article walks through that design pattern in full — covering the exact traps you will hit around unmanaged memory management, .NET model binding validation, certificate enforcement, and multipart binary upload — and how to solve them for production-grade reliability.


The Architecture Overview

The most significant architectural trap when integrating an OpenInsight 10 application with a strict .NET-backed REST API is treating updates as simple PATCH statements. Many modern document management APIs enforce full object graphs during a PUT request. If you send only the properties modified in your UI window, the server-side model binder encounters a NullReferenceException on missing tracking fields (internal IDs, system creation timestamps, hidden file references) and crashes with a 500 Server Error.

To keep OpenInsight completely insulated from needing to know or track hidden system-level fields, the solution is a clean separation of concerns using a Read-Modify-Write cycle:

  1. OpenInsight Form: Collects user-edited criteria inside an Edit Table grid.
  2. Basic+ (RTI_JSONX): Translates internal OpenInsight delimiters (@FM and @VM) cleanly into a structured JSON string, escaping text variables natively and protecting data boundaries.
  3. PowerShell Engine via rti_powerscript: Authenticates via an OAuth2 token endpoint, fetches the absolute fresh master copy of the record from the server, merges the OpenInsight delta JSON string in memory, and submits a 100% compliant payload graph back to the network.

The result: OpenInsight never touches HTTP. PowerShell never parses OpenInsight delimiters. Each layer does only what it does best.


Step 1: The Basic+ Serialization Pipeline

When preparing data for a REST API, performing the transform in Basic+ via RTI_JSONX is vastly superior to doing text-splitting inside a PowerShell script. OpenInsight natively handles its own delimiters; passing a raw matrix to the OS layer is incredibly fragile if a user types a character that collides with an internal separator.

The following snippet shows how to iterate through an Edit Table array, build individual key/value JSON objects, append them into a root array container, and properly manage unmanaged heap pointers.

* =============================================================================
* Routine: Form Event Handler (e.g., BTN_UPDATE Click)
* Purpose: Extract Edit Table grid edits and serialize to valid JSON
* =============================================================================
docId = @atWindow->udp_DocId$
modified_Array = .ET_Metadata->Array
* Safely append the active context module without using dangerous <-1> indicators
nextCol = FieldCount(modified_Array<1>, @VM) + 1
modified_Array<1, nextCol> = 'Document Type'
modified_Array<2, nextCol> = @atWindow->udp_CurrentModule$
* 1. Initialize a new root JSON Array pointer handle
hJsonArray = rti_jsonx(null$, "NEW", "ARRAY")
* 2. Loop through the metadata row entries
numRows = nextCol
For r = 1 To numRows
attrName = modified_Array<1, r>
attrValue = modified_Array<2, r>
If attrName != "" Then
hPairObj = rti_jsonx(null$, "NEW", "OBJECT")
Call rti_jsonx(hPairObj, "SETVALUE", "name", attrName, "STRING")
Call rti_jsonx(hPairObj, "SETVALUE", "value", attrValue, "STRING")
* Method 16 = RJSON_MTD_APPENDITEM$ -- array append does NOT go through rti_jsonx
Call rti_rjson(16, hJsonArray, "", hPairObj)
* CRITICAL: Free the child immediately -- parent now holds the reference
Call rti_jsonx(hPairObj, "FREE")
End
Next r
* 3. Serialize the object graph to a flat text string
jsonString = rti_jsonx(hJsonArray, "STRINGIFY")
* 4. Free the parent array handle
Call rti_jsonx(hJsonArray, "FREE")
* 5. Hand the payload to the REST interface
response = IQREST_INTERFACE('MODIFY', docId, jsonString)

A few things are worth mentioning:

  • Array append does not go through rti_jsonx. You must call rti_rjson(16, hParent, "", hChild) directly. This is the raw method number for RJSON_MTD_APPENDITEM$ inside RevJSON.dll.
  • Free the child immediately after appending. The parent array now holds the only live reference. Holding the child handle is not just unnecessary — it is a leak that compounds in loops.
  • Free the parent after STRINGIFY. The serialized string is a normal Basic+ variable. Once you have it, the handle has no further purpose.
  • Memory Managment is an oi10 requirement. OpenInsight 11 has a native Json type which manages the memory for you.

Step 2: Storing the PowerShell Module in the OI Repository

Note that the PowerShell module lives entirely inside the OpenInsight repository. There is no separate .psm1 file to deploy, version, or synchronize. Because this was an OpenInsight 10 project, the module is stored in an OI Insert record using a wrapper which the OI compiler silently ignores at compile time but that is readable at runtime.

Compile Insert IQREST_UTILS_PSM
#IFDEF IGNORE_ME
# PowerShell module begins here -- lines starting with # are PS comments
function Get-IQToken {
param([string]$BaseUrl, [string]$Username, [string]$Password)
$body = @{ username = $Username; password = $Password }
$resp = Invoke-RestMethod -Uri "$BaseUrl/api/token" -Method Post -Body $body
return $resp.access_token
}
function Get-IQDocument {
param([string]$BaseUrl, [string]$Token, [string]$DocId)
$headers = @{ Authorization = "Bearer $Token" }
return Invoke-RestMethod -Uri "$BaseUrl/api/documents/$DocId" -Method Get -Headers $headers
}
function Update-IQMetadata {
param([string]$BaseUrl, [string]$Token, [string]$DocId, [string]$DeltaJson)
# Step 1: Read-Modify-Write -- fetch the complete document graph first
$doc = Get-IQDocument -BaseUrl $BaseUrl -Token $Token -DocId $DocId
$delta = ConvertFrom-Json $DeltaJson
# Step 2: Merge the delta attributes sent from OpenInsight
foreach ($pair in $delta) {
$existing = $doc.Attributes | Where-Object { $_.name -eq $pair.name }
if ($existing) {
$existing.value = $pair.value
} else {
$doc.Attributes += [PSCustomObject]@{ name = $pair.name; value = $pair.value }
}
}
# Step 3: PUT the complete, merged object graph back
$headers = @{ Authorization = "Bearer $Token"; "Content-Type" = "application/json" }
$body = $doc | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri "$BaseUrl/api/documents/$DocId" -Method Put `
-Headers $headers -Body $body
Write-Output "UPDATE_STATUS:SUCCESS"
}
#ENDIF

At runtime, the calling Basic+ routine reads the insert record and strips the OI framing before passing it to rti_powerscript:

* Read the insert record from the repository
Call Repository("ACCESS", repoRow, "STPROCINS", "IQREST_UTILS_PSM", "", "", "", sErr)
insertText = repoRow<2>
* Strip everything before the first # to remove the OI compiler wrapper
psModule = insertText[Index(insertText, '#', 1), Len(insertText)]

This approach has significant operational benefits: the PowerShell code travels with your OI application export, is subject to the same source control workflow, and requires zero additional deployment steps on a new workstation.


Step 3: The PowerShell Communication Engine

rti_powerscript hosts a PowerShell runspace in-process via OI’s .NET bridge. It requires System.Management.Automation.dll in the OI client files directory — the same assembly used by PowerShell itself. Once that assembly is present, no other installation is required.

The calling pattern from the IQREST_INTERFACE subroutine looks like this:

Declare Function rti_powerscript
* Build the complete script: load the module, then call the function
psScript = psModule : @fm
psScript := "$baseUrl = '" : baseUrl : "'" : @fm
psScript := "$token = Get-IQToken -BaseUrl $baseUrl -Username '" : user : "' -Password '" : pass : "'" : @fm
psScript := "Update-IQMetadata -BaseUrl $baseUrl -Token $token -DocId '" : docId : "' -DeltaJson '" : jsonString : "'"
* Execute -- pipeline is single-use, must CLEAR before running another script
scriptHandle = rti_powerscript("ADDSCRIPT", psScript)
results = rti_powerscript("RUNCOMMAND", scriptHandle)
status = results<1>
dummy = rti_powerscript("CLEAR")
* Check result
If status == "UPDATE_STATUS:SUCCESS" Then
Msg(@window, "Document updated successfully.")
End Else
errMsg = Field(status, ":", 3)
Msg(@window, errMsg)
End

Key behaviors to understand:

  • Default runspace auto-initializes. You do not need to call CREATERUNSPACE for typical single-shot scripts. The runspace persists in a named common block between calls.
  • Pipelines are single-use. After RUNCOMMAND, you must call CLEAR before running another script. Forgetting this causes the second call to fail silently.
  • Each Write-Output line becomes one field. RUNCOMMAND returns results as an @FM-delimited string where results<1> is the first output line, results<2> is the second, and so on. CRLF within a line is converted to @VM.
  • Result tokens are explicit contracts. By convention, the first Write-Output line is a status token like UPDATE_STATUS:SUCCESS or UPDATE_STATUS:ERROR:message text. Basic+ checks this token and reacts accordingly. This keeps error handling clean on both sides of the boundary.

Step 4: Handling Real-World Network Constraints

In most enterprise environments, you will immediately run into TLS certificate validation failures against internal servers running self-signed or private CA certificates. Rather than deploying certificates to every workstation, the cleanest approach for an internal integration is a targeted TLS bypass scoped to the specific request:

# At the top of the PowerShell module, before any Invoke-RestMethod calls:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
[System.Net.ServicePointManager]::SecurityProtocol = `
[System.Net.SecurityProtocolType]::Tls12 -bor `
[System.Net.SecurityProtocolType]::Tls13

The TLS12/TLS13 enforcement is equally important. Older .NET framework versions default to TLS 1.0, which most modern servers now reject outright.

Multipart File Upload

The one area where Invoke-RestMethod falls short is multipart/form-data uploads where the boundary must be precisely controlled and binary content must be included alongside metadata fields. For Add-IQDocument, we dropped down to System.Net.HttpWebRequest directly:

function Add-IQDocument {
param([string]$BaseUrl, [string]$Token, [string]$FilePath, [string]$MetaJson)
$boundary = [System.Guid]::NewGuid().ToString()
$url = "$BaseUrl/api/documents"
$req = [System.Net.HttpWebRequest]::Create($url)
$req.Method = "POST"
$req.Headers.Add("Authorization", "Bearer $Token")
$req.ContentType = "multipart/form-data; boundary=$boundary"
$bodyStream = New-Object System.IO.MemoryStream
# Metadata part
$metaPart = "--$boundary`r`nContent-Disposition: form-data; name=`"metadata`"`r`n`r`n$MetaJson`r`n"
$metaBytes = [System.Text.Encoding]::UTF8.GetBytes($metaPart)
$bodyStream.Write($metaBytes, 0, $metaBytes.Length)
# File part
$fileName = [System.IO.Path]::GetFileName($FilePath)
$filePart = "--$boundary`r`nContent-Disposition: form-data; name=`"file`"; filename=`"$fileName`"`r`nContent-Type: application/octet-stream`r`n`r`n"
$fileBytes = [System.Text.Encoding]::UTF8.GetBytes($filePart)
$bodyStream.Write($fileBytes, 0, $fileBytes.Length)
$fileData = [System.IO.File]::ReadAllBytes($FilePath)
$bodyStream.Write($fileData, 0, $fileData.Length)
$closing = "`r`n--$boundary--`r`n"
$closingBytes = [System.Text.Encoding]::UTF8.GetBytes($closing)
$bodyStream.Write($closingBytes, 0, $closingBytes.Length)
$req.ContentLength = $bodyStream.Length
$bodyStream.Position = 0
$bodyStream.CopyTo($req.GetRequestStream())
$resp = $req.GetResponse()
Write-Output "ADD_STATUS:SUCCESS"
}

This gives you byte-perfect control over the multipart boundary, content-type headers, and binary encoding — something Invoke-RestMethod -Form cannot reliably deliver across all PowerShell versions.


Why This Architecture Works

After working through this integration, the design philosophy is clear and reusable for any REST API target:

ConcernHandled byWhy it belongs there
UI state, user input, OI delimitersBasic+ / RTI_JSONXOI knows its own data model; JSON escaping is handled natively
HTTP, TLS, OAuth2, multipartPowerShellModern .NET stack; no native support needed in Basic+
Server-side model graph integrityPowerShell Read-Modify-WriteFetching fresh state before PUT avoids all partial-update 500 errors
Module deploymentOI Insert + #IFDEF IGNORE_MEPowerShell travels with the OI app; no separate file management
Result communicationWrite-Output token protocolClean contract; Basic+ reads results<1> without parsing raw HTTP

The #IFDEF IGNORE_ME insert pattern for storing PowerShell modules is particularly worth highlighting. It means your PowerShell is versioned in the OI repository, exported with your application, and deployed with zero extra steps. This pattern works well for OpenInsight 10, but it overloads the functionality of inserts. We expect that interacting with external agents will be a common requirement in the future, so in OpenInsight version 11 we intend to add a powerscript document type, as well as python, JavaScript and other common scripting languages supported by the new Revelation application server (RevAS). That is a topic for future articles.

For any integration project that involves a .NET-modeled API, certificate-sensitive endpoints, or binary file transfer, this three-layer pattern — Basic+ for data, RTI_JSONX for serialization, PowerShell for the network — is a solid approach for a production-reliable result. We intend that OpenInsight 11 will make using this pattern even easier.

Quickdex in OpenInsight 10

Overview

QUICKDEX.MFS and RIGHTDEX.MFS are programs that provide a quick method to obtain a list of all keys in a table, and both call the subroutine LRDEX.SUB. This stored procedure maintains a sorted list of keys in a special record named %RECORDS%. You can read this list in one operation. For example:

Keys = Xlate(‘MYTABLE’,’%RECORDS%’, ”, ‘X’)

However, there is a cost to maintaining this record. LRDEX.SUB must lock, read, scan, and if needed update the %RECORDS% row every time a row is written or deleted. This creates a potential bottleneck in a busy table. READNEXT operations must scan the result list to remove ‘%RECORDS%’.  The read method must rebuild the %RECORDS% row if it does not exist. The special %RECORDS% row is not compatible with other BFS’s. The LRDEX.SUB program originated with early versions of Advanced Revelation. At that time, storing a list of keys offered a substantial performance benefit. The benefit justified the cost for smallish tables that rarely change. These tables often populate dropdowns for data entry forms, where a quick read of all keys is useful.

When reviewing LRDEX.SUB for OpenInsight 10 we realized it needed revision. The sorting depended on the Arev language sets feature and was incompatible with DICT.MFS, causing crashes.  Rebuilding the %RECORDS% rows was not optimized. We wanted to address these and other issues and improve performance.

Changes

We started by optimizing the %RECORDS% rebuild. We needed to perform a rapid table scan and write a sorted list of keys.  OI10’s RList already provides optimized table scans, so we reused that with V119 to sort the list.  Surprisingly, reading %RECORDS% with and without the new rebuild showed a negligible performance difference!  We realized that storing a list of keys no longer offered much benefit.  We could remove the overhead of maintaining the list without sacrificing performance.  Thus, the revised LRDEX.SUB will perform a table scan when you ask for %RECORDS%. It does not lock or write anything. It still checks for %RECORDS% in a READNEXT. It will delete the record if it finds it. Quickdex.mfs remains best suited to smaller tables. However, it will not fail unexpectedly as a table grows, and it will work with any of the BFS’s.

Sharing data with OpenInsight 9

Note that the OI10 version of LRDEX.SUB is not compatible with the OI 9 version. OI9 requires the %RECORDS% row, OI10 deletes it. For that reason we soft-coded LRDEX.SUB so you can specify which version to use with a configuration setting.  If you need share tables between OI9 and OI10 you must configure OI10 to use the same logic as OI9.  Create a record in the SYSENV table named “RTI_CFG_LRDEX” with “RTI_LRDEX_SUB_9” in the first row.

Summary

OpenInsight 10 offers a simplified Quickdex/Rightdex capability with similar benefits and without the shortcomings of the OI9 version. The simplified version cannot be used on tables shared with OI9. You can configure OI10 to use the OI9 version if needed.

RList eXtended – New features for OpenInsight 10.1

RList is the OpenInsight tool for queries and ad hoc reports. OpenInsight 10 implements an extended version of RList via the new RTI_RLISTX stored procedure which offers additional features for selecting and reporting data. However, you do not need to change existing programs to take advantage of this.

This post takes a look at some of the new RList capabilities, along with a full description of the API and some code examples.

OLIST/RUN_REPORT syntax support

LIST statements can use the same syntax as RUN_REPORT or OLIST if the output is going to the screen or printer.

  • With OLIST LIST statements you can use keywords like GRID and PDFFILE. When the target is TARGET_PRINTER$, RList calls OLIST_PRODUCE to render the output.

Stacked selects

RList accepts a stack of SELECT statements and optionally one output (LIST) statement.  RList will execute SELECT statements until zero records are selected or a LIST statement is executed.  This allows developers to break a complex query into a series of simpler SELECT statements. Each subsequent SELECT statement refines the active select list.

RList to BRW

The TARGET_BRWDESIGN$ option will create a BRW (Banded Report Writer) report from an RList statement and open it in the BRW Designer for the developer to refine it. RList will prompt for the report group name.  The intent is to let you quickly rough out a report or even a set of master/detail reports using RList, then use the BRW Designer to refine the result.

RList to a variable

The TARGET_VARIABLE$ option will return the result of an RList statement into a variable.  For example, you can get a list of keys without using loop/readnext, you can populate an edit table with a SELECT statement, and you can obtain CSV, XML or JSON data by calling RList.  This is similar to the OpenInsight 9 SELECT_INTO stored procedure. The SELECT_INTO syntax is still supported and now calls RTI_RLISTX internally to implement the commands instead.

Cursor support

RList support allows you to specify a cursor number between 0 and 8, or -1 to use the next non-zero cursor. OpenInsight 9 offered cursor support but the RList interface did not. Cursors permit sub-queries that don’t corrupt the main select loop.  Sub-queries can operate on other BFS’s too. For example OpenInsight calculated columns can query SQL tables.

Reduce, Select by, ReadNext, Swap.Cursor, Make.list, Save_Select, Activate_Save_Select all support cursors in OpenInsight 10.

Performance enhancements

RList implements many optimizations in selecting and reporting: 

  • It will use indexes to refine an existing select, whereas previous versions only use indexes on a select without an active select list. 
  • Caching can reduce server IO so RList can now cache rows as they are read if it knows that the same rows will be sorted or reported – previous versions always read the records to select, sort and report.   
  • RESOLVE_SELECT, the program which finalizes a select, is improved. If you specify the number of rows, RESOLVE_SELECT exits when it reaches that number of rows rather than resolving all of the rows before applying the limit.
  • RList calls a new routine, RTI_CHAIN_SELECT, to pre-process selects which has some query optimization built in. For example, it will select on indexed fields before non-indexed, and hard fields before calculated. It performs sorts after the all selects are completed.

RList syntax in OpenInsight 10

Call RList( Statement, Target, TargetName, UserArg, DebugFlag )

Statement parameter

This parameter should contain one or more OpenList (SELECT/LIST) statements, separated by field marks.  RList will process each statement sequentially until it exhausts the list of keys, selects zero rows, or executes a LIST statement.  Note that in OpenInsight 10 RList accepts the same syntax as the classic OLIST or RUN_REPORT procedures as well as that used in TCL and any legacy OpenInsight 9 syntax.   

Target parameter

Target is a code indicating the desired output format.  OpenInsight supplies an insert which enumerates the options for target. See the RLIST_EQUATES insert record for more details.

Target ValueDescription
TARGET_PRINTER$0Sends the output of a LIST statement to the printer.
TARGET_CLIENT$1Sends the output of a LIST statement to the screen.
TARGET_VARIABLE$2Returns the output of a LIST statement to a variable.
TARGET_CALLBACK$3Triggers an RLIST_CALLBACK routine.
TARGET_SAVELIST$4Performs a SAVE_SELECT operation on the result of select statements.
TARGET_ACTIVELIST$5Activates a cursor with the result of select statements.
TARGET_LATENTLIST$6Creates a latent cursor for subsequent ReadNext processing.
TARGET_CHECKSYNTAX$7Checks the statements for valid syntax but does not execute them.
TARGET_CALLBACK_NOFMT$8Triggers an RLIST_CALLBACK routine but with no formatting or truncation applied to the returned values.
TARGET_BRWDESIGN$9Generate a Banded report,  and open it in the designer.
TARGET_BRWRUN$10Generate a Banded report, and execute immediately.

TargetName parameter

This parameter is polymorphic. You supply different values for different combinations of target and SELECT or LIST statements:

TargetSelect StatementLIST statement
TARGET_PRINTER$N/aN/a
TARGET_CLIENT$N/aN/a
TARGET_VARIABLE$RList will return the keys if the output format is “KEYS”.  See the UserArg parameter.RList will return the output into the variable. The format of the output depends on the UserArg parameter.
TARGET_CALLBACK$N/aThe name of an “RList callback” procedure.
TARGET_SAVELIST$The name of the list to save. The string you pass is the list name in the SYSLISTS table.  If you pass a space delimited string, RLIST will write the list to a table other than SYSLISTS using the first word as the name of the table and the second as the id of the list.N/a
TARGET_ACTIVELIST$N/aN/a
TARGET_LATENTLIST$N/aN/a
TARGET_CHECKSYSTAX$N/aN/a
TARGET_CALLBACK_NOFMT$N/aThe name of an “RList callback” procedure.  RList will not enforce column widths on the output. Used by SELECT_INTO in OpenInsight 9.
TARGET_BRWDESIGN$N/aThe name of the report group and report to generate.
TARGET_BRWRUN$N/aN/a

UserArg parameter

Another polymorphic parameter whose format depends on the chosen Target parameter:

TargetSelect StatementLIST statement
TARGET_PRINTER$N/aUserArg<2> = cursor number
TARGET_CLIENT$N/aUserArg<2> = cursor number
TARGET_VARIABLE$UserArg<1> = output format. “KEYS” is the only relevant format. UserArg<2> = cursor numberUserArg<1> = ResultFormat ( see below ) UserArg<2> = cursor number
TARGET_CALLBACK$N/aUserArg<2> = cursor number
TARGET_SAVELIST$UserArg<2> = cursor numberN/a
TARGET_ACTIVELIST$UserArg<2> = cursor numberN/a
TARGET_LATENTLIST$ N/a
TARGET_CHECKSYSTAX$N/aN/a
TARGET_CALLBACK_NOFMT$ N/a UserArg<2> = cursor number
TARGET_BRWDESIGN$N/aThe name of the report group and report to generate.
TARGET_BRWRUN$N/aN/a

ResultFormat

ResultFormat values are applicable when returning the result to a variable using the TARGET_VARIABLE$ Target.

ResultFormatDescription
ADOResult is an OLE record-set containing VARCHAR values.
CSVResult is a set of comma separated values, all values quoted, commas between columns, carriage-return/linefeed characters between rows.
EDTEdit Table format.  Row 1 is column headings, @vm between columns, @fm between rows.  Useful for populating edit tables using RList statements
HTMLResult is an HTML table.
JSONResult is a JSON array of row objects, each column is an object in the row, multi-values are arrays.
KEYSResult is an @fm-delimited list of keys.
MVDefault format. Result is an array – @fm between columns, @rm between rows. Row 1 is the column headers.
TABResult is a set of tab-delimited column values, carriage-return/linefeed characters between rows.
TXTSame as TAB (see above).
XMLResult is an XML collection of rows, each row is an XML collection of columns.

Cursor Number

Cursor numbers specified in the UserArg parameter should be one of the following values:

  • An integer between 0 and 8
  • Null to use for cursor 0 (the default)
  • -1 for next available, which chooses an inactive cursor from 1 to 8

DebugFlag parameter

 N/a.

Code Examples

RList to PDF

RList To PDF

Stacked Queries

Sometimes it’s easier to execute a series of select statements rather than a single complex query. RList accepts a list of selects with an optional closing list statement. RList will execute until completed or no records are selected. If one of the conditions selects nothing then the list statement will not run.

Stacked queries

Query using a cursor

RList now supports cursors like REDUCE and SELECT BY. Indicate the cursor in field two of the UserArg parameter (in other words, UserArg<2>). Use this to execute sub queries. The sub query can return keys from a select or the output from a LIST statement.

Query using alternate cursor

Sub-query without corrupting the main select

Sub-query example

Sub query in calculated column

You can use sub-queries in calculated columns too. A cursor variable of -1 uses the next available cursor. This allows you to nest calculated columns which perform sub-selects. If you always use subqueries rather than direct calls to btree.extract then your logic will function with or without indexes. You can make indexing choices in the performance tuning stage of development rather than when designing the dictionaries.

Calculated column using sub-query

Return results to a variable

A new target type, TARGET_VARIABLE$ ( 2 ), will return the output to a variable. Pass a variable as the third parameter and an output format as the fourth. For SELECT statements, you must specify ‘KEYS’ as the output format. For LISTS statements you supply one of the formats listed above.

Select keys into a variable

Populating an edit table control

The “EDT” output format is convenient for loading data into edit table controls:

Populate an edit table control

Conclusion

RList for OpenInsight 10 offers new features as well as improved performance. Some of the features are available in previous versions but not via the RList interface. We tried to unify the disparate query capabilities (rlist, reduce, btree.extract, select_Into, olist ) under a single interface so you can focus on functionality and we can focus on performance.