Anda di halaman 1dari 10

1

Using Standalone Rowsets to Write a File


The following is an example of using standalone rowsets along with a file layout to write a file: writes a file using a file layout that contains parent-child records:

Local File &MYFILE; Local Rowset &rsBusExp, &rsBusExpPer, &rsBusExpDtl; Local Record &rBusExp, &rBusExpPer, &rBusExpDtl; Local SQL &SQL1, &SQL2, &SQL3; &rBusExp = CreateRecord(Record.PERSONAL_DATA); &rBusExpPer = CreateRecord(Record.BUS_EXPENSE_PER); &rBusExpDtl = CreateRecord(Record.BUS_EXPENSE_DTL); &rsBusExp = CreateRowset(Record.PERSONAL_DATA, CreateRowset(Record.BUS_EXPENSE_PER,
CreateRowset(Record.BUS_EXPENSE_DTL)));

&rsBusExpPer = &rsBusExp.GetRow(1).GetRowset(1); &MYFILE = GetFile("c:\temp\BUS_EXP.out", "W", %FilePath_Absolute); &MYFILE.SetFileLayout(FileLayout.BUS_EXP_OUT); &EMPLID = "8001"; &SQL1 = CreateSQL("%selectall(:1) where EMPLID = :2", &rBusExp, &EMPLID); &SQL2 = CreateSQL("%selectall(:1) where EMPLID = :2", &rBusExpPer, &EMPLID); While &SQL1.Fetch(&rBusExp)&rBusExp.CopyFieldsTo(&rsBusExp.GetRow(1).PERSONAL_DATA); &I = 1; While &SQL2.Fetch(&rBusExpPer)&rBusExpPer.CopyFieldsTo(&rsBusExpPer(&I).BUS_EXPENSE_PER); &J = 1; &SQL3 = CreateSQL("%selectall(:1) where EMPLID = :2 and EXPENSE_PERIOD_DT = :3",
&rBusExpDtl, &EMPLID,&rsBusExpPer(&I).BUS_EXPENSE_PER.EXPENSE_PERIOD_DT.Value);

&rsBusExpDtl = &rsBusExpPer.GetRow(&I).GetRowset(1); While &SQL3.Fetch(&rBusExpDtl)&rBusExpDtl.CopyFieldsTo(&rsBusExpDtl(&J).BUS_EXPENSE_DTL); &rsBusExpDtl.InsertRow(&J); &J = &J + 1; &rsBusExpPer.InsertRow(&I); &I = &I + 1; &MYFILE.WriteRowset(&rsBusExp); End-While; End-While; End-While;

&MYFILE.Close();

2 The previous code generates the following output file.


AA8001 Schumacher,Simon BB8001 06/11/1989YNA0 Customer Go-Live Celebration CC8001 06/11/1989 06/01/198908226.83 USDEntertain Clients 10100 BB8001 08/31/1989YNA0 Customer Focus Group Meeting CC8001 08/31/198908/11/1989012401.58 USDCustomer Visit 10100 CC8001 08/31/198908/12/198904250.48 USDCustomer Visit 10100 CC8001 08/31/198908/12/198902498.34 USDCustomer Visit 10100 BB8001 03/01/1998YYP0 Attend Asia/Pacific Conference CC8001 03/01/199802/15/1998011200 USDConference

Using Standalone Rowsets to Read a File The following code shows an example of reading in a file and inserting the rows into the database:
Local File &MYFILE; Local Rowset &rsBusExp, &rsBusExpPer, &rsBusExpDtl; Local Record &rBusExp, &rBusExpPer, &rBusExpDtl; Local SQL &SQL1; &rBusExp = CreateRecord(Record.PERSONAL_DATA); &rBusExpPer = CreateRecord(Record.BUS_EXPENSE_PER); &rBusExpDtl = CreateRecord(Record.BUS_EXPENSE_DTL); &rsBusExp = CreateRowset(Record.PERSONAL_DATA,
CreateRowset(Record.BUS_EXPENSE_PER,CreateRowset(Record.BUS_EXPENSE_DTL)));

&MYFILE = GetFile("c:\temp\BUS_EXP.out", "R", %FilePath_Absolute); &MYFILE.SetFileLayout(FileLayout.BUS_EXP_OUT); &SQL1 = CreateSQL("%Insert(:1)"); &rsBusExp = &MYFILE.ReadRowset(); While &rsBusExp <> Null; &rsBusExp.GetRow(1).PERSONAL_DATA.CopyFieldsTo(&rBusExp); &rsBusExpPer = &rsBusExp.GetRow(1).GetRowset(1); For &I = 1 To &rsBusExpPer.ActiveRowCount &rsBusExpPer(&I).BUS_EXPENSE_PER.CopyFieldsTo(&rBusExpPer); &rBusExpPer.ExecuteEdits(%Edit_Required); If &rBusExpPer.IsEditError Then For &K = 1 To &rBusExpPer.FieldCount &MYFIELD = &rBusExpPer.GetField(&K); If &MYFIELD.EditError Then
&MSGNUM = &MYFIELD.MessageNumber; &MSGSET = &MYFIELD.MessageSetNumber; End-If;End-For;

Else &SQL1.Execute(&rBusExpPer); &rsBusExpDtl = &rsBusExpPer.GetRow(&I).GetRowset(1); For &J = 1 To &rsBusExpDtl.ActiveRowCount &rsBusExpDtl(&J).BUS_EXPENSE_DTL.CopyFieldsTo(&rBusExpDtl); &rBusExpDtl.ExecuteEdits(%Edit_Required); If &rBusExpDtl.IsEditError Then For &K = 1 To &rBusExpDtl.FieldCount &MYFIELD = &rBusExpDtl.GetField(&K); If &MYFIELD.EditError Then
&MSGNUM = &MYFIELD.MessageNumber; &MSGSET = &MYFIELD.MessageSetNumber; End-If;End-For;

Else &SQL1.Execute(&rBusExpDtl); End-If; &rsBusExp = &MYFILE.ReadRowset(); End-While;

End-For; End-If; End-For; &MYFILE.Close();

Using Errors and Warnings


For the most part, errors and warnings display messages to users informing them about invalid data. For this reason, they are almost always placed in FieldEdit or SaveEdit PeopleCode, or in SearchSave PeopleCode for validation during search processing. In conjunction with edits, errors stop processing, while warnings allow processing to continue. When errors and warnings appear in places other than FieldEdit or SaveEdit, their effects vary. This section discusses how to: Use errors and warning syntax. Use errors and warnings in edit events. Use errors and warnings in RowSelect events. Use errors and warnings in RowDelete events. Use errors and warnings in other events.

Using Error and Warning Syntax


Errors and warnings require only a message that the Component Processor displays to users. You can code the message into the error or warning statement, or you can use the message catalog. Use the message catalog with the MsgGet, MsgGetExplainText, and similar functions. Errors and warnings use the same syntax. For example:
Error MsgGet(11100, 180, "Message not found."); Warning MsgGet(11100, 180, "Message not found.");

Using Errors and Warnings in Edit Events


You can use the following PeopleCode events for validation edits: FieldEdit and SaveEdit. The Component Processor applies FieldEdit when the user changes a field, and SaveEdit when the user saves a component. Errors and warnings in these events display a message. Most errors and warnings appear in these event types, although you can use errors and warnings elsewhere. FieldEdit Event Errors You can use either the record field or component record field event. The record field event for each record runs before the component record field event for that record. An error in FieldEdit prevents the system from accepting the new value of a field. The Component Processor highlights the problem field. The user must either change the field back to its original value or to something else which does not trigger the error. A warning enables the Component Processor to accept the new data. The Component Processor does not highlight a field that has warnings. SaveEdit Event Errors You can use the record field or the component record event. All record field events for a record run before the component record events.
An error in SaveEdit prevents the system from saving any row of data. The Component Processor does not update the database for any field if one field has an error. Although the Component Processor displays an error message, it does not turn any field red. Unlike FieldEdit errors, SaveEdit errors can happen anywhere on a page or component, for any row of data. The data causing the error may appear on a different page within the same group, or a row of data not currently displayed. If this is the case, the field in error is brought into view by the system. A warning in SaveEdit also is applied to all data in the page or component, but the Component Processor will accept the data, if told to by the user. In a FieldEdit warning, the Component Processor displays a message box with the text and two buttons: OK and the standard Explain (the Explain button returns an explanation for the last message retrieved with the MsgGet function). In a SaveEdit warning, the message box contains an additional button, Cancel. OK accepts the data, overriding the warning and continuing the save process. Cancel ends the save process. Because errors and warnings apply to all rows of data and all pages in a group, you must provide the user explicit information about what caused the error. Typically, you use the message catalog function to store messages and substitute variables into them. However, you can also facilitate this by concatenating in a field value. For example, if you have a stack of historical data on the page, you could use the following error statement: Error ("The value exceeds the maximum on "|effdt|".");

Using Errors and Warnings in RowSelect Events


RowSelect PeopleCode filters out rows of data after the system applies search record criteria. It also can stop the Component Processor from reading additional rows of data. Note. Errors and warnings should no longer be used in RowSelect processing; instead, use DiscardRow and StopFetching. The behavior of errors and warnings in RowSelect PeopleCode is retained for compatibility with previous releases of PeopleTools. A warning causes the Component Processor to reject the current row, but the Component Processor continues reading more data. An error prevents more data coming into the page or component. The Component Processor accepts the row that causes the error, but does not read any more data. To reject the current row and stop loading additional rows, issue a warning and an error. You must specify text for an error or warning, but the Component Processor does not display messages from RowSelect. You can still use the message text as a way of documenting the program. DiscardRow StopFetching

Using Errors and Warnings in RowDelete Events


When you delete a row of data, the system prompts you to confirm. If you confirm, any record field RowDelete PeopleCode runs, and any component record RowDelete PeopleCode also runs. Errors and warnings in RowDelete display a message box. A warning from RowDelete presents two choices: accept the RowDelete (the OK button), or cancel the RowDelete (the Cancel button). An error from RowDelete PeopleCode prevents the Component Processor from removing that row of data from the page.

Using Errors and Warnings in Other Events


Do not put errors or warning in PeopleCode attached to the FieldDefault, FieldFormula, RowInit, FieldChange, RowInsert, SavePreChange, WorkFlow, and SavePostChange events. These event types activate processing that a user has no direct control over. However, the Component Processor may issue its own errors and warnings when it runs PeopleCode and encounters an unrecoverable error. The Component Processor cancels the transaction to avoid unpredictable results.

CreateArray

CreateArray(paramlist) Where paramlist is an arbitrary-length list of values in the form: param1 [, param2] ...

Use the CreateArray function to construct an array and returns a reference to it.
The type of the first parameter determines the type of array that is built. That is, if the first parameter is of type NUMBER, an array of number is built. If there is no first parameter an empty array of ANY is built.

It uses flattening and promotion as required to convert subsequent values into suitable elements of the array.

Parameters Returns

paramlist Specify a list of values to be used as the elements of the array.

Returns a reference to the array.

Local Array of Array of Number &AAN; Local Array of Number &AN; &AAN = CreateArray(CreateArray(1, 2), CreateArray(3, 4), 5); &AN = CreateArray(6, &AAN[1]);

&AAN is a two dimensional array with three elements:


A one-dimensional array with 1 and 2 as elements. A one-dimensional array with only the element 5. A one-dimensional array with 3 and 4.

The last parameter to Array was promoted to a one-dimensional array. &AN is a one-dimensional array with 3 elements: 6, 1, and 2. The last parameter to Array in the last line was flattened into its two elements.

CreateArrayAny

CreateArrayAny([paramlist]) Where paramlist is an arbitrary-length list of values in the form: param1 [, param2] ...

Use to construct an array whose elements are of data type ANY and returns a reference to it.
It uses flattening and promotion as required to convert subsequent values into suitable elements of the array. If you do not specify any parameters, it's the same as using the CreateArray function without any parameters.
If you do not know how many values are needed in a SQL statement until runtime, you can use an array of any to supply the values.

Parameters Returns

paramlist Specify a list of values to be used as the elements of the array.

Returns a reference to an array of ANY.


CreateArray(CreateArrayAny(1, 2), CreateArrayAny("hi"), "there");

local Array of Any &ArrayAny = CreateArrayAny(1, 2, "hi", "there"); local Array of Array of Any &AAAny = &ArrayAny is a 2D-array with four elements: 1, 2, "hi" and "there". All the elements have the data type Any. &AAAny is a 2-D array with three elements: a one-dimensional array with 1 and 2 as elements, a 1-D array with "hi" as its element, and a one-dimensional array with only the element "there". The last parameter to the array was promoted to a one-dimensional array.

CreateArrayRept

CreateArrayRept(val, count) Use to create an array that contains count copies of val. If val is itself an array, the created array has one higher dimension, and each element (sub-array) is the array reference val. The type of the first parameter (val) determines the type of array that is built. That is, if the first parameter is of type NUMBER, an array of number is built. If count is zero, CreateArrayRept creates an empty array,using the val parameter for the type. If you are making an array that is multi-dimensional, val will be the subarray used as the elements. To make the subarrays distinct, use the Clone method. For example: &A = CreateArrayRept(&AN, 4).Clone(); Parameters val A value of any type. count The number of copies of val contained in the array.

Returns

Returns a reference to the array.

The following code sets &A to a new empty array of string: &A = CreateArrayRept("", 0); The following code sets &A to a new array of ten zeroes: &A = CreateArrayRept(0, 10); The following code sets &AAS to a new array of array of strings, with two subarrays:
&AAS = CreateArrayRept(CreateArray("one", "two"), 2);

Note that in this case, both elements (rows) of &AAS contain the same subarray, and changing the value of an element in one of them changes it in both of them. To get distinct subarrays, use the Clone method:
&AAS = CreateArrayRept(CreateArray("one", "two"), 2).Clone();

The following example shows how to create a two-dimension array using CreateArrayRept and Push. In addition, it shows how to randomly assigns values to the cells in a two-dimension array.
Local array of array of string &ValueArray; &Dim1 = 10; &ValueArray = CreateArrayRept(CreateArrayRept("", 0), 0); For &I = 1 To &Dim1 &ValueArray.Push(CreateArrayRept("", &Dim2)); End-For; &ValueArray[1][1] = "V11"; &ValueArray[2][1] = "V21"; WinMessage &Dim2 = 5;

("&ValueArray[1][1] = " | &ValueArray[1][1] | " " | "&ValueArray[2][1] = " | &ValueArray[2][1], 0);

CreateProcessRequest
Description

CreateProcessRequest([ProcessType,ProcessName])

Use to create a ProcessRequest object. After you've created this object, you can assign values to its properties then use the Schedule method to submit the process request for scheduling. If you specify PSJob for the process type, the ProcessRequest object contains all the items of the job.

Parameters
ProcessType Specify the process type as a string. Values depend on the process types defined for your system. ProcessName Specify the name of the process as a string.

Returns

A reference to a ProcessRequest object.

Local ProcessRequest &MYRQST; &MYRQST = CreateProcessRequest("PSJOB", &MyJobName);

CreateRecord

CreateRecord(RECORD.recname)

Use to create a standalone record definition and its component set of field objects. The specified record must have been defined previously, that is, it must have a record definition. However, if you are calling this function from PeopleCode associated with a page, the record does not have to be included on the current page. The record and field objects created by this function are accessible only within PeopleCode. They can be used with any of the record and field object methods and properties. The record and field objects are automatically deleted when there are no remaining references to them stored in any variables. The fields created by this function are initialized to null values. Default processing is not performed. No data associated with the record definition's SQL table is brought in: only the record definition. You can select into a record object created this way using the SelectByKey record class method. You can also select into it using the SQLExec function.

Parameters RECORD.recname Specify a record definition that already exists. Returns This function returns a record object that references a new record buffer and set of fields. Example
Local Record &REC2; &REC2 = CreateRecord(RECORD.OPC_METH);

7 In the following example, a free-standing record is created (&PSBATREPREQRES). Values are assigned to the fields associated with the record. Then a second record is created (&PUBHDR), and the values from the first record are used to populate the second record.
&PSBATREPREQRES = CreateRecord(RECORD.PSBATREPREQRES); &PSBATREPREQRES.BATREPID.Value = &BATREPID; &PSBATREPREQRES.PUBID.Value = &MSG.Pubid; &PSBATREPREQRES.CHNLNAME.Value = &MSG.ChannelName; &PSBATREPREQRES.PUBNODE.Value = &MSG.PubNodeName; &PSBATREPREQRES.MSGNAME.Value = &MSG.Name; &PUBHDR = CreateRecord(RECORD.PSAPMSGPUBHDR); &PSBATREPREQRES.CopyFieldsTo(&PUBHDR);

To create a PeopleCode record object for a record whose name is unknown when the PeopleCode is written, do the following. Suppose a record name is in the PeopleCode variable &RECNAME. Use the @ operator to convert the string to a component name. The following code creates a corresponding record object:
&RECNAME = "RECORD." | Upper(&RECNAME); &REC = CreateRecord(@ &RECNAME);

The following example uses SQLExec to select into a record object, based on the effective date.
Local Record &DST; &DST = CreateRecord(RECORD.DST_CODE_TBL); &DST.SETID.Value = GetSetId(FIELD.BUSINESS_UNIT, DRAFT_BU, RECORD.DST_CODE_TYPE,""); &DST.DST_ID.Value = DST_ID_AR; SQLExec("%SelectByKeyEffDt(:1,:2)", &DST, %Date, &DST); /* do further processing using record methods and properties */

CreateRowset

CreateRowset({RECORD.recname | &Rowset} [,
{FIELD.fieldname,RECORD.recname |&Rowset}] . . .)

Use to create an unpopulated, standalone rowset. A standalone rowset is a rowset that has the specified structure, but is not tied to any data (that is, to the component buffer or to a message.) In addition, a standalone rowset isn't tied to the Component Processor. When you fill it with data, no PeopleCode runs (like RowInsert, FieldDefault, and so on.) The first parameter determines the structure of the rowset to be created. If you specify a record as the first parameter, it's used as the primary level 0 record. If you don't specify any other parameters, you create a rowset containing one row, with one unpopulated record. To populate this type of rowset with data, you should only use: the Fill or FillAppend rowset class methods a record method (SelectByKey) the SQLExec function If you specify a rowset object, you are creating a new rowset based on the structure of the specified rowset object, including any child rowsets. It will not contain any data. If you want to populate this type of rowset with data, use the CopyTo method or a SQL statement. Note. You should not use the rowset Select or SelectNew methods for populating rowsets created using CreateRowset. Use Fill or FillAppend instead. Restrictions on Using CreateRowset The following methods and properties don't work with a rowset created using CreateRowset: Select SelectNew Any GUI methods (like HideAllRows) Any effective date methods or properties (like EffDt, EffSeq, or GetCurrEffRow) In addition, rowsets created using CreateRowset are not automatically tied to the database. This means if you insert or delete rows, the rows will not be inserted or deleted in the database when you save the page.

Parameters

RECORD.recname | &Rowset Specify either a record name or an existing rowset object.

8
FIELD.fieldname,RECORD.recname | &Rowset Use FIELD.fieldname,RECORD.recname to specify a related display record.FIELD.fieldname refers to the controlling field, (not the related display field) while RECORD.recname refers to the related display record. If you specify &rowset, you are adding a child rowset object to the newly created rowset. This must be an existing rowset object.

Returns An unpopulated, standalone rowset object. Example The following creates a simple rowset of just a single record per row:
&RS = CreateRowset(RECORD.QA_MYRECORD);

The following creates a rowset with the same structure as the specified rowset:
&RS2 = CreateRowset(&RS);

The following code creates a rowset structure composed of four records in an hierarchical structure, that is,
QA_INVEST_HDR QA_INVEST_LN QA_INVEST_TRANS QA_INVEST_DTL

Note that you have to start at the bottom of the hierarchy, and add the upper levels, not the other way around.
Local Rowset &RS, &RS2, &RS_FINAL; &RS2 = CreateRowset(RECORD.QA_INVEST_DTL); &RS = CreateRowset(RECORD.QA_INVEST_TRANS, &RS2); &RS2 = CreateRowset(RECORD.QA_INVEST_LN, &RS); &RS_FINAL = CreateRowset(RECORD.QA_INVEST_HDR, &RS2);

The following example reads all of the QA_MYRECORD records into a rowset, and returns the number of rows read:
&RS = CreateRowset(RECORD.QA_MYRECORD); &NUM_READ = &RS.Fill();

To make a clone of an existing rowset, that is, to make two distinct copies, you can do the following:
&RS2 = CreateRowset(&RS); &RS.CopyTo(&RS2);

The following code example is used for creating multiple children in a standalone rowset:
Local Rowset &rsBOCMRole, &rsBOCMRel, &rsBOCMUse; &rsBOCMRole = CreateRowset(Record.BO_CM_ROLE); &rsBOCMRel = CreateRowset(Record.BO_CM_REL); &rsBOCMUse = CreateRowset(Record.BO_CM_USE); &rsBOCM = CreateRowset(Record.BO_CM, &rsBOCMUse, &rsBOCMRole, &rsBOCMRel);

CreateRowsetCache

CreateRowsetCache(&Rowset, [Rowset.]Name,Description)

Use the CreateRowsetCache function to create a new RowsetCache object with the given name if it doesn't already exist.

Parameters &Rowset Specify an already instantiated and populated rowset that you want to use for
creating a RowsetCache object. The RowsetCache object will have the same format and data as &Rowset. Record.Name Specify the name of the created RowsetCache object. If you just specify name, you must enclose the name in quotation marks. Description Specify a description of the RowsetCache as a string.

Returns

A reference to the new RowsetCache object if there is not already a RowsetCache object of the given name.

Local RowsetCache &Cache; Local Rowset &RS; &RS = CreateRowset(Record.PSLANGUAGES); &NUM_READ = &RS.Fill(); &Cache = CreateRowsetCache(&RS, "AAROWSET1", "ROWSET_AAROWSET1");

CreateSQL

Syntax
CreateSQL([{sqlstring | SQL.SqlName}[, paramlist]])
Chapter 1 PeopleCode Built-in Functions Copyright 1988, 2010, Oracle and/or its affiliates. All Rights Reserved. 163

Where paramlist is an arbitrary-length list of values in the form:


inval1 [, inval2] ...

Description
Use the CreateSQL function to instantiate a SQL object from the SQL class and opens it on the given sqlstring and input values. sqlstring is a PeopleCode string value giving the SQL statement. Any errors in the SQL processing cause the PeopleCode program to be terminated with an error message. You can use CreateSQL with no parameters to create an empty SQL object that can be used to assign properties before being populated and executed. Opening and Processing sqlstring If sqlstring is a SELECT statement, it is immediately bound with the inval input values and executed. The SQL object should subsequently be the subject of a series of Fetch method calls to retrieve the selected rows. If you want to fetch only a single row, use the SQLExec function instead. If you want to fetch a single row into a PeopleCode record object, use the record Select method. If sqlstring is not a SELECT statement, and either there are some inval parameters, or there are no bind placeholders in the SQL statement, the statement is immediately bound and executed. This means that there is nothing further to be done with the SQL statement and the IsOpen property of the returned SQL object will be False. In this case, using the SQLExec function would generally be better. If you want to delete, insert or update a record object, use the record Delete, Insert, or Update methods. If sqlstring is not a SELECT statement, there are no inval parameters, and there are bind placeholders in the SQL statement, the statement is neither bound nor executed. The resulting SQL object should subsequently be the subject of a series of Execute method calls to affect the desired rows. Using Arrays with paramlist You can use a parameter of type "Array of Any" in place of a list of bind values or in place of a list of fetch result variables. This is particularly useful when fetching an unknown number of results.
&Sql1 = CreateSql("Select * from " | &TableName); &AAny = CreateArrayAny(); While &Sql1.Fetch(&AAny) /* Process the row in &AAny. */ ... End-While;

Because the Array of Any promotes to absorb any remaining select columns, it must be the last parameter for the SQL object Fetch method or (for results) SQLExec. For binding, it must be the only bind parameter, as it is expected to supply all the bind values needed.
PeopleCode Built-in Functions Chapter 1 164 Copyright 1988, 2010, Oracle and/or its affiliates. All Rights Reserved.

Parameters Returns

sqlstring| SQL.SqlName Specify either a SQL string containing the SQL command to be created or a reference to an existing SQL definition. This string can include bind variables, inline bind variables, and metaSQL. paramlist Specify input values for the SQL string.

None.

10 This SQL object should be used in a series of Fetch method calls:


Local SQL &SQL; &SQL = CreateSQL("%SelectAll(:1) where EMPLID = :2", RECORD.ABSENCE_HIST, &EMPLID);

This SQL object has been opened, bound, and is already closed again:
&SQL = CreateSQL("Delete from %Table(:1) where EMPLID = :2", RECORD.ABSENCE_HIST,&EMPLID);

This SQL object should be used in a series of Execute method calls:


&SQL = CreateSQL("Delete from %Table(:1) where EMPLID = :2");

This SQL object is created as an empty object in order to set properties before being executed:
&Sql = CreateSQL(); &Sql.Tracename = "SQL1"; &Sql.ReuseCursor = True; &Sql.Open(......); /* do the deed */