HP 3000 Manuals

Programming with Dynamic Parameters (contd) [ ALLBASE/SQL Advanced Application Programming Guide ] MPE/iX 5.0 Documentation


ALLBASE/SQL Advanced Application Programming Guide

Programming with Dynamic Parameters (contd) 

Using Data Structures and a Data Buffer to Process Dynamic Parameters 
(contd) 

Example in C Using Output and Input Data Buffers.   

Suppose you have designed an application that builds a SELECT statement.
A user can enter any valid DBEnvironment name, table name, column name,
and a column value to be used as a filter in the WHERE clause.  The
application builds the appropriate query and displays the query result.

Your application prepares this SELECT statement and describes it for
output.  It also describes the statement for input so that ALLBASE/SQL
can determine a default data type and format for the user entered column
value.  Note that the "Using Default Data Types with Dynamic Parameters"
section later in this chapter contains detailed information about default
data types and default data formats for dynamic parameters.

The following C pseudocode outlines the above scenario with emphasis on
ALLBASE/SQL programming for dynamic parameter substitution.  The
functionality is available for the C and Pascal languages.

     :

     #define  NbrFmtRecords    255
     #define  MaxDataBuff     2500
     #define  MaxColSize      3996
     #define  MaxName           20
     #define  MaxStr           132
     #define  SQLINT             0
     #define  SQLCHAR            2
     #define  OK                 0
     #define  TRUE               1
     #define  FALSE              0

     The ConvertType union structure is used to convert the SearchValue before 
     it is assigned to DataBufferIn. 

     typedef union ct {
       char           CharData[MaxColSize];
       char           VarCharData[MaxColSize];
       int            IntegerData;
       short          SmallIntData;
       float          FloatData;
       double         DecimalData;
       } ConvertType;

     Host variables are declared as follows: 
     DynamicCommand contains the dynamic SELECT statement.  SQLMessage holds messages 
     returned by the SQLEXPLAIN statement.  SearchValue, entered by the user, 
     is the value searched for by the SELECT statement. 

     EXEC SQL BEGIN DECLARE SECTION;
        char      DynamicCommand[1023];
        char      SQLMessage[133];
        char      SearchValue[1024];
     EXEC SQL END DECLARE SECTION;

     Declare the SQL communications area. 

     EXEC SQL INCLUDE SQLCA;

     The sqldain record contains information about the sqlfmtsin format array and the 
     DataBufferIn variable. 

     sqlda_type sqldain;

     The sqldaout record contains information about the sqlfmtsout format array and the 
     DataBufferOut variable. 
     sqlda_type sqldaout;

     The sqlfmtsin format array describes the dynamic parameters in the WHERE clause of 
     the SELECT statement.  Each record in the array describes one dynamic parameter. 
     Since this program specifies a single dynamic parameter, only the first record 
     in the array, sqlfmtsin[0], will be checked. 

     sqlformat_type sqlfmtsin[NbrFmtRecords];

     The sqlfmtsout format array describes the columns in the select list of the 
     SELECT statement.  Each record in the array describes one column. 

     sqlformat_type sqlfmtsout[NbrFmtRecords];

     DBEName contains the user specified database environment name. 

     char DBEName[MaxName];

     TableName contains the user specified table name of the SELECT statment. 

     char TableName[MaxName];

     ColName contains the user specified column name of the SELECT statment. 

     char ColName[MaxName];

     DataBufferIn contains the value of the dynamic parameter, in this case the 
     value of the column in the WHERE clause of the SELECT statement. 

     char DataBufferIn[MaxDataBuff];

     DataBufferOut contains the row values retrieved by the SELECT statement. 

     char DataBufferOut[MaxDataBuff];

     :

     /********************************************************************/
     main()
     /********************************************************************/
     {

     Prompt the user for the database environment used in the CONNECT statement. 

     sprintf (DBEName,"");
     sprintf (DynamicCommand,"");
     Prompt ("DBEnvironment name",DBEName);

     After prompting the user for the table name and the column name, move the 
     SELECT statement into the DynamicCommand variable.  The dynamic parameter, 
     represented by the question mark, is not specified until after the PREPARE 
     and DESCRIBE statements. 

     if (ConnectDBE()) {
        Prompt ("Table Name",TableName);
        while (strlen(TableName)!=0) {
            Prompt ("Column Name",ColName);
            sprintf (DynamicCommand,"SELECT * FROM %s WHERE %s = ?;",
                    TableName, ColName);
            Prepare();
            Prompt ("Table Name",TableName);
           } /* end while */
        ReleaseDBE();
        } /* end if */
     else
        printf("\nError: Cannot Connect to %s",DBEName);

     }  /* End of Main Program */

     /********************************************************************/
     int Prepare()
     /********************************************************************/
     {

     Before the PREPARE statement, the input and output descriptor fields must be 
     set up. 

     The sqldain.sqln variable is assigned the number of records in the sqlfmtsin 
     array and the sqldain.sqlfmtarr variable is assigned the address of the 
     sqlfmtsin array. 

     sqldain.sqln      = NbrFmtRecords;
     sqldain.sqlfmtarr = sqlfmtsin;

     The sqldaout.sqln variable is assigned the number of records in the sqlfmtsout 
     array and the sqldaout.sqlfmtarr variable is assigned the address of the 
     sqlfmtsout array. 

     sqldaout.sqln      = NbrFmtRecords;
     sqldaout.sqlfmtarr = sqlfmtsout;

     if (BeginTransaction()) {

        Prepare the dynamic SELECT statement.  At this point the value of the 
        dynamic parameter is still undefined. 

        EXEC SQL PREPARE CMD1 FROM :DynamicCommand;

        if (sqlca.sqlcode != OK) {
           SQLStatusCheck();
           EndTransaction();
           }
        else {

           The DESCRIBE statement gets information about the statement that was 
           dynamically preprocessed by the PREPARE statement. 

           Here dynamic parameter information is obtained: 

           EXEC SQL DESCRIBE INPUT CMD1 INTO sqldain;
           if (sqlca.sqlcode != OK) {
               SQLStatusCheck();
               EndTransaction();
               }
           else

           Here query result information is obtained: 

           EXEC SQL DESCRIBE CMD1 INTO sqldaout;
           if (sqlca.sqlcode != OK) {
               SQLStatusCheck();
               EndTransaction();
               }
           else

              Fetch();
           }

       }  /* End if BeginTransaction */

     }  /* End of Prepare function */
     /********************************************************************/
     int Fetch()
     /********************************************************************/
     {

     short       i;
     ConvertType ConvertedSearch;

     Declare the cursor for the SELECT statement. 

     EXEC SQL DECLARE CURSOR1 CURSOR FOR CMD1;

     Prompt the user for the search value, which will be assigned to the dynamic 
     parameter in the WHERE clause of the SELECT statement. 
     The sqlfmtsin[0].sqlname variable contains the column name in the WHERE 
     clause of the SELECT statement. 

     Prompt (sqlfmtsin[0].sqlname,SearchValue);

     Set up the input descriptor fields of the sqldain record before opening the cursor. 

     The sqldain.sqlnrow variable is assigned the number of rows in DataBufferIn, that is, 
     the number of dynamic parameters specified. 

     sqldain.sqlnrow   = 1;

     The sqldain.sqlbuflen variable is assigned the number of bytes in DataBufferIn. 

     sqldain.sqlbuflen = MaxDataBuff;

     The sqldain.sqlrowbuf variable is assigned the address of DataBufferIn. 

     sqldain.sqlrowbuf = (int) DataBufferIn;

     Since the search value entered by the user is a character string, it must be 
     converted to the format of the column in the WHERE clause of the dynamic 
     SELECT statement.  The SearchValue is first assigned to the ConvertedSearch 
     record, and then assigned to DataBufferIn. 

     Check the value of sqlfmtsin[0].sqltype to determine the data type of the column 
     in the WHERE clause. 

     if (sqlfmtsin[0].sqltype == SQLINT) {
        INT or SMALLINT columns generate the same data type value in sqltype, but 
        must be distinguished because they have different lengths.  If sqlvallen 
        is equal to the size of an integer variable, then the data type is INT. 
        if (sqlfmtsin[0].sqlvallen == sizeof(ConvertedSearch.IntegerData))
           SQL INT data type. 
           ConvertedSearch.IntegerData =  atoi(SearchValue);
        else
           Otherwise, the column data type is SMALLINT. 
           ConvertedSearch.SmallIntData =  atoi(SearchValue);
        }
     else if (sqlfmtsin[0].sqltype == SQLCHAR) {
        Otherwise, the column data type is CHAR. 
        for (i = 0;i < strlen(SearchValue); i++)
            ConvertedSearch.CharData[i] = SearchValue[i];
        for (i = strlen(SearchValue); i < sqlfmtsin[0].sqlvallen; i++)
            ConvertedSearch.CharData[i] = ' ';
        }
     else
        printf ("Error: Conversion routine unavailable for that data type.\n");

     Move the ConvertedSearch data to DataBufferIn. 

     StrMove (sqlfmtsin[0].sqlvallen,ConvertedSearch.CharData,0,DataBufferIn,0);

     Assign zero to the two-byte, null field value that must follow the search value 
     data in DataBufferIn. 

     ConvertedSearch.SmallIntData = 0;
     StrMove (2,ConvertedSearch.CharData,0,DataBufferIn,sqlfmtsin[0].sqlvallen);

     Open the cursor, using the input description record sqldain. 

     EXEC SQL OPEN CURSOR1 USING SQL DESCRIPTOR sqldain;

     if (sqlca.sqlcode != OK)
        SQLStatusCheck();

     Set up the output descriptor fields of the sqldaout record before performing the fetch. 

     The sqldaout.sqlbuflen variable is assigned the number of bytes in DataBufferOut. 

     sqldaout.sqlbuflen = MaxDataBuff;

     The sqldaout.sqlnrow variable is assigned the number of rows in DataBufferOut, 
     that is, the number of rows to fetch. 

     sqldaout.sqlnrow   = ((sqldaout.sqlbuflen) / (sqldaout.sqlrowlen));

     The sqldaout.sqlrowbuf variable is assigned the address of DataBufferOut. 

     sqldaout.sqlrowbuf = (int) DataBufferOut;

     Fetch rows into DataBufferOut until no more rows are found. 

     do {
        EXEC SQL FETCH CURSOR1 USING SQL DESCRIPTOR sqldaout;

        if (sqlca.sqlcode == 100)
           printf ("Warning: No more rows qualify for this operation\n");
        else if (sqlca.sqlcode != 0)
           SQLStatusCheck();
        else
           The DisplaySelect function parses DataBufferOut and displays the data. 
           See program cex10a in the ALLBASE/SQL C Application Programming Guide 
           for a full listing of the DisplaySelect function. 
           DisplaySelect();

       } while (sqlca.sqlcode == 0);

     Close the cursor and end the transaction. 

     EXEC SQL CLOSE CURSOR1;

     if (sqlca.sqlcode != OK)
        SQLStatusCheck();

     EndTransaction();

     }  /* End of Fetch function */

     /********************************************************************/
     int StrMove (n,source,subs,dest,subd)
     /********************************************************************/

     int n, subs, subd;
     char source[], dest[];

     {
     int i = 1;

     Move n number of bytes from source, starting at source[subs] to dest, 
     starting at dest[subd]. 

     while (i++ <= n)
        dest[subd++] = source[subs++];

     }  /* End of StrMove function */

     /********************************************************************/
     int Prompt (displaystr,inputstr)
     /********************************************************************/

     char *displaystr, *inputstr;

     {

     printf("Enter %s: ",displaystr);
     gets(inputstr);

     } /* End of Prompt function */

     :

You could enhance the above pseudocode by coding an application for the
following scenario.  After the display, offer the user these choices:

   *   Enter another value for the same column.
   *   Enter another table name, column name, and column value for the
       same DBEnvironment.
   *   Exit the application.

Since the column value is passed by means of a dynamic parameter, if the
user chooses to enter another column value for the same column, you can
improve performance by reusing the already prepared stored section for
the given SELECT statement.

Each time the user enters another value for the same SELECT statement,
your application does the following:

   *   Loads the new value into the input data buffer.
   *   Opens the dynamic cursor.
   *   Fetches all qualifying rows.
   *   Closes the cursor.

Using a BULK INSERT Statement with Dynamic Parameters 

Suppose you are writing an application that inserts multiple rows of
data.  At coding time, you know the format of the BULK INSERT statement,
and you know which parameters in the statement will differ for each row
(the dynamic parameters).

The application must run in many DBEnvironments, and you want it to
achieve maximum performance.  For portability, you decide on dynamic
statements.  For maximum performance, you decide to use parameter
substitution and the BULK INSERT statement.  To minimize your coding
time, you use host variables, rather than a data buffer.

The following pseudocode examples illustrate this scenario for C, COBOL,
and Pascal applications.


NOTE When host variables are used, EXECUTE statement syntax differs for a BULK INSERT statement and an INSERT statement.
Example in C Using a BULK INSERT. : boolean OrdersOk; boolean ConnectDBE(); : sqlca_type sqlca; /* SQL communication area. */ Define a host variable array to hold dynamic parameter values. Be sure each host variable data type matches (or is compatible with) its ALLBASE/SQL default data type: EXEC SQL BEGIN DECLARE SECTION; struct { int NewOrderNumber; int NewVendorNumber; sqlind NewVendorNumberInd; char NewOrderDate[11]; /* Add a byte for end of char array. */ sqlind NewOrderDateInd; } NewOrders[25]; If the dynamic parameter represents data for a column that can contain nulls, and it is possible that input data will contain null values, be sure to define a null indicator host variable immediately following the related host variable. If you are using other than the default values for the starting index and number of rows in the array, define host variables for these as well: short StartIndex; short NumberOfRows; /* Maximum possible rows to bulk insert. */ int OrderNumber; /* Host variables for input data. */ int VendorNumber; sqlind VendorNumberInd; char OrderDate[11]; sqlind OrderDateInd; : char SQLMessage[133]; /* Add a byte for end of char array. */ EXEC SQL END DECLARE SECTION; main() { /*Specify main functions. */ if (ConnectDBE()) { /* If the application is successfully */ /* connected to a DBEnvironment, proceed. */ OrdersOk = TRUE; BeginTransaction(); PrepareIt(); CreateOrders(); InsertNew(); if (OrdersOk) /* If there were no errors in processing, */ CommitWork(); /* data is committed to the database. */ TerminateProgram(); } /* End if. */ } /* End of main program. */ : Use the PREPARE statement to preprocess the dynamic statement, in this case, from a string: int PrepareIt(){ EXEC SQL PREPARE CMD from 'BULK INSERT INTO PurchDB.Orders VALUES (?,?,?);'; switch (sqlca.sqlcode){ /* Check for processing errors. */ case OK: break; default: SQLStatusCheck(); RollBackWork(); OrdersOk = FALSE; } /* End switch. */ } /* End function PrepareIt. */ Load up to 25 rows of new orders for the BULK INSERT. This data could originate from an interactive user or from a file: int CreateOrders() { int i = 0; /* Define and initialize an index to move */ /* through array elements. */ NumberOfRows = 25; StartIndex = 1; Count rows as they are loaded into the NewOrders array up to a maximum of 25: for (i = 0; i <= NumberOfRows; i++){ Read a file record or accept a row of data from the user into the appropriate host variables. Load host variable data into the bulk insert array. NewOrders[i].NewOrderNumber = OrderNumber; NewOrders[i].NewVendorNumber = VendorNumber; NewOrders[i].NewVendorNumberInd = VendorNumberInd; strcpy (NewOrders[i].NewOrderDate,OrderDate); NewOrders[i].NewOrderDateInd = OrderDateInd; } /* End for. */ } /* End of function CreateOrders. */ Execute the prepared CMD command specifying the array where data for the BULK INSERT is located: int InsertNew(){ EXEC SQL EXECUTE CMD USING :NewOrders, :StartIndex, :NumberOfRows; switch (sqlca.sqlcode){ /* Check for processing errors. */ case OK: break; default: SQLStatusCheck(); RollBackWork(); OrdersOk = FALSE; } /* End switch. */ } /* End of function InsertNew. */ Example in COBOL Using a BULK INSERT. : WORKING-STORAGE SECTION. * SQL communication area. * EXEC SQL INCLUDE SQLCA END-EXEC. * Host variables for input data. * EXEC SQL BEGIN DECLARE SECTION END-EXEC. 01 ORDERNUMBER PIC S9(9) COMP. 01 VENDORNUMBER PIC S9(9) COMP. 01 VENDORNUMBERIND SQLIND. 01 ORDERDATE PIC X(8). 01 ORDERDATEIND SQLIND. Define a host variable array to hold dynamic parameter values. Be sure each host variable data type matches (or is compatible with) its ALLBASE/SQL default data type: 01 NEWORDERS. 05 EACH-ROW OCCURS 25 TIMES. 10 NEWORDERNUMBER PIC S9(9) COMP. 10 NEWVENDORNUMBER PIC S9(9) COMP. 10 NEWVENDORNUMBERIND SQLIND. 10 NEWORDERDATE PIC X(8). 10 NEWORDERDATEIND SQLIND. If the dynamic parameter represents data for a column that can contain nulls, and it is possible that input data will contain null values, be sure to define a null indicator host variable immediately following the related host variable. If you are using other than the default values for the starting index and number of rows in the array, define host variables for these as well: 01 STARTINDEX PIC S9(4) COMP. * Maximum possible rows to bulk insert. * 01 NUMBEROFROWS PIC S9(4) COMP. 01 SQLMESSAGE PIC X(132). EXEC SQL END DECLARE SECTION END-EXEC. PROCEDURE DIVISION. A100-MAIN. PERFORM A200-CONNECT-DBENVIRONMENT THRU A200-EXIT. PERFORM B100-PREPARE-IT THRU B100-EXIT. PERFORM C100-CREATE-ORDERS THRU C100-EXIT UNTIL DONE. PERFORM D100-BULK-INSERT THRU D100-EXIT. PERFORM A500-TERMINATE-PROGRAM THRU A500-EXIT. A100-EXIT. EXIT. : Use the PREPARE statement to preprocess the dynamic statement, in this case, from a string: B100-PREPARE-IT. MOVE 1 to I. MOVE SPACES TO DONE-FLAG. MOVE SPACES TO NEWORDERS. PERFORM A300-BEGIN-TRANSACTION THRU A300-EXIT. EXEC SQL PREPARE CMD from 'BULK INSERT INTO PurchDB.Orders VALUES (?, ?, ?);' END-EXEC. Check for processing errors. Display any messages, and either commit or roll back the transaction: IF SQLCODE = OK PERFORM A400-COMMIT-WORK THRU A400-EXIT ELSE PERFORM S100-SQL-STATUS-CHECK THRU S100-EXIT PERFORM A450-ROLLBACK-WORK THRU A450-EXIT. B100-EXIT. EXIT. Load up to 25 rows of new orders for the BULK INSERT. This data could originate from an interactive user or from a file (In this case, it is an interactive user.): C100-CREATE-ORDERS. DISPLAY ' '. DISPLAY 'You can specify as many as 25 line items.'. DISPLAY ' '. MOVE ' Order Number> ' TO PROMPT-USER DISPLAY " " WRITE PROMPT-USER ACCEPT NEWORDERNUMBER(I) MOVE ' Vendor Number> ' TO PROMPT-USER DISPLAY " " WRITE PROMPT-USER ACCEPT NEWVENDORNUMBER(I) MOVE ' Order Date (YYYYMMDD)> ' TO PROMPT-USER DISPLAY " " WRITE PROMPT-USER MOVE SPACES TO NEWORDERDATE(I) ACCEPT NEWORDERDATE(I) IF I = 25 MOVE "X" TO DONE-FLAG GO TO C100-EXIT ELSE PERFORM C200-MORE-LINES THRU C200-EXIT. C100-EXIT. EXIT. C200-MORE-LINES. DISPLAY ' ' MOVE 'Do you want to specify another line item (Y/N)?> ' TO PROMPT-USER MOVE SPACE TO RESPONSE-ALPHA DISPLAY " " WRITE PROMPT-USER ACCEPT RESPONSE-ALPHA. IF RESPONSE-ALPHA NOT = "Y" AND RESPONSE-ALPHA NOT = "y" MOVE "X" TO DONE-FLAG GO TO C200-EXIT ELSE COMPUTE I = I + 1. C200-EXIT. EXIT. Execute the prepared CMD command specifying the array where data for the BULK INSERT is located: D100-BULK-INSERT. DISPLAY ' '. MOVE I TO NUMBEROFROWS. MOVE 1 TO STARTINDEX. MOVE 1 to I. DISPLAY 'BULK INSERT INTO PurchDB.OrderItems'. EXEC SQL EXECUTE CMD USING :NEWORDERS, :STARTINDEX, :NUMBEROFROWS END-EXEC. Check for processing errors. Display any messages, and either commit or roll back the transaction: IF SQLCODE = OK PERFORM A400-COMMIT-WORK THRU A400-EXIT ELSE PERFORM S100-SQL-STATUS-CHECK THRU S100-EXIT PERFORM A450-ROLLBACK-WORK THRU A450-EXIT. D100-EXIT. EXIT. :


MPE/iX 5.0 Documentation