File classes: Difference between revisions

From m204wiki
Jump to navigation Jump to search
m (Created page with "<!-- File classes --> One of the great strengths of User Language is the tight integration of the '''database Manipulation Language''', often abbreviated DML, with the prog...")
 
No edit summary
 
(31 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<!-- File classes -->
<!-- File classes -->
   
   
One of the great strengths of <var class="product">SOUL</var> is the tight integration of the
One of the great strengths of User Language is the tight integration of the
'''database Manipulation Language''', often abbreviated DML,
'''database Manipulation Language''', often abbreviated DML,
with the programming language.
with the programming language.
That is, statements like Find, For Each Record, and For Each Occurrence
That is, statements like <var>Find</var>, <var>For Each Record</var>, and <var>For Each Occurrence</var>
can be easily embedded in User Language procedure code.
can be easily embedded in <var class="product">SOUL</var> procedure code.
Embedding some of these statements in methods, however, can be problematic,
Embedding some of these statements in methods, however, can be problematic,
and it would be very useful to have object-oriented versions of some standard User Language
and it would be very useful to have object-oriented versions of some standard <var class="product">SOUL</var>
file-oriented facilities.
file-oriented facilities.
   
   
For example, one could execute a Find statement inside a method:
For example, one could execute a <var>Find</var> statement inside a method:
<p class="code"> class customer
<p class="code">class customer
    ...
  ...
    subroutine getRecords
  subroutine getRecords
   
   
    custRecs: in file customer fd
  custRecs: in file customer fd
                custid = %custId
                custid = %custId
              end find
            end find
          ...
          ...
    end subroutine
  end subroutine
end class
end class
</p>
</p>
But it would be impossible to use such a found set between methods in
But it would be impossible to use such a found set between methods in
Line 30: Line 28:
for an instance of a class.
for an instance of a class.
   
   
The [[Janus SOAP ULI]] provides these capabilities with record set, record,
<var class="product">SOUL</var> provides these capabilities with record set, record,
and sorted record set
and sorted record set objects.
objects.
The classes, associated with <var class="product">Model 204</var> file structures, are called
The classes, associated with <var class="product">Model 204</var> file structures, are called
'''file classes''', though they can also reference groups.
'''file classes''', though they can also reference groups.  
 
The names of these classes are:
<ul>
<li><var>[[Recordset class|Recordset]]</var>
<li><var>[[Record class|Record]]</var>
<li><var>[[RecordsetCursor class|RecordsetCursor]]</var>
<li><var>[[SortedRecordset class|SortedRecordset]]</var>
</ul>
   
   
==Declaring file objects==
One unusual aspect of file classes is that a reference to a file
One unusual aspect of file classes is that a reference to a file
object will not only affect the statement in which it appears,
object will not only affect the statement in which it appears,
Line 42: Line 48:
For this to be accomplished, the file or group context of a
For this to be accomplished, the file or group context of a
file object must be known at compile-time, so this context must
file object must be known at compile-time, so this context must
be specified in a file object declaration.
be specified in a file object declaration. The syntax of this declaration follows:
==File object declaration syntax==
 
<p class="pre"> {variable} [Is] Object fileClassName                      -
<p class="syntax"><span class="squareb">{</span><span class="term">variable</span><span class="squareb">}</span> <span class="squareb">[</span><span class="literal">Is</span><span class="squareb">]</span> <span class="literal">Object</span> <span class="term">fileClassName</span>                     -
            In [ [Perm | Temp] Group | File ] fgname      -
          <span class="literal">In</span> <span class="squareb">[</span> <span class="squareb">[</span><span class="literal">Perm</span> <span class="squareb">|</span> <span class="literal">Temp</span><span class="squareb">]</span> <span class="literal">Group</span> <span class="squareb">|</span> <span class="literal">File</span> <span class="squareb">]</span> <span class="term">fgname</span>       -
            [ Array(arraySize) ] [ Global[(globalName)] ]
          <span class="squareb">[</span> <span class="literal">Array</span>(<span class="term">arraySize</span>) <span class="squareb">]</span> <span class="squareb">[</span> <span class="literal">Global</span><span class="squareb">[</span>(<span class="term">globalName</span>)<span class="squareb">]</span> <span class="squareb">]</span>
</p>
</p>
<dl>
 
<dt>variable
<table class="syntaxTable">
<dd>The name of the file object variable.
<tr><th>variable</th>
If outside a class declaration block and structure, the variable must
<td>The name of the file object variable.
begin with a percent sign (%).
If outside a class declaration block and structure, the variable must begin with a percent sign (<tt>%</tt>).
If inside a structure declaration, the variable must not begin with
If inside a structure declaration, the variable must not begin with a percent sign.
a percent sign.
If inside a class declaration block, the variable cannot start with a percent sign and must be preceded by the word <code>Variable</code>.</td></tr>
If inside a class declaration block, the variable cannot start with
 
a percent sign and must be preceded by the word <code>Variable</code>.
 
<dt>fileClassName
<tr><th>fileClassName</th>
<dd>The name of a file class.
<td>The name of a file class.
<var class="product">Sirius Mods</var> version 6.5 supports only the Recordset class,
Options are the <var>Recordset</var>, <var>Record</var>, <var>RecordsetCursor</var>, and <var>SortedRecordset</var> classes.</td></tr>
and <var class="product">Sirius Mods</var> version 6.6 supports the Record, RecordsetCursor,
 
and SortedRecordset classes.
<tr><th>fgname</th>
<dt>fgname
<td>The file or group name.
<dd>The file or group name.
If this name is not preceded by keywords that explicitly identify the file or group context, the <var class="product">Model 204</var> search ordering is followed to determine the reference context: temporary groups first, then open permanent groups, then open files.</td></tr>
If this name is not preceded by keywords that explicitly identify the file or group
 
context, the <var class="product">Model 204</var> search ordering is followed to determine the reference context:
 
temporary groups first, then open permanent groups, then open files.
<tr><th>arraySize</th>
<dt>arraySize
<td>The number of elements in the object array, if it is an array.</td></tr>
<dd>The number of elements in the object array, if it is an array.
 
<dt>globalname
 
<dd>The optional global name, if the collection variable is
<tr><th>globalname</th>
not a class or structure variable.
<td>The optional global name, if the collection variable is not a class or structure variable.
If <code>Global</code> is specified without <code>globalName</code>,
If <var>Global</var> is specified without <var class="term">globalName</var>, the name of the variable without a
the name of the variable without a
percent sign is used as the global name.
percent sign is used as the global name.
An object array cannot be Global.
An object array cannot be <var>Global</var>.</td></tr>
</dl>
</table>
   
   
Because they are objects, file objects can be assigned or compared to
Because they are objects, file objects can be assigned or compared to
each other, but only if the variables have the same
each other, but only if the variables have the same
class and the same file/group context.
class and the same file/group context.
As just two examples, you cannot assign a Record object variable to a
As just two examples, you cannot assign a <var>Record</var> object variable to a
Recordset object variable, and you
<var>Recordset</var> object variable, and you
cannot assign a Recordset In File Ccasys
cannot assign a <code>Recordset In File Ccasys</code>
object variable to a Recordset In Group Customer object variable.
object variable to a <code>Recordset In Group Customer</code> object variable.
   
   
When invoking a shared method in a file class using the class name,
When invoking a shared method in a file class using the class name,
the file/group context must be specified along with the class name:
the file/group context must be specified along with the class name:
<p class="code"> %productRec is object record in file products
<p class="code">%productRec is object record in file products
  ...
...
%productRec  = %(record in file products):new(%n)
%productRec  = %(record in file products):new(%n)
</p>
</p>
This example can also be written, of course, as:
This example can also be written, of course, as:
<p class="code"> %productRec is object record in file products
<p class="code">%productRec is object record in file products
  ...
...
%productRec  = new(%n)
%productRec  = new(%n)
</p>
</p>
==DeepCopy for file objects==
==DeepCopy for file objects==
As of <var class="product">Sirius Mods</var> version 6.8,
The <var>Record</var>, <var>Recordset</var>, and <var>SortedRecordset</var> objects are deep-copyable.
the Record, Recordset, and SortedRecordset objects are deep-copyable.
This means that these objects can be contained inside <var class="product">SOUL</var> classes and
This means that these objects can be contained inside User Language classes and
those classes, themselves, could be deep-copyable.
those classes, themselves, could be deep-copyable.
   
   
Perhaps even more important, this means that these objects can be passed
Perhaps even more importantly, this means that these objects can be passed
back and forth between master and daemon threads ([[??]] refid=daemons.),
back and forth between master and [[Daemon class|daemon]] threads,
via the Run, GetInputObject, ReturnObject, and ReturnInfoObject methods.
via the <var>Run</var>, <var>GetInputObject</var>, <var>ReturnObject</var>, and <var>ReturnInfoObject</var> methods.
Among other things, this makes it easy to dynamically generate Find statements
Among other things, this makes it easy to dynamically generate <var>Find</var> statements
that run on a daemon thread and then use the resulting found set on the master
that run on a daemon thread and then use the resulting found set on the master
thread:
thread:
<p class="code"> b
<p class="code">b
   
   
%recs      is object recordSet in file sirfiled
%recs      is object recordSet in file sirfiled
%daem      is object daemon
%daem      is object daemon
%program    is object stringList
%program    is object stringList
%criteria1  is string len 32
%criteria1  is string len 32
%criteria2  is string len 32
%criteria2  is string len 32
   
   
%criteria1 = 'rectype eq ''FILE'''
%criteria1 = 'rectype eq ''FILE'''
   
   
%program = new
%program = new
text to %program
text to %program
    b
    b
        %recs      is object recordSet in file sirfiled
      %recs      is object recordSet in file sirfiled
        find records to %recs
      find records to %recs
          {%criteria1}
          {%criteria1}
          {%criteria2}
          {%criteria2}
        end find
      end find
        %(daemon):returnObject(%recs)
      %(daemon):returnObject(%recs)
    end
    end
end text
end text
   
   
%daem = new
%daem = new
%daem:open('FILE SIRFILED', default=true)
%daem:open('FILE SIRFILED', default=true)
%daem:run('*LOWER')
%daem:run('*LOWER')
%daem:run(%program, , %recs)
%daem:run(%program, , %recs)
   
   
print %recs:count
print %recs:count
   
   
end
end
</p>
</p>
==Request cancelled if group field reference is nonsensical for file==
==Request cancelled if group field reference is nonsensical for file==
One of the benefits of the [[Janus SOAP ULI]] File classes is easier migration
One of the benefits of the <var class="product">SOUL</var> File classes is easier migration
between file-context and group-context User Language.
between file-context and group-context <var class="product">SOUL</var>.
In most cases, this is provided by allowing the same User Language code to
In most cases, this is provided by allowing the same <var class="product">SOUL</var> code to
operate whether in file or group context.
operate whether in file or group context.
   
   
Line 152: Line 158:
In particular, a field can be missing in file &ldquo;A&rdquo; yet defined in at least
In particular, a field can be missing in file &ldquo;A&rdquo; yet defined in at least
one other group member,
one other group member,
or it can be defined as INVISIBLE in file &ldquo;A&rdquo;
or it can be defined as <code>INVISIBLE</code> in file "A"
and yet defined and not INVISIBLE in at least
and yet defined and not <code>INVISIBLE</code> in at least one other group member.
one other group member.
When "A" is the current file, both of these situations can result in nonsensical
When &ldquo;A&rdquo; is the current file,
references to the value of that field, or nonsensical updates to that field.
both of these situations can result in nonsensical
references to the value of that field, or nonsensical
updates to that field.
   
   
With version 7.0 of [[Janus SOAP ULI]], your applications are
<!-- With version 7.0 of <var class="product">Janus SOAP ULI</var> -->
protected against nonsense field references when done in the context
Your applications are protected against nonsense field references when done in the context
of [[Janus SOAP ULI]] File objects.
of <var class="product">SOUL</var> File objects.
The '''general''' (but not exact &mdash; see [[#Exact rules of group field cancellation|Exact rules of group field cancellation]])
The '''general''' (but not exact &mdash; see [[#Exact rules of group field cancellation|"Exact rules of group field cancellation"]])
guideline is that the request is cancelled if '''both''' these
guideline is that the request is cancelled if '''both''' these conditions exist:
conditions exist:
<ul>
<ul>
<li>A record is referenced in a group context
<li>A record is referenced in a group context
bound to an object of one of the File classes.
bound to an object of one of the File classes.
<li>A field in the record is referenced which would not be
<li>A field in the record is referenced which would not be
allowed if the reference were made in single file context using the
allowed if the reference were made in single file context using the current file.
current file.
</ul>
</ul>
   
   
For example, consider the following field definitions and group
For example, consider the following field definitions and group definition:
definition:
<p class="code">IN FILE F1 DEFINE FIELD ONLY1
<p class="code"> IN FILE F1 DEFINE FIELD ONLY1
IN FILE F1 DEFINE FIELD VIS1
IN FILE F1 DEFINE FIELD VIS1
IN FILE F2 DEFINE FIELD VIS1 WITH INVISIBLE ORDERED
IN FILE F2 DEFINE FIELD VIS1 WITH INVISIBLE ORDERED
CREATE TEMP GROUP G FROM F1, F2
CREATE TEMP GROUP G FROM F1, F2
PARAMETER UPDTFILE F1
PARAMETER UPDTFILE F1
END GROUP
END GROUP
</p>
</p>
   
   
Each of the User Language statements within the following <code>For Each Record</code>
Each of the <var class="product">SOUL</var> statements within the following <code>For Each Record</code> will result in compilation errors:
will result in compilation errors:
<p class="code">In File F2 For Each Record
<p class="code"> In File F2 For Each Record
  Print ONLY1
    Print ONLY1
  Add ONLY1 = 'Field only in file F1'
    Add ONLY1 = 'Field only in file F1'
  Print VIS1
    Print VIS1
  Delete VIS1
    Delete VIS1
End For
End For
</p>
</p>
   
   
These are illegal because:
These are illegal because:
<ul>
<ul>
<li>You cannot refer to a field that is not defined (Print ONLY1 and
<li>You cannot refer to a field that is not defined (<code>Print ONLY1</code> and <code>Add ONLY1</code>).
Add ONLY1).
<li>You cannot refer to the value of a field that is <var>INVISIBLE</var> (<code>Print VIS1</code>).
<li>You cannot refer to the value of a field that is INVISIBLE (Print
<li>You cannot delete, by occurrence, a field that is <var>INVISIBLE</var> (<code>Delete VIS1</code>).
VIS1).
<li>You cannot delete, by occurrence, a field that is INVISIBLE (Delete
VIS1).
</ul>
</ul>
If you convert this application (which does not use the [[Janus SOAP ULI]] File
If you convert this application (which does not use the <var class="product">SOUL</var> File
classes) to use a group, it compiles and evaluates without any complaint:
classes) to use a group, it compiles and evaluates without any complaint:
<p class="code"> In Group G For Each Record
<p class="code">In Group G For Each Record
    Print ONLY1
  Print ONLY1
    Add ONLY1 = 'Field only in file F1'
  Add ONLY1 = 'Field only in file F1'
    Print VIS1
  Print VIS1
    Delete VIS1
  Delete VIS1
End For
End For
</p>
</p>
However, when the current file in the <code>For Each Record</code>
However, when the current file in the <code>For Each Record</code>
loop is F2, the following occur, and this '''may be an application
loop is <code>F2</code>, the following occur, and this '''may be an application error''':
error''':
<ul>
<ul>
<li>The value (<code>Print ONLY1</code>)
<li>The value (<code>Print ONLY1</code>)
of an undefined field is the null string.
of an undefined field is the null string.
<li>Updates to undefined fields (<code>Add ONLY1</code>) are ignored.
<li>Updates to undefined fields (<code>Add ONLY1</code>) are ignored.
<li>The value (<code>Print VIS1</code>)
 
of an INVISIBLE field is the null string.
<li>The value (<code>Print VIS1</code>) of an INVISIBLE field is the null string.
<li>A certain class of updates to undefined fields (<code>Delete VIS1</code>)
 
are ignored.
<li>A certain class of updates to undefined fields (<code>Delete VIS1</code>) are ignored.
</ul>
</ul>
   
   
The [[Janus SOAP ULI]] takes a more conservative approach to handling fields that
<var class="product">SOUL</var> takes a more conservative approach to handling fields that
are undefined or
are undefined or <var>INVISIBLE</var> in some members of a group, when the field references are in records
INVISIBLE in some members of a group, when the field references are in records
that are bound to an object in one of the File classes.
that are bound to an object in one of the File classes.
   
   
Using this protection, the following
Using this protection, the following request is cancelled when the first record in file <code>F2</code> is processed:
request is cancelled when the first record in file F2 is processed:
<p class="code">Begin
<p class="code"> Begin
%rs Object Recordset In Group G
%rs Object Recordset In Group G
In Group G Find To %rs
In Group G Find To %rs
End Find
End Find
For Each Record In %rs
For Each Record In %rs
  Print ONLY1
    Print ONLY1
End For
End For
End
End
</p>
</p>
   
   
Similarly, the request is cancelled when processing the first
Similarly, the request is cancelled when processing the first record in file <code>F2</code> if the
record in file F2 if the
statement in the <code>For Each Record</code> loop is any of the following:
statement in the <code>For Each Record</code> loop is any of the following:
<p class="code">   Add ONLY1 = 'Field only in file F1'
<p class="code">Add ONLY1 = 'Field only in file F1'
   
   
    Print VIS1
Print VIS1
   
   
    Delete VIS1
Delete VIS1
</p>
</p>
   
   
The above request cancellations detect two likely errors:
The above request cancellations detect two likely errors: either the field definitions in the group members are
either the field definitions in the group members are
incorrect, or the application lacks a way to determine when it makes sense to execute statements with
incorrect, or the application lacks a way to determine
references to fields undefined or <var>INVISIBLE</var> in some members of the group.
when it makes sense to execute statements with
One way your application can make this latter determination is shown in [[#Using Is Defined and Is Visible to avoid cancellation|"Using Is Defined and Is Visible to avoid cancellation"]].
references to fields undefined or INVISIBLE in some members of the group.
One way your application can make this latter
determination is shown in ??[[#Using Is Defined and Is Visible to avoid cancellation|Using Is Defined and Is Visible to avoid cancellation]].
   
   
The exact request cancellation conditions introduced are shown in
The exact request cancellation conditions introduced are shown in the next subsection.
the next subsection.
 
===Exact rules of group field cancellation===
===Exact rules of group field cancellation===
If the following conditions exist:
If the following conditions exist:
<ul>
<ul>
<li>a record is referenced in group context;
<li>A record is referenced in group context.
</ul>
</ul>
'''and'''
'''and'''
<ul>
<ul>
<li>a record is referenced in a group context
<li>A record is referenced in a group context
bound to an object of one of the File classes;
bound to an object of one of the File classes.
</ul>
</ul>
'''and'''
'''and'''
<ul>
<ul>
<li>using that record reference,
 
a field is referenced in a statement (or expression)
<li>Using that record reference, a field is referenced in a statement (or expression)
'''other than''' one of the following:
'''other than''' one of the following:
<p class="code"> Count Occurrences (CTO)
<p class="code">Count Occurrences (CTO)
For Each Occurrence (FEO)
For Each Occurrence (FEO)
Delete Each
Delete Each
Is [Not] Present
Is [Not] Present
Is [Not] Defined
Is [Not] Defined
Is [Not] Visible
Is [Not] Visible
$Field_Image (that is, an item of the argument 1 Image)
$Field_Image (that is, an item of the argument 1 Image)
$Field_ListI(that is, an item of the argument 2 Image)
$Field_ListI(that is, an item of the argument 2 Image)
Sort Records
Sort Records
</p>
</p>
</ul>
</ul>
'''and'''
'''and'''
<ul>
<ul>
<li>'''one of''' the following conditions exist:
<li>'''One of''' the following conditions exist:
<ul>
<ul>
<li>the field is not defined in the current file
<li>The field is not defined in the current file.
</ul>
</ul>
'''or'''
'''or'''
<ul>
<ul>
<li>the field is INVISIBLE in the current file
<li>The field is <var>INVISIBLE</var> in the current file
and the value of the field is referenced
and the value of the field is referenced.
</ul>
</ul>
'''or'''
'''or'''
<ul>
<ul>
<li>the field is INVISIBLE in the current file
<li>The field is <var>INVISIBLE</var> in the current file
and is referenced in a Note statement
and is referenced in a <var>Note</var> statement.
</ul>
</ul>
'''or'''
'''or'''
<ul>
<ul>
<li>the field is INVISIBLE in the current file
<li>The field is <var>INVISIBLE</var> in the current file
and the field is updated by a &ldquo;CHANGE field To newval&rdquo;
and the field is updated by a <code>CHANGE field To newval</code>
(''not'' &ldquo;CHANGE field = oldval To newval&rdquo;) statement
(''not'' <code>CHANGE field = oldval To newval</code>) statement.
</ul>
</ul>
'''or'''
'''or'''
<ul>
<ul>
<li>the field is INVISIBLE in the current file
<li>The field is <var>INVISIBLE</var> in the current file
and the field is updated by a &ldquo;DELETE field occurrence&rdquo;
and the field is updated by a <code>DELETE field occurrence</code>
(''not'' &ldquo;DELETE field = vale&rdquo;) statement
(''not'' <code>DELETE field = value</code>) statement.
</ul>
</ul>
</ul>
</ul>
'''then'''
'''Then''' the request will be cancelled.
the request will be cancelled.
   
   
'''Note:'''
<p class="note">'''Note:'''
In addition to the list of statements above (CTO, FEO, etc.)
In addition to the list of statements above (<var>CTO</var>, <var>FEO</var>, etc.)
which do not cause request cancellation, a few updating
which do not cause request cancellation, a few updating
statements are shown in the next list, and updating
statements are shown in the next list, and updating
statements for INVISIBLE fields are shown in the list after that,
statements for <var>INVISIBLE</var> fields are shown in the list after that,
which do not cause request cancellation.
which do not cause request cancellation. </p>
 
==Other statements that do not cancel request==
==Other statements that do not cancel request==
One of the conditions that is necessary for request cancellation
One of the conditions that is necessary for request cancellation
due to nonsensical field references is that
due to nonsensical field references is that a record referenced in a group context is
a record referenced in a group context is
bound to an object of one of the File classes, and that a field is referenced using that record.
bound to an object of one of the File classes, and that a field
The following <var class="product">SOUL</var> statements refer to fields but
is referenced using that record.
The following User Language statements refer to fields but
are not in a record context, so they are not subject to
are not in a record context, so they are not subject to
the [[Janus SOAP ULI]] nonsensical-field-reference protection:
the <var class="product">SOUL</var> nonsensical-field-reference protection:
<ul>
<ul>
<li>Store Record
<li><var>Store Record</var>
<li>File Records Under
<li><var>File Records Under</var>
<li>For Each Value
<li><var>For Each Value</var>
<li>Find Records
<li><var>Find Records</var>
<li>Find And Print Count
<li><var>Find And Print Count</var>
<li>&ldquo;Where&rdquo; and &ldquo;Order By&rdquo; clauses of For Each Record
<li><var>Where</var> and <var>Order By</var> clauses of <var>For Each Record</var>
<li>Find Values
<li><var>Find Values</var>
</ul>
</ul>
   
   
Also, the following updating statements are allowed for INVISIBLE
Also, the following updating statements are allowed for <var>INVISIBLE</var>
fields, so they do not cause request cancellation if the field is
fields, so they do not cause request cancellation if the field is
INVISIBLE in the current file:
<var>INVISIBLE</var> in the current file:
<ul>
<ul>
<li>Add
<li><var>Add</var>
<li>Change field = oldval To newval
<li><code>Change field = oldval To newval</code>
<li>Delete field = value
<li><code>Delete field = value</code>
<li>Insert
<li><var>Insert</var>
</ul>
</ul>
===Using Is Defined and Is Visible to avoid cancellation===
===Using Is Defined and Is Visible to avoid cancellation===
Using the same field and group definitions from ??[[#Request cancelled if group field reference is nonsensical for file|Request cancelled if group field reference
Using the same field and group definitions from [[#Request cancelled if group field reference is nonsensical for file|"Request cancelled if group field reference is nonsensical for file"]], here is a <var class="product">SOUL</var> request that skips nonsensical field references:
is nonsensical for file]],
<p class="code">Begin
here is a User Language request that skips nonsensical field references:
%rs Object Recordset In Group G
<p class="code"> Begin
In Group G Find To %rs
%rs Object Recordset In Group G
End Find
In Group G Find To %rs
For Each Record In %rs
End Find
  If ONLY1 Is Defined Then
For Each Record In %rs
      Print ONLY1
    If ONLY1 Is Defined Then
      Add ONLY1 = 'Field only in file F1'
      Print ONLY1
  End If
      Add ONLY1 = 'Field only in file F1'
  If VIS1 Is Visible Then
    End If
      Print VIS1
    If VIS1 Is Visible Then
      Delete VIS1
      Print VIS1
  End If
      Delete VIS1
End For
    End If
End
End For
End
</p>
</p>
Note that this is not the same result as the second User Language example
Note that this is not the same result as the second <var class="product">SOUL</var> example
(&ldquo;In Group G For Each Record&rdquo;) in
(<code>In Group G For Each Record</code>) in
??[[#Request cancelled if group field reference is nonsensical for file|Request cancelled if group field reference is nonsensical for file]].
[[#Request cancelled if group field reference is nonsensical for file|"Request cancelled if group field reference is nonsensical for file"]].
In that example, two blank lines are printed for each record in file F2 (by the
In that example, two blank lines are printed for each record in file <code>F2</code> (by the
<code>Print ONLY1</code> and
<code>Print ONLY1</code> and <code>Print VIS1</code> statements).
<code>Print VIS1</code> statements).
 
<!-- FOLLOWING CAUSES EACH H2 TO GET A NEW PAGE; EXTENDS TO SUBSEQUENT CHAPS UNLESS RESET -->
[[Category:Overviews]]
.sr @h2@cct = &@h2@cc
.sr @h2@cc = 65

Latest revision as of 11:45, 13 April 2015


One of the great strengths of SOUL is the tight integration of the database Manipulation Language, often abbreviated DML, with the programming language. That is, statements like Find, For Each Record, and For Each Occurrence can be easily embedded in SOUL procedure code. Embedding some of these statements in methods, however, can be problematic, and it would be very useful to have object-oriented versions of some standard SOUL file-oriented facilities.

For example, one could execute a Find statement inside a method:

class customer ... subroutine getRecords custRecs: in file customer fd custid = %custId end find ... end subroutine end class

But it would be impossible to use such a found set between methods in the class or to hold the locks on the records while an instance of a customer object exists. Similarly, it might be useful to be able to hold a record open for an instance of a class.

SOUL provides these capabilities with record set, record, and sorted record set objects. The classes, associated with Model 204 file structures, are called file classes, though they can also reference groups.

The names of these classes are:

Declaring file objects

One unusual aspect of file classes is that a reference to a file object will not only affect the statement in which it appears, but will sometimes establish a context for the compiler to resolve field names. For this to be accomplished, the file or group context of a file object must be known at compile-time, so this context must be specified in a file object declaration. The syntax of this declaration follows:

{variable} [Is] Object fileClassName - In [ [Perm | Temp] Group | File ] fgname - [ Array(arraySize) ] [ Global[(globalName)] ]


variable The name of the file object variable.

If outside a class declaration block and structure, the variable must begin with a percent sign (%). If inside a structure declaration, the variable must not begin with a percent sign.

If inside a class declaration block, the variable cannot start with a percent sign and must be preceded by the word Variable.
fileClassName The name of a file class. Options are the Recordset, Record, RecordsetCursor, and SortedRecordset classes.
fgname The file or group name. If this name is not preceded by keywords that explicitly identify the file or group context, the Model 204 search ordering is followed to determine the reference context: temporary groups first, then open permanent groups, then open files.
arraySize The number of elements in the object array, if it is an array.
globalname The optional global name, if the collection variable is not a class or structure variable.

If Global is specified without globalName, the name of the variable without a percent sign is used as the global name.

An object array cannot be Global.

Because they are objects, file objects can be assigned or compared to each other, but only if the variables have the same class and the same file/group context. As just two examples, you cannot assign a Record object variable to a Recordset object variable, and you cannot assign a Recordset In File Ccasys object variable to a Recordset In Group Customer object variable.

When invoking a shared method in a file class using the class name, the file/group context must be specified along with the class name:

%productRec is object record in file products ... %productRec = %(record in file products):new(%n)

This example can also be written, of course, as:

%productRec is object record in file products ... %productRec = new(%n)

DeepCopy for file objects

The Record, Recordset, and SortedRecordset objects are deep-copyable. This means that these objects can be contained inside SOUL classes and those classes, themselves, could be deep-copyable.

Perhaps even more importantly, this means that these objects can be passed back and forth between master and daemon threads, via the Run, GetInputObject, ReturnObject, and ReturnInfoObject methods. Among other things, this makes it easy to dynamically generate Find statements that run on a daemon thread and then use the resulting found set on the master thread:

b %recs is object recordSet in file sirfiled %daem is object daemon %program is object stringList %criteria1 is string len 32 %criteria2 is string len 32 %criteria1 = 'rectype eq FILE' %program = new text to %program b %recs is object recordSet in file sirfiled find records to %recs {%criteria1} {%criteria2} end find  %(daemon):returnObject(%recs) end end text %daem = new %daem:open('FILE SIRFILED', default=true) %daem:run('*LOWER') %daem:run(%program, , %recs) print %recs:count end

Request cancelled if group field reference is nonsensical for file

One of the benefits of the SOUL File classes is easier migration between file-context and group-context SOUL. In most cases, this is provided by allowing the same SOUL code to operate whether in file or group context.

However, there is an opportunity for undetected errors when migrating an application from file to group context. One flexibility of Model 204 groups is that field definitions of the individual files can differ. In particular, a field can be missing in file “A” yet defined in at least one other group member, or it can be defined as INVISIBLE in file "A" and yet defined and not INVISIBLE in at least one other group member. When "A" is the current file, both of these situations can result in nonsensical references to the value of that field, or nonsensical updates to that field.

Your applications are protected against nonsense field references when done in the context of SOUL File objects. The general (but not exact — see "Exact rules of group field cancellation") guideline is that the request is cancelled if both these conditions exist:

  • A record is referenced in a group context bound to an object of one of the File classes.
  • A field in the record is referenced which would not be allowed if the reference were made in single file context using the current file.

For example, consider the following field definitions and group definition:

IN FILE F1 DEFINE FIELD ONLY1 IN FILE F1 DEFINE FIELD VIS1 IN FILE F2 DEFINE FIELD VIS1 WITH INVISIBLE ORDERED CREATE TEMP GROUP G FROM F1, F2 PARAMETER UPDTFILE F1 END GROUP

Each of the SOUL statements within the following For Each Record will result in compilation errors:

In File F2 For Each Record Print ONLY1 Add ONLY1 = 'Field only in file F1' Print VIS1 Delete VIS1 End For

These are illegal because:

  • You cannot refer to a field that is not defined (Print ONLY1 and Add ONLY1).
  • You cannot refer to the value of a field that is INVISIBLE (Print VIS1).
  • You cannot delete, by occurrence, a field that is INVISIBLE (Delete VIS1).

If you convert this application (which does not use the SOUL File classes) to use a group, it compiles and evaluates without any complaint:

In Group G For Each Record Print ONLY1 Add ONLY1 = 'Field only in file F1' Print VIS1 Delete VIS1 End For

However, when the current file in the For Each Record loop is F2, the following occur, and this may be an application error:

  • The value (Print ONLY1) of an undefined field is the null string.
  • Updates to undefined fields (Add ONLY1) are ignored.
  • The value (Print VIS1) of an INVISIBLE field is the null string.
  • A certain class of updates to undefined fields (Delete VIS1) are ignored.

SOUL takes a more conservative approach to handling fields that are undefined or INVISIBLE in some members of a group, when the field references are in records that are bound to an object in one of the File classes.

Using this protection, the following request is cancelled when the first record in file F2 is processed:

Begin %rs Object Recordset In Group G In Group G Find To %rs End Find For Each Record In %rs Print ONLY1 End For End

Similarly, the request is cancelled when processing the first record in file F2 if the statement in the For Each Record loop is any of the following:

Add ONLY1 = 'Field only in file F1' Print VIS1 Delete VIS1

The above request cancellations detect two likely errors: either the field definitions in the group members are incorrect, or the application lacks a way to determine when it makes sense to execute statements with references to fields undefined or INVISIBLE in some members of the group. One way your application can make this latter determination is shown in "Using Is Defined and Is Visible to avoid cancellation".

The exact request cancellation conditions introduced are shown in the next subsection.

Exact rules of group field cancellation

If the following conditions exist:

  • A record is referenced in group context.

and

  • A record is referenced in a group context bound to an object of one of the File classes.

and

  • Using that record reference, a field is referenced in a statement (or expression) other than one of the following:

    Count Occurrences (CTO) For Each Occurrence (FEO) Delete Each Is [Not] Present Is [Not] Defined Is [Not] Visible $Field_Image (that is, an item of the argument 1 Image) $Field_ListI(that is, an item of the argument 2 Image) Sort Records

and

  • One of the following conditions exist:
    • The field is not defined in the current file.

    or

    • The field is INVISIBLE in the current file and the value of the field is referenced.

    or

    • The field is INVISIBLE in the current file and is referenced in a Note statement.

    or

    • The field is INVISIBLE in the current file and the field is updated by a CHANGE field To newval (not CHANGE field = oldval To newval) statement.

    or

    • The field is INVISIBLE in the current file and the field is updated by a DELETE field occurrence (not DELETE field = value) statement.

Then the request will be cancelled.

Note: In addition to the list of statements above (CTO, FEO, etc.) which do not cause request cancellation, a few updating statements are shown in the next list, and updating statements for INVISIBLE fields are shown in the list after that, which do not cause request cancellation.

Other statements that do not cancel request

One of the conditions that is necessary for request cancellation due to nonsensical field references is that a record referenced in a group context is bound to an object of one of the File classes, and that a field is referenced using that record. The following SOUL statements refer to fields but are not in a record context, so they are not subject to the SOUL nonsensical-field-reference protection:

  • Store Record
  • File Records Under
  • For Each Value
  • Find Records
  • Find And Print Count
  • Where and Order By clauses of For Each Record
  • Find Values

Also, the following updating statements are allowed for INVISIBLE fields, so they do not cause request cancellation if the field is INVISIBLE in the current file:

  • Add
  • Change field = oldval To newval
  • Delete field = value
  • Insert

Using Is Defined and Is Visible to avoid cancellation

Using the same field and group definitions from "Request cancelled if group field reference is nonsensical for file", here is a SOUL request that skips nonsensical field references:

Begin %rs Object Recordset In Group G In Group G Find To %rs End Find For Each Record In %rs If ONLY1 Is Defined Then Print ONLY1 Add ONLY1 = 'Field only in file F1' End If If VIS1 Is Visible Then Print VIS1 Delete VIS1 End If End For End

Note that this is not the same result as the second SOUL example (In Group G For Each Record) in "Request cancelled if group field reference is nonsensical for file". In that example, two blank lines are printed for each record in file F2 (by the Print ONLY1 and Print VIS1 statements).