HPlogo ALLBASE/SQL Advanced Application Programming Guide: HP 3000 MPE/iX Computer Systems > Chapter 4 Using Parameter Substitution in Dynamic Statements

Programming with Dynamic Parameters

» 

Technical documentation

Complete book in PDF
» Feedback

 » Table of Contents

 » Index

Depending on the purpose of your application, there is a broad spectrum of scenarios in which dynamic parameters could be useful. You might know almost all the elements of a statement at coding time, including the statement type and what dynamic parameters are required. At the opposite extreme, a program might be required to handle a completely unknown SQL statement containing dynamic parameters. Generally speaking, the less you know about a dynamic statement at coding time, the more coding you must do to verify the statement's content at run time.

The two basic methods of assigning dynamic parameter values involve use of either host variables or ALLBASE/SQL data structures and a data buffer. To use host variables, you must at least know the exact format of your SQL statement, although you need not know the specific data values of dynamic parameters. To use data structures and a data buffer, you do not need to know the exact format of your SQL statement.

Table 4-2 Dynamic Parameter Functionality by Programming Language

LanguageDynamic Parameter Data Assignment via Host VariablesDynamic Parameter Data Assignment via a Data BufferDynamic Parameters in a BULK INSERT Statement
Cyesyesyes
COBOLyesnoyes (with host variables)
FORTRANyesnono
Pascalyesyesyes

 

Host variables are available for C, COBOL, FORTRAN, and Pascal applications. Data buffers are available for C and Pascal applications only. In addition, dynamic parameters within a BULK INSERT statement require special syntax and are discussed separately. BULK INSERT functionality is available for C, COBOL, and Pascal applications. The following subsections discuss each basic coding method:

  • Using Host Variables to Process Dynamic Parameters.

  • Using Data Structures and a Data Buffer to Process Dynamic Parameters

  • Using a BULK INSERT Statement with Dynamic Parameters.

Using Host Variables to Process Dynamic Parameters

When you know at coding time the data type and format of each dynamic parameter in a dynamic statement, you have the choice of using either a host variable or a data buffer to provide dynamic parameter input at run time. This section details the use of host variables with non-bulk statements. (The next section discusses the data buffer technique.) The functionality described in this section is available for C, COBOL, FORTRAN, and Pascal programs.

Suppose you are coding an interactive user application. It involves mapping a user's menu choice to a partially known statement, then prompting for and accepting dynamic parameter values for data whose format is known at coding time. The following pseudocode illustrates this scenario.

[vellip]

Accept a variable indicating which of a set of statements the user has chosen.

Prepare the dynamic command for this statement:

PREPARE CMD FROM 'UPDATE PurchDB.Parts SET SalesPrice = ? WHERE PartNumber = ?;'

Prompt the user for values for the SalesPrice and PartNumber columns.

Execute the dynamic command using host variables to provide dynamic parameter values:

EXECUTE CMD USING :SalesPrice, :PartNumber;

You could now loop back to prompt the user for additional values for SalesPrice and PartNumber.  Note that the dynamic command does not have to be prepared again.

Using Data Structures and a Data Buffer to Process Dynamic Parameters

If at coding time you don't know the data types of all dynamic parameters in the prepared statement, you must use two ALLBASE/SQL data structures and a data buffer to obtain the default data types and pass dynamic parameter input to the database. These data structures are the same as those used for dynamic output processing:

  • sqlda_type data structure.

  • sqlfmts_type data structure.

  • data buffer.

The following discussion points out how to use these structures for input data. Here the term input data means dynamic parameter data, and output data means select list data. When a prepared statement is described for both input and output data, you must define one set of data structures for input data and another set for output data. Refer to the chapter "Using Dynamic Operations" in the ALLBASE/SQL C Application Programming Guide or the ALLBASE/SQL Pascal Application Programming Guide for a detailed description and example of how to use dynamic data structures for output.

Using the SQLDA for Input

To use an SQLDA structure for input, you prepare the dynamic command, then use the INPUT option with the DESCRIBE statement:

   DESCRIBE INPUT DynamicCommand INTO SQLDA

In place of SQLDA, you could name any data structure of type sqlda_type.

When the DESCRIBE statement executes, whether for input data or output data, the values in a given format array must be consistent with the values in its related data buffer. Refer to Table 4-6 “Fields in a Format Array Record in C” and Table 4-7 “Fields in a Format Array Record in Pascal” for a detailed description of the format array.

One difference between the use of the SQLDA for input data versus output data involves the Sqln and Sqld fields. Sqln is set by your program prior to issuing the DESCRIBE statement and represents the maximum number of 48 byte format array records allowed by the program. When using the DESCRIBE OUTPUT specification, you tell ALLBASE/SQL to load each format record with information for each select list item in the currently prepared statement (if it is a query). Using the DESCRIBE INPUT specification indicates that you want ALLBASE/SQL to load each format array record with information for each dynamic parameter in the currently prepared statement. Therefore, following execution of the DESCRIBE statement, Sqld represents either the number of select list items output or the number of dynamic parameters input.

When you describe dynamic parameters for input, the Sqlindlen field in the format array always equals two, even if it relates to a table column that does not allow nulls. Therefore, you must allow two bytes for this field in the corresponding input data buffer. By contrast, when you describe output for a column that was defined as not null, the Sqlindlen field is set to 0 to indicate no bytes are allocated in the corresponding data buffer for information relating to null values.

CAUTION: ALLBASE/SQL reads the data buffer based on its related format array. When you have described input, be sure the data you load into the data buffer corresponds to the information in its related format array.

Using the Data Buffer for Input

When you are describing data for input, it is your program's responsibility to load the data buffer with input values for each dynamic parameter based on information in the related format array. (This is unlike describing data for output, where ALLBASE/SQL loads the specified data buffer when data is fetched.) Following is a list of possible coding steps:

  1. Define any host variables to be used to pass a command string to ALLBASE/SQL via the PREPARE and DESCRIBE statements.

  2. Define any necessary sqlda_type structures (also called descriptor areas) for holding information about a given command string. Sqlda_type structures are used to communicate information regarding a specific SQL statement between this program and the database to which it is connected. Information is transferred when the DESCRIBE statement executes and when the FETCH statement executes.

    Remember that the INCLUDE SQLDA statement generates one sqlda_type data structure named sqlda. So, if you need more than one such structure, you must code a declaration for each.

    If you know nothing about an SQL statement until run time, define an sqlda_type structure for output to determine if the statement is a query or not. If it is a query, ALLBASE/SQL loads the related sqlformat_type structure with the format of the query result (one 48 byte element per select list item). You must also define an sqlda_type structure for input in case the statement contains dynamic parameters. In this case, ALLBASE/SQL loads the related sqlformat_type structure with the format of the dynamic parameters (one 48 byte element per dynamic parameter).

  3. Define an sqlformat_type structure (also called a format array) for each required data buffer.

    Information is transferred to the format array when the DESCRIBE statement executes.

  4. Define any necessary data buffers.

    Each data buffer must correspond to an sqlda_type structure and an sqlformat_type structure.

    If the statement is a query, your program needs a data buffer to hold the query results generated by the FETCH statement. If the statement contains dynamic parameters, your program needs a data buffer into which it loads the values of those dynamic parameters. The dynamic parameter values are transmitted to the database by means of an OPEN or an EXECUTE statement.

  5. Use the PREPARE statement to preprocess the dynamic statement.

  6. Set the appropriate sqlda_type fields. See Table 4-4 “Setting SQLDA Fields for Output and for Input in C” and Table 4-5 “Setting SQLDA Fields for Output and for Input in Pascal”.

    Remember, when you describe input for a non-bulk statement (a statement that processes just one row), sqlnrow must always be equal to one prior to issuing the OPEN or EXECUTE statement.

  7. Use the DESCRIBE statement (with the optional OUTPUT specification) to determine the statement type and its format if it is a query. Information goes to the specified sqlda_type and sqlformat_type data structures.

    You must use DESCRIBE OUTPUT if, at coding time, the composition of your prepared statement is completely unknown or if you know it is a query but you do not know its exact format and content.

  8. Use the DESCRIBE statement with the INPUT specification to determine the number of dynamic parameters in the prepared statement and the default data type and format of each. Your application obtains this information via the specified sqlda_type and sqlformat_type data structures. (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.)

  9. Load the input data buffer with dynamic parameter values based on information provided by the DESCRIBE INPUT statement.

  10. If the prepared statement is a query, use a DECLARE statement to associate it with a cursor.

    Use an OPEN statement to put qualifying rows of the query into the data buffer you have defined for output. Specify the USING DESCRIPTOR clause of the OPEN statement to pass in dynamic parameter values.

    In a loop, use a FETCH USING DESCRIPTOR statement to process each row.

  11. Close the cursor and commit work.

  12. If the prepared statement is not a query, use the EXECUTE statement with the USING clause to pass in dynamic parameter values.

Table 4-3 ALLBASE/SQL Data Type Byte Alignment

Format Array sqltype FieldData TypeSeries 700 and 800 Byte AlignmentSeries 300 and 400 Byte Alignment
0INTEGER42
0SMALLINT22
1BINARY11
2CHAR11
3VARCHAR42
4DOUBLE PRECISION82
4FLOAT (4 bytes)42
4FLOAT (8 bytes)82
4REAL42
5DECIMAL42
5NUMERIC42
6TID42
10DATE11
11TIME11
12DATETIME11
13INTERVAL11
14VARBINARY42
15LONG BINARY11
16LONG VARBINARY11

 

Table 4-4 Setting SQLDA Fields for Output and for Input in C

Field NameField DescriptionC Data TypeYou Set Before DESCRIBE or ADVANCEYou Set Before OPEN or EXECUTE USING INPUTYou Set Before FETCH or EXECUTE USING OUTPUTALLBASE/ SQL Sets at DESCRIBE or ADVANCEALLBASE/ SQL Sets at FETCH or EXECUTE USING OUTPUT
IUsed for input.      
OUsed for output.      
RUsed for DESCRIBE RESULT and ADVANCE.      
sqldaidreservedchar[8]     
sqlmprocnumber of multiple row result sets inside a procedureshort   IOR 
sqloparmnumber of output dynamic parameters in a dynamically prepared EXECUTE PROCEDURE statementshort   O 
sqlnnumber of format array elements (for output, one record per select list item to a maximum of 1024; for input, one record per dynamic parameter to a maximum of 255)intIOR    
sqldfor output, number of columns in query result (0 if non-query or EXECUTE PROCEDURE); for input, number of input dynamic parameters in the prepared statementint   IOR 
sqlfmtarraddress of format arrayintIOR  IOR [1] 
sqlnrownumber of rows in the data buffer [2]int I[2]O[2]  
sqlrrownumber of rows put into the data bufferint    O
sqlrowlennumber of bytes in each rowint   IOR 
sqlbuflennumber of bytes in the data bufferint IO  
sqlrowbufaddress of data bufferint IO  

[1] Data is loaded into the format array when a DESCRIBE or ADVANCE statement executes.

[2] When you describe for output, use sqlnrow to specify the number of rows to fetch into the data buffer. When you describe for input, use sqlnrow to specify the number of rows you have loaded into the data buffer (Always set to one for a non-bulk statement.).

 

Table 4-5 Setting SQLDA Fields for Output and for Input in Pascal

Field NameField DescriptionC Data TypeYou Set Before DESCRIBE or ADVANCEYou Set Before OPEN or EXECUTE USING INPUTYou Set Before FETCH or EXECUTE USING OUTPUTALLBASE/ SQL Sets at DESCRIBE or ADVANCEALLBASE/ SQL Sets at FETCH or EXECUTE USING OUTPUT
IUsed for input.      
OUsed for output.      
RUsed for DESCRIBE RESULT and ADVANCE.      
sqldaidreservedchar[8]     
sqlmprocnumber of multiple row result sets inside a procedureshort   IOR 
sqloparmnumber of output dynamic parameters in a dynamically prepared EXECUTE PROCEDURE statementsmallint   O 
sqlnnumber of format array elements (for output, one record per select list item to a maximum of 1024; for input, one record per dynamic parameter to a maximum of 255)integerIOR    
sqldfor output, number of columns in query result (0 if non-query or EXECUTE PROCEDURE); for input, number of input dynamic parameters in the prepared statementinteger   IOR 
sqlfmtarraddress of format arrayintegerIOR  IOR[1] 
sqlnrownumber of rows in the data buffer [2]integer I[2]O[2]  
sqlrrownumber of rows put into the data bufferinteger    O
sqlrowlennumber of bytes in each rowinteger   IOR 
sqlbuflennumber of bytes in the data bufferinteger IO  
sqlrowbufaddress of data bufferinteger IO  

[1] Data is loaded into the format array when a DESCRIBE or ADVANCE statement executes.

[2] When you describe for output, use sqlnrow to specify the number of rows to fetch into the data buffer. When you describe for input, use sqlnrow to specify the number of rows you have loaded into the data buffer (Always set to one for a non-bulk statement.).

 

Table 4-6 Fields in a Format Array Record in C

Field NameMeaning of FieldC Data Type
sqlntyreserved; always set to 111short
sqltype

data type of column:



    0 = SMALLINT or INTEGER

    1 = BINARY

    2 = CHAR*

    3 = VARCHAR*

    4 = FLOAT

    5 = DECIMAL

    8 = NATIVE CHAR [1]

    9 = NATIVE VARCHAR [1]

   10 = DATE*

   11 = TIME*

   12 = DATETIME*

   13 = INTERVAL*

   14 = VARBINARY

   15 = LONG BINARY

   16 = LONG VARBINARY

   19 = case insensitive CHAR*

   20 = case insensitive VARCHAR*

   21 = case insensitive NATIVE CHAR*

   22 = case insensitive NATIVE VARCHAR*  
short
sqlprecprecision of DECIMAL datashort
sqlscalescale of DECIMAL datashort
sqltotallenbyte sum of sqlvallen, sqlindlen, indicator alignment bytes, and next data value alignment bytesint
sqlvallennumber of bytes in data value, including a 4-byte prefix containing actual length of VARCHAR dataint
sqlindlen

number of bytes null indicator occupies in the data buffer

   for output:

     0 bytes:  column defined NOT NULL

     2 bytes:  column allows null values 



   for input:  always 2 bytes
int
sqlvofbyte offset of value from the beginning of a rowint
sqlnofbyte offset of null indicator from the beginning of a row, dependent on the value of sqlindlenint
sqlnamedefined name of column or, for computed expression, EXPRchar[20]

[1] Native CHAR or VARCHAR is what SQLCore uses internally when a CHAR or VARCHAR column is defined with a LANG = ColumnLanguageName clause. They possess the same characteristics as the related types CHAR and VARCHAR, except that data stored in native columns will be sorted, compared, or truncated using local language rules. Native, character, and Date/Time types are compatible with regular character types.

 

Table 4-7 Fields in a Format Array Record in Pascal

Field NameMeaning of FieldPascal Data Type
sqlntyreserved; always set to 111SmallInt
sqltype

data type of column:

    0 = SMALLINT or INTEGER

    1 = BINARY

    2 = CHAR*

    3 = VARCHAR*

    4 = FLOAT

    5 = DECIMAL

    8 = NATIVE CHAR [1]

    9 = NATIVE VARCHAR [1]

   10 = DATE*

   11 = TIME*

   12 = DATETIME*

   13 = INTERVAL*

   14 = VARBINARY

   15 = LONG BINARY

   16 = LONG VARBINARY

   19 = case insensitive CHAR*

   20 = case insensitive VARCHAR*

   21 = case insensitive NATIVE CHAR*

   22 = case insensitive NATIVE VARCHAR*
SmallInt
sqlprecprecision of DECIMAL dataSmallInt
sqlscalescale of DECIMAL dataSmallInt
sqltotallenbyte sum of sqlvallen, sqlindlen, indicator alignment bytes, and next data value alignment bytesInteger
sqlvallennumber of bytes in data value, including a 4-byte prefix containing actual length of VARCHAR dataInteger
sqlindlen

number of bytes null indicator occupies in the data buffer:

   for output:

     0 bytes:  column defined NOT NULL

     2 bytes:  column allows null values 

   

   for input:  always 2 bytes
Integer
sqlvofbyte offset of value from the beginning of a rowInteger
sqlnofbyte offset of null indicator from the beginning of a row, dependent on the value of sqlindlenInteger
sqlnamedefined name of column or, for computed expression, EXPRPacked Array [1..20] of char

[1] Native CHAR or VARCHAR is what SQLCore uses internally when a CHAR or VARCHAR column is defined with a LANG = ColumnLanguageName clause. They possess the same characteristics as the related types CHAR and VARCHAR, except that data stored in native columns will be sorted, compared, or truncated using local language rules. Native, character, and Date/Time types are compatible with regular character types.

 

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.



   [vellip] 



   #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];



   [vellip] 



   /********************************************************************/  

   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 */



   [vellip] 

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

   [vellip]



   boolean   OrdersOk;

   boolean   ConnectDBE();



[vellip]

    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;



       [vellip]



       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.                   */



   [vellip]



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

       [vellip]



       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.



       [vellip]



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.



       [vellip]

Example in Pascal Using a BULK INSERT

    [vellip]



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;



    NewOrders              : array[1..25]

      of record

        NewOrderNumber     : integer;

        NewVendorNumber    : integer;

        NewVendorNumberInd : sqlind;

        NewOrderDate       : packed array[1..10] of char;

        NewOrderDateInd    : sqlind;

      end;



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: 



    StartIndex            : SmallInt;

    NumberOfRows          : SmallInt;      (* Maximum possible rows to bulk *)

                                           (* insert. *)



    OrderNumber           : integer;       (* Host variables for user input.*)

    VendorNumber          : integer;

    VendorNumberInd       : sqlind;

    OrderDate             : packed array[1..10] of char;

    OrderDateInd          : sqlind;



    [vellip]



    SQLMessage            : packed array[1..132] of char;



    EXEC SQL END DECLARE SECTION;



    sqlca                 : SQLCA_type;    (* SQL Communication Area *)



    OrdersOK              : boolean;



    [vellip]



Use the PREPARE statement to preprocess the dynamic statement, in this case, from a string: 



   procedure PrepareIt;

   begin



   EXEC SQL PREPARE CMD from 'BULK INSERT INTO PurchDB.Orders VALUES (?,?,?);';



   if SQLCA.SQLCODE <> OK then         (* Check for processing errors.           *)

     begin

     SQLStatusCheck;

     RollBackWork;

     OrdersOK := FALSE;

     end;



   end;                               (* End PrepareIt Procedure.               *)



Load up to 25 rows of new orders for the BULK INSERT.  This data could originate from an interactive user or from a file: 



   procedure CreateOrders;



   var

   i:integer;



   begin



   NumberOfRows := 25;

   StartIndex   := 1;



Count rows as they are loaded into the NewOrders array up to a maximum of 25: 



   for i := 1 to NumberOfRows do

      begin



   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;

      NewOrders[i].NewOrderDate := OrderDate;

      NewOrders[i].NewOrderDateInd := OrderDateInd;



      end;                           (* End for.                                *)

   end;                              (* End procedure CreateOrders.             *)



Execute the prepared CMD command specifying the array where data for the BULK INSERT is located: 



   procedure InsertNew;

   begin



   EXEC SQL EXECUTE CMD USING :NewOrders, :StartIndex, :NumberOfRows;}}



   if SQLCA.SQLCODE <> OK then         (* Check for processing errors.           *)

     begin

     SQLStatusCheck;

     RollBackWork;

     OrdersOK := FALSE;

     end;                            

   end;                               (* End of procedure InsertNew.            *)



   [vellip]



   begin                              (* Begin the program.                     *)



   if ConnectDBE then                 (* If the application is successfully     *)

                                   (* connected to a DBEnvironment, proceed. *)

      begin



      OrdersOK := TRUE;



      BeginTransaction;

      PrepareIt;

      CreateOrders;

      InsertNew;



   if OrdersOK then                   (* If there were no errors in processing, *)

      CommitWork;                     (* data is committed to the database.     *)

      end;



   TerminateProgram;



   end.                               (* End the Program.                       *)
Feedback to webmaster