solidDB Help : solidDB reference : SQL: Statements : CREATE PROCEDURE
  
CREATE PROCEDURE
CREATE PROCEDURE procedure‑name [(param‑definition [, param‑definition …])]
   [RETURNS (output‑column‑definition [, output‑column‑definition …])]
   [SQL‑data‑access‑indication]
   {int-procedure-definition | ext-procedure-definition}
where:
param‑definition::= [param‑mode] param‑name data‑type
param‑mode::= IN | OUT | INOUT
output‑column‑definition::= column‑name column‑type
SQL‑data‑access‑indication::= CONTAINS SQL | READS SQL DATA |
   MODIFIES SQL DATA | NO SQL
int-procedure-definition::= [LANGUAGE SQL] BEGIN procedure‑body END
ext-procedure-definition::= LANGUAGE C EXTERNAL NAME 'ext-procedure-lib-identifier'
procedure‑body::= [declare‑statement; …][procedure‑statement; …]
declare‑statement::= DECLARE variable‑name data‑type;
procedure‑statement::= prepare‑statement | execute‑statement |
   fetch‑statement | control‑statement | post‑statement |
   wait‑event‑statement | wait‑register‑statement | exec‑direct‑statement |
   writetrace‑statement | sql‑dml‑or‑ddl‑statement
prepare‑statement::= EXEC SQL PREPARE
   {cursor‑name | CURSORNAME({string‑literal | variable})}
   sql‑statement
execute‑statement::= EXEC SQL EXECUTE cursor‑name
   [USING (variable [, variable ...])]
   [INTO (variable [, variable ...])] |
   EXEC SQL CLOSE cursor‑name |
   EXEC SQL DROP cursor‑name |
   EXEC SQL {COMMIT | ROLLBACK} WORK |
   EXEC SQL SET TRANSACTION {READ ONLY | READ WRITE} |
   EXEC SQL WHENEVER SQLERROR {ABORT | ROLLBACK [WORK], ABORT}
   EXEC SEQUENCE sequence‑name.CURRENT INTO variable |
   EXEC SEQUENCE sequence‑name.NEXT INTO variable |
   EXEC SEQUENCE sequence‑name SET VALUE USING variable
fetch‑statement::= EXEC SQL FETCH cursor‑name
cursor‑name::= literal
post‑statement::= POST EVENT event‑name [(parameters)]
wait‑event‑statement::= WAIT EVENT [event‑specification …] END WAIT
event‑specification::= WHEN event‑name [(parameters)]
  BEGIN statements END EVENT
wait‑register‑statement::= REGISTER EVENT event‑name |
  UNREGISTER EVENT event‑name
writetrace‑statement::= WRITETRACE(string)
control‑statement::= SET variable‑name = value |
   variable‑name::= value |
   WHILE expression LOOP procedure‑statement… END LOOP |
   LEAVE |
   IF expression THEN procedure‑statement
      [ELSEIF procedure‑statementTHEN] …
      ELSE procedure‑statementEND IF |
   RETURN |
   RETURN SQLERROR OF cursor‑name |
   RETURN ROW |
   RETURN NOROW
exec‑direct‑statement::= EXEC SQL [USING (variable [, variable ...])]
   [CURSORNAME(variable)]
   EXECDIRECT sql‑dml‑or‑ddl‑statement |
   EXEC SQL cursor‑name
      [USING (variable [, variable ...])]
      [INTO (variable [, variable ...])]
      [CURSORNAME(variable)]
      EXECDIRECT sql‑dml‑or‑ddl‑statement
data‑type::= see data-type
expression::= see expression
Access requirements
Procedures are owned by the creator of the procedure. Access rights can be granted to other users. When the procedure is run, it has the same rights to database objects as the creator.
Usage
Use the CREATE PROCEDURE statement to create a procedure that contains several SQL statements or a whole transaction and execute it with a single call statement, see Stored procedures.
Stored procedures are written in the solidDB proprietary SQL procedure language (internal procedures) or in the C programming language (external procedures), see External stored procedures.
The output of an external procedure can be returned as a result set with a single row or as output parameters.
Parameters, clauses, keywords, and variables
RETURNS: Uses the solidDB proprietary method to return a result set table with several rows of data in separate columns. For more information, see Using RETURNS to provide output from a stored procedure.
param-definition
param‑mode: Indicates whether parameters are input, output or input/output parameters. For more information, see Using parameters for input and output in a stored procedure.
SQL‑data‑access‑indication: Indicates how the procedure affects the associated data. You can use this clause to make the use of stored procedures more efficient in HotStandby (HSB) configurations, see Using stored procedures in HotStandby configurations.
READS SQL DATA: Procedure contains only SQL instructions that read data, for example. SELECT statements.
MODIFIES SQL DATA: Procedure contains SQL statements that write data, for example, UPDATE, INSERT, or DELETE statements.
NO SQL: Procedure contains no SQL statements.
CONTAINS SQL: Procedure contains SQL statements, but does not contain statements that read or write data, for example, SET statements.
int-procedure-definition
prepare‑statement:
EXEC SQL PREPARE: Prepares the SQL statements.
cursor‑name: Specifies a cursor name inside the transaction.
CURSORNAME: Dynamically generates a cursor name instead of using a specified cursor name, see Using dynamic cursor names.
execute‑statement: Executes prepared SQL statements by using cursors, and closes or drops cursors. For more information, see Using a cursor.
The statement can also be used to terminate transactions or control the transaction type. For more information, see Transactions.
fetch statement: Fetches rows. If the fetch completes successfully, the column values are stored into the variables that are defined in the INTO clause of the EXECUTE or EXECDIRECT statement. For more information, see Using a cursor.
post‑statement: Posts system events and user-defined events, see POST EVENT.
wait‑register‑statement: Tells the server that you want to be notified (or stop being notified) of all future occurrences of the specified event, even if you are not yet waiting for it, see REGISTER EVENT and UNREGISTER EVENT.
By separating the REGISTER EVENT and WAIT EVENT statements, you can start queuing events immediately, while waiting until later to actually start processing them.
wait‑event‑statement: Makes procedures wait for an event to happen before continuing. You can use WAIT EVENT with both system-defined events and user-defined events.
If you want to stop the stored procedure waiting for an event, you can use ODBC function SQLCancel() called from a separate thread in the client application. This function cancels executing statements. Alternatively, you can create a specific user event and send it. The waiting stored procedure must be modified to wait for this additional event. The client application recognizes this event and exits the waiting loop.
With system events, you can also wait on an event without using a stored procedure by using the ADMIN EVENT statement, see ADMIN EVENT. However, you cannot post events by using ADMIN EVENT.
control‑statement: Control statements that you can use in stored procedures. For more information, see Using control statements.
writetrace‑statement: Sends trace output (string) to the soltrace.out trace file. This can be useful when debugging problems in stored procedures.
The output is written only if you turn tracing on.
For more information about tracing in stored procedures and how to turn on tracing, see Tracing facilities for stored procedures and triggers.
exec‑direct‑statement: Allows you to execute statements inside stored procedures without preparing those statements first. For more information, see Using EXECDIRECT.
ext-procedure-definition:
LANGUAGE C: Indicates that the CREATE PROCEDURE statement is being used to register a new procedure that is based on code that is written in the C programming language.
EXTERNAL NAME 'ext‑procedure‑lib‑identifier': Identifies the name of the user-written code that implements the procedure. The name must be enclosed by single quotation marks. Extraneous blanks are not permitted.
[path]library‑name: Identifies the path (optional) and name of the function. If you specify an absolute path, you must append the file extension of the library file. If you do not specify the file extension, you can port and use the library on several platforms, however, you might need to add the location of the library to the LD_LIBRARY_PATH or LIBPATH (Linux and UNIX) or PATH (Windows) environment variable.
!C‑routine‑name: Identifies the entry point name of the C routine (procedure) to be invoked. The exclamation point (!) serves as a delimiter between the library name and the routine name. For example, '!proc8' would direct solidDB to look for the library in the location specified by [path]library‑name, and to use entry point proc8 within that library.
Notes
On Windows 32-bit systems, use the calling convention _cdecl for calling the C routines. This allows the checking and handling of varying argument counts. Also, ensure that your DLL exports the routines you want to call: for example, include the routines in the corresponding .def file in your DLL project.
The library (and the procedure within the library) does not need to exist when the CREATE PROCEDURE statement is executed. However, when the procedure is called, the library and procedure within the library must exist and be accessible.
Restrictions
The call interface of the external functions is assumed to be non-fenced and thread-safe.
All procedures are assumed non-deterministic; the procedure depends on some state values that affect results.
You should not start new threads that execute in parallel with the called procedure; unlike an ODBC handle, the provided connection handle is not multithread safe. If new threads are launched, they cannot access the connection concurrently or after the original external procedure has returned.
Return values
SQL_ERROR
SQL_SUCCESS
Examples
Procedure that uses the RETURN statement
The following example uses the explicit RETURN statement to return multiple rows, one at a time.
"create procedure return_tables
   returns (name varchar)
begin
   exec sql execdirect create table table_name (lname char (20));
   exec sql whenever sqlerror rollback, abort;
   exec sql prepare c1 select table_name from sys_tables;
   exec sql execute c1 into (name);
   while sqlsuccess loop
      exec sql fetch c1;
      if not sqlsuccess
         then leave;
      end if
      return row;
      end loop;
   exec sql close c1;
   exec sql drop c1;
end";
Procedure that uses the EXECDIRECT clause
"CREATE PROCEDURE p
BEGIN
   DECLARE host_x INT;
   DECLARE host_y INT;
-- Examples of execdirect without a cursor. Here we create a
-- table and insert a row into that table.
   EXEC SQL EXECDIRECT create table foo (x int, y int);
   EXEC SQL EXECDIRECT insert into foo(x, y) values (1, 2);

   SET host_x = 1;

-- Example of execdirect with cursor name.
-- In this example, "c1" is the cursor name; "host_x" is the
-- variable whose value will be substituted for the "?";
-- "host_y" is the variable into which we will store the value
-- of the column y (when we fetch it).
-- Note: although you do not need a "prepare" statement, you
-- still need close/drop.

   EXEC SQL c1 USING(host_x) INTO(host_y) EXECDIRECT
      SELECT y from foo where x=?;
   EXEC SQL FETCH c1;
   EXEC SQL CLOSE c1;
   EXEC SQL DROP c1;
END";
Procedures that uses CURSORNAME() function
The following example shows the usage of the CURSORNAME() pseudo-function. This shows only part of the body of a stored procedure, not a complete stored procedure.
-- Declare a variable that will hold a unique string that we can
-- use as a cursor name.
DECLARE autoname VARCHAR ;
Autoname := GET_UNIQUE_STRING('CUR_') ;
EXEC SQL PREPARE curs_name CURSORNAME(autoname)
      SELECT * FROM TABLES;
EXEC SQL EXECUTE curs_name USING(...) INTO(...);
EXEC SQL FETCH curs_name;
EXEC SQL CLOSE curs_name;
EXEC SQL DROP curs_name;
Procedure that uses GET_UNIQUE_STRING and CURSORNAME
The following example uses the GET_UNIQUE_STRING and CURSORNAME functions in a recursive stored procedure.
The following stored procedure demonstrates the use of these two functions in a recursive procedure. The cursor name curs1 appears to be hardcoded, but in fact it has been mapped to the dynamically generated name.
-- Demonstrate GET_UNIQUE_STRING and CURSORNAME functions in a
-- recursive stored procedure.
-- Given a number N greater than or equal to 1, this procedure
-- returns the sum of the numbers 1 - N. (This can also be done
-- in a loop, but the purpose of the example is to show the use
-- of the CURSORNAME function in a recursive procedure.)

"CREATE PROCEDURE Sum1ToN(n INT)
RETURNS (SumSoFar INT)
BEGIN
   DECLARE SumOfRemainingItems INT;
   DECLARE nMinusOne INT;
   DECLARE autoname VARCHAR;

   SumSoFar := 0;
   SumOfRemainingItems := 0;
   nMinusOne := n - 1;

   IF (nMinusOne > 0) THEN
      Autoname := GET_UNIQUE_STRING('CURSOR_NAME_PREFIX_') ;
      EXEC SQL PREPARE curs1 CURSORNAME(autoname)
            CALL Sum1ToN(?);
      EXEC SQL EXECUTE curs1 USING(nMinusOne)
            INTO(SumOfRemainingItems);
      EXEC SQL FETCH curs1;
      EXEC SQL CLOSE curs1;
      EXEC SQL DROP curs1;
   END IF;

   SumSoFar := n + SumOfRemainingItems;
END";
Procedure that creates a unique name for a synchronization message
DECLARE Autoname VARCHAR;
DECLARE Sqlstr VARCHAR;
Autoname := get_unique_string('MSG_') ;
Sqlstr := 'MESSAGE' + autoname + 'BEGIN';
EXEC SQL EXECDIRECT Sqlstr;
...
Sqlstr := 'MESSAGE' + autoname + 'FORWARD';
EXEC SQL EXECDIRECT Sqlstr;
Procedure that uses GET_UNIQUE_STRING
The following example demonstrates how to use the GET_UNIQUE_STRING() function to generate unique message names from within a recursive stored procedure.
CREATE TABLE table1 (i int, beginMsg VARCHAR, endMsg VARCHAR);
-- This is a simplified example of recursion.
-- Note that the messages that are composes are not actually
-- used! This is not a true example of synchronization; it is
-- only an example of generating unique message names. The
-- "count" parameter is the number of times that you want this
-- function to call itself (not including the initial call).
"CREATE PROCEDURE repeater(count INT)

BEGIN

DECLARE Autoname VARCHAR; DECLARE MsgBeginStr VARCHAR;
DECLARE MsgEndStr VARCHAR;

Autoname := GET_UNIQUE_STRING('MSG_');
MsgBeginStr := 'MESSAGE ' + Autoname + ' BEGIN';
MsgEndStr := 'MESSAGE ' + Autoname + ' END';

EXEC SQL c1 USING (count, MsgBeginStr, MsgEndStr) EXECDIRECT
   INSERT INTO table1 (i, beginMsg, endMsg) VALUES (?,?,?);
EXEC SQL CLOSE c1;
EXEC SQL DROP c1;

-- After you have composed the SQL statement as a string,
-- you can execute it one of two ways:
-- 1) by using the EXECDIRECT feature or
-- 2) by preparing and executing the SQL statement.
-- In this example, we use EXECDIRECT.

EXEC SQL EXECDIRECT MsgBeginStr;
EXEC SQL EXECDIRECT MsgEndStr;
-- Do something useful here.
-- The recursive portion of the function.
IF (count > 1) THEN
   SET count = count - 1;
   -- Note that we can also use our unique name as a cursor name,
   -- for example:
   EXEC SQL Autoname USING (count) EXECDIRECT CALL repeater(?);
   EXEC SQL CLOSE Autoname;
   EXEC SQL DROP Autoname;
END IF
RETURN;
END";

CALL repeater(3);
-- Show the message names that we composed. SELECT * FROM table1;
The output from this SELECT statement would look similar to the following example:
I  BEGINMSG                ENDMSG
-- --------------------    ------------------
1  MESSAGE MSG_019 BEGIN   MESSAGE MSG_019 END
2  MESSAGE MSG_020 BEGIN   MESSAGE MSG_020 END
3  MESSAGE MSG_021 BEGIN   MESSAGE MSG_021 END
External procedure
The following example registers an external stored procedure EXTPROCEXAMPLE1.
CREATE PROCEDURE EXTPROCEXAMPLE1(IN P1 INTEGER,
      INOUT P2 INTEGER, OUT P3 CHAR(10))
READS SQL DATA
LANGUAGE C
EXTERNAL NAME 'examplelib1!extprocexample1'
C program for the procedure:
#include <solidodbc3.h>
/* UTF-8 sequence for a char can be up to 4 bytes */
#define CHARMAXBYTELEN(charlen) (4 * (charlen) + 1)
int extprocexample1(SQLHDBC hdbc, /* implicit input argument: 'hot'
                                     connection handle */
  char sqlst[6],      /* implicit output argument : sqlstate */
  char qualName[],    /* implicit input argument: procedure name */
  char diagMsg[71],   /* implicit output argument: diagnostics message */   SQLULEN nArgs,      /* implicit input: actual number of args */
  SQLLEN nullind[3],  /* implicit input/output: null/size indicator
                         variables
(when nArgs > 0) */ int* p1, int* p2, char p3[CHARMAXBYTELEN(10)])
                      /* explicit arguments */
{
  SQLHSTMT hstmt = SQL_NULL_STMT;
  SQLRETURN rc;
  int retcode;
  if (nArgs != 3) {
    goto argcount_failure;
  }
  nullind[2] = SQL_NULL_DATA;
  if (nullind[0] == SQL_NULL_DATA || nullind[1] == SQL_NULL_DATA) {
    goto null_input_detected;
  }
  rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  rc = SQLPrepare(hstmt, "SELECT P2,P3 FROM EXTPROCEXAMPLE1_TAB "
    "WHERE P1 = ? AND P2 > ?", SQL_NTS);
  if (rc != SQL_SUCCESS) {
    goto failure; }
  rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG,
       SQL_INTEGER, 0, 0, p1, sizeof(*p1), NULL);
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT,
       SQL_C_SLONG, SQL_INTEGER, 0, 0, p2, sizeof(*p2), NULL);
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  rc = SQLBindColumn(hstmt, 1, SQL_C_SLONG, p2, 0, NULL);
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  rc = SQLBindColumn(hstmt, 2, SQL_C_CHAR, p3, CHARMAXBYTELEN(10), NULL);
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  rc = SQLExecute(hstmt);
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  rc = SQLFetch(hstmt);
  if (rc == SQL_NO_DATA) {
    goto no_data_found;
  }
  if (rc != SQL_SUCCESS) {
    goto failure;
  }
  nullind[1] = sizeof(int); nullind[2] = sizeof(int);
  retcode = SQL_SUCCESS;
  cleanup:;
    if (hstmt != SQL_NULL_HANDLE) {
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    }
    return retcode; null_input_detected:;
  no_data_found:;
    retcode = SQL_SUCCESS;
    nullind[1] = SQL_NULL_DATA;
    nullind[2] = SQL_NULL_DATA;
    goto cleanup;
  failure:;
    retcode = SQL_ERROR;
    if (hstmt != SQL_NULL_HANDLE) {
      rc = SQLError(SQL_NULL_HANDLE, SQL_NULL_HANDLE, hstmt,
                 sqlst, &nativeerror, diagMsg, 71, NULL
    );
    if (rc == SQL_NO_DATA) {
      goto generic_failure; } }
    else {
      rc = SQLError(SQL_NULL_HANDLE, hdbc, SQL_NULL_HANDLE,
          sqlst, &nativeerror, diagMsg, 71, NULL);
      if (rc == SQL_NO_DATA) {
        goto generic_failure;
      }
    }
    goto exit_cleanup;
  generic_failure:;
    memcpy(sqlst, "1C000", 6);
    memcpy(diagMsg, "Generic error", 14);
  goto exit_cleanup;
  argcount_failure:;
    memcpy(sqlst, "1C000", 6);
    strncpy(diagMsg, "wrong number of arguments, 3 expected", 71);
    goto exit_cleanup;
  }
Go up to
SQL: Statements