Re: Dynamic SQL in Pro*C calling stored package procedures
Date: 2000/07/07
Message-ID: <8k4p33$n9g$1_at_nnrp1.deja.com>
This may be more complicated than you need, but it should work:
/******************************************************************* Modified Sample Program 10: Dynamic SQL Method 4 Modified: 27 Jan 97 12:37:06
This program connects you to ORACLE using your username and
password, then prompts you for a SQL statement. You can enter
any legal SQL statement. Use regular SQL syntax, not embedded SQL.
Your statement will be processed. If it is a query, the rows
fetched are displayed.
You can enter multi-line statements. The limit is 1023 characters.
This sample program only processes up to MAX_ITEMS bind variables and
MAX_ITEMS select-list items. MAX_ITEMS is #defined to be 40.
This program has been updated from the original sample to allow output
values from calls to stored procedures/functions or pl/sql blocks
generally. Note pl/sql blocks must start with "begin" or "BEGIN". The
original sample10.pc only really allowed for input parameters since
the space allocated in the bind descriptor was based on the value
entered.
The program has been changed to always allocate 32K for each
parameter if the sql entered starts with "begin". This has meant
changing the datatype to 5 (ie null terminated string) as opposed to 1
(blank padded string) so the data can be easily printed. The
process_select_list routine has also been changed to print out the
values
of all parameters when a pl/sql block is called. This is really
a best guess technique because, unlike oci, there is no direct way to
describe
a procedure in dynamic method 4 (other than using plsql dbms
routines) to see what parameter types it has and whether they are
ins/outs etc. When running this program to call pl/sql blocks, the
program will prompt for all bind variables. Simply enter "null" for
the output variables.
*******************************************************************/ #include <stdio.h>
#include <string.h>
#include <setjmp.h>
/* Maximum number of select-list items or bind variables. */ #define MAX_ITEMS 40
/* Maximum lengths of the _names_ of the
select-list items or indicator variables. */
#define MAX_VNAME_LEN 30 #define MAX_INAME_LEN 30 #ifndef NULL
#define NULL 0
#endif
char *dml_commands[] = {"SELECT", "select", "INSERT", "insert",
"UPDATE", "update", "DELETE", "delete"};
EXEC SQL BEGIN DECLARE SECTION;
char sql_statement[1024];
EXEC SQL VAR sql_statement IS STRING(1024);
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE sqlca;
EXEC SQL INCLUDE sqlda;
SQLDA *bind_dp;
SQLDA *select_dp;
extern SQLDA *sqlald();
extern void sqlnul();
/* Define a buffer to hold longjmp state info. */ jmp_buf jmp_continue;
/* A global flag for the error routine. */ int parse_flag = 0;
main()
{
int oracle_connect();
int alloc_descriptors();
int get_sql_statement();
int set_bind_variables();
int process_select_list();
int i;
/* Connect to the database. */
if (oracle_connect() != 0)
exit(1);
/* Allocate memory for the select and bind descriptors. */
if (alloc_descriptors(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN) != 0)
exit(1);
/* Process SQL statements. */
for (;;)
{
i = setjmp(jmp_continue);
/* Get the statement. Break on "exit". */ if (get_sql_statement() != 0) break; /* Prepare the statement and declare a cursor. */ EXEC SQL WHENEVER SQLERROR DO sql_error(); parse_flag = 1; /* Set a flag for sql_error(). */ EXEC SQL PREPARE S FROM :sql_statement; parse_flag = 0; /* Unset the flag. */ EXEC SQL DECLARE C CURSOR FOR S; /* Set the bind variables for any placeholders in the SQL statement. */ set_bind_variables(); /* Open the cursor and execute the statement. * If the statement is not a query (SELECT), the * statement processing is completed after the * OPEN. */ EXEC SQL OPEN C USING DESCRIPTOR bind_dp; /* Call the function that processes the select-list. * If the statement is not a query, this function * just returns, doing nothing. */ process_select_list(); /* Tell user how many rows processed. */ for (i = 0; i < 8; i++) { if (strncmp(sql_statement, dml_commands[i], 6) == 0) { printf("\n\n%25d row%25c processed.\n", sqlca.sqlerrd[2], sqlca.sqlerrd[2] == 1 ? '\0' : 's'); break; } } } /* end of for(;;) statement-processing loop */for (i = 0; i < MAX_ITEMS; i++)
/* When done, free the memory allocated for
pointers in the bind and select descriptors. */
{
if (bind_dp->V[i] != (char *) 0) free(bind_dp->V[i]); free(bind_dp->I[i]); /* MAX_ITEMS were allocated. */ if (select_dp->V[i] != (char *) 0) free(select_dp->V[i]); free(select_dp->I[i]); /* MAX_ITEMS were allocated. */}
/* Free space used by the descriptors themselves. */
sqlclu(bind_dp);
sqlclu(select_dp);
EXEC SQL WHENEVER SQLERROR CONTINUE;
/* Close the cursor. */
EXEC SQL CLOSE C;
EXEC SQL COMMIT WORK RELEASE;
puts("\nHave a good day!\n");
EXEC SQL WHENEVER SQLERROR DO sql_error();
return;
}
oracle_connect()
{
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR username[128]; VARCHAR password[32];
EXEC SQL END DECLARE SECTION;
printf("\nusername: "); fgets((char *) username.arr, sizeof username.arr, stdin); fflush(stdin);
username.arr[strlen((char *) username.arr)-1] = '\0'; username.len = strlen((char *) username.arr);
printf("password: "); fgets((char *) password.arr, sizeof password.arr, stdin); fflush(stdin);
password.arr[strlen((char *) password.arr) - 1] = '\0'; password.len = strlen((char *) password.arr);
EXEC SQL WHENEVER SQLERROR GOTO connect_error;
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user %25s.\n", username.arr);
return 0;
connect_error:
fprintf(stderr, "Cannot connect to ORACLE as user %25s\n", username.arr);
return -1;
}
/* * Allocate the BIND and SELECT descriptors using sqlald(). * Also allocate the pointers to indicator variables * in each descriptor. The pointers to the actual bind * variables and the select-list items are realloc'ed in * the set_bind_variables() or process_select_list() * routines. This routine allocates 1 byte for select_dp->V[i] * and bind_dp->V[i], so the realloc will work correctly. */
alloc_descriptors(size, max_vname_len, max_iname_len)
int size;
int max_vname_len;
int max_iname_len;
{
int i;
/* * The first sqlald parameter determines the maximum number of * array elements in each variable in the descriptor. In * other words, it determines the maximum number of bind * variables or select-list items in the SQL statement. * * The second parameter determines the maximum length of * strings used to hold the names of select-list items * or placeholders. The maximum length of column * names in ORACLE is 30, but you can allocate more or less * as needed. * * The third parameter determines the maximum length of * strings used to hold the names of any indicator * variables. To follow ORACLE standards, the maximum * length of these should be 30. But, you can allocate * more or less as needed. */ if ((bind_dp = sqlald(size, max_vname_len, max_iname_len)) == (SQLDA *) 0) { fprintf(stderr, "Cannot allocate memory for bind descriptor."); return -1; /* Have to exit in this case. */}
if ((select_dp =
sqlald (size, max_vname_len, max_iname_len)) == (SQLDA *) 0) {
fprintf(stderr, "Cannot allocate memory for select descriptor."); return -1;
}
select_dp->N = MAX_ITEMS;
/* Allocate the pointers to the indicator variables, and the
actual data. */
for (i = 0; i < MAX_ITEMS; i++) {
bind_dp->I[i] = (short *) malloc(sizeof (short)); select_dp->I[i] = (short *) malloc(sizeof(short)); bind_dp->V[i] = (char *) malloc(1); select_dp->V[i] = (char *) malloc(1);}
return 0;
}
get_sql_statement()
{
char *cp, linebuf[256];
int iter, plsql;
int help();
for (plsql = 0, iter = 1; ;)
{
if (iter == 1) { printf("\nSQL> "); sql_statement[0] = '\0'; } fgets(linebuf, sizeof linebuf, stdin); fflush(stdin); cp = strrchr(linebuf, '\n'); if (cp && cp != linebuf) *cp = ' '; else if (cp == linebuf) continue; if ((strncmp(linebuf, "EXIT", 4) == 0) || (strncmp(linebuf, "exit", 4) == 0)) { return -1; } else if (linebuf[0] == '?' || (strncmp(linebuf, "HELP", 4) == 0) || (strncmp(linebuf, "help", 4) == 0)) { help(); iter = 1; continue; } if (strstr(linebuf, "BEGIN") || (strstr(linebuf, "begin"))) { plsql = 1; } strcat(sql_statement, linebuf); if ((plsql && (cp = strrchr(sql_statement, '/'))) || (!plsql && (cp = strrchr(sql_statement, ';')))) { *cp = '\0'; break; } else { iter++; printf("%253d ", iter); }
}
return 0;
}
set_bind_variables()
{
int i, n;
char bind_var[64];
/* Describe any bind variables (input host variables) */
EXEC SQL WHENEVER SQLERROR DO sql_error();
bind_dp->N = MAX_ITEMS; /* Initialize count of array elements. */ EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;
/* If F is negative, there were more bind variables
than originally allocated by sqlald(). */
if (bind_dp->F < 0)
{
printf ("\nToo many bind variables (%25d), maximum is %25d\n.", -bind_dp->F, MAX_ITEMS); return;
}
/* Set the maximum number of array elements in the
descriptor to the number found. */ bind_dp->N = bind_dp->F;
/* Get the value of each bind variable as a
- character string. *
- C[i] contains the length of the bind variable
- name used in the SQL statement.
- S[i] contains the actual name of the bind variable
- used in the SQL statement. *
- L[i] will contain the length of the data value
- entered. *
- V[i] will contain the address of the data value
- entered. *
- T[i] is always set to 1 because in this sample program
- data values for all bind variables are entered
- as character strings.
- ORACLE converts to the table value from CHAR. *
- I[i] will point to the indicator value, which is
- set to -1 when the bind variable value is "null". */ for (i = 0; i < bind_dp->F; i++) { printf ("\nEnter value for bind variable %25.*s: ", (int)bind_dp->C[i], bind_dp->S[i]); fgets(bind_var, sizeof bind_var, stdin);
/* Get length and remove the new line character. */ n = strlen(bind_var) - 1; /* Set it in the descriptor. */ bind_dp->L[i] = n; if ( ( strncmp(sql_statement,"begin",5) == 0 || strncmp(sql_statement,"BEGIN",5) == 0 ) ) { /* * Could be an output variable so allocate 32K. */ bind_dp->L[i] = 32000; bind_dp->T[i] = 5; /* Set to string so we can print return */ } /* (re-)allocate the buffer for the value. sqlald() reserves a pointer location for V[i] but does not allocate the full space for the pointer. */ bind_dp->V[i] = (char *) realloc(bind_dp->V[i], (bind_dp->L[i] + 1)); /* And copy it in. */ strncpy(bind_dp->V[i], bind_var, n); bind_dp->V[i][n] = '\0'; /* In case we're using type 5!*/
/* Set the indicator variable's value. */ if ((strncmp(bind_dp->V[i], "NULL", 4) == 0) || (strncmp(bind_dp->V[i], "null", 4) == 0)) *bind_dp->I[i] = -1; else *bind_dp->I[i] = 0; /* Set the bind datatype to 1 (CHAR) for input only (ie non * pl/sql but 5 STRING for possible output (else we'll end up * printing 32000 chars!). i */ if (bind_dp->T[i] != 5) bind_dp->T[i] = 1;
}
}
process_select_list()
{
int i, null_ok, precision, scale;
if (strncmp(sql_statement, "BEGIN", 5) == 0 ||
strncmp(sql_statement, "begin", 5) == 0) {
/* * Is plsql block so print all parameters. */ for ( i=0; i<bind_dp->F; i++ ) { printf ("\nValue of bind variable %25.*s: %25.*s", (int)bind_dp->C[i], bind_dp->S[i], (int)bind_dp->L[i], bind_dp->V[i]); } select_dp->F = 0; return;
}
else if ((strncmp(sql_statement, "SELECT", 6) != 0) &&
(strncmp(sql_statement, "select", 6) != 0)) {
select_dp->F = 0; return;
}
/* If the SQL statement is a SELECT, describe the
select-list items. The DESCRIBE function returns their names, datatypes, lengths (including precision and scale), and NULL/NOT NULL statuses. */
select_dp->N = MAX_ITEMS;
EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;
/* If F is negative, there were more select-list
items than originally allocated by sqlald(). */
if (select_dp->F < 0)
{
printf ("\nToo many select-list items (%25d), maximum is % 25d\n",
-(select_dp->F), MAX_ITEMS); return;
}
/* Set the maximum number of array elements in the
descriptor to the number found. */ select_dp->N = select_dp->F;
/* Allocate storage for each select-list item.
sqlprc() is used to extract precision and scale from the length (select_dp->L[i]). sqlnul() is used to reset the high-order bit of the datatype and to check whether the column is NOT NULL. CHAR datatypes have length, but zero precision and scale. The length is defined at CREATE time. NUMBER datatypes have precision and scale only if defined at CREATE time. If the column definition was just NUMBER, the precision and scale are zero, and you must allocate the required maximum length. DATE datatypes return a length of 7 if the default format is used. This should be increased to 9 to store the actual date character string. If you use the TO_CHAR function, the maximum length could be 75, but will probably be less (you can see the effects of this in SQL*Plus). ROWID datatype always returns a fixed length of 18 if coerced to CHAR. LONG and LONG RAW datatypes return a length of 0 (zero), so you need to set a maximum. In this example, it is 240 characters. */
printf ("\n");
for (i = 0; i < select_dp->F; i++)
{
/* Turn off high-order bit of datatype (in this example, it does not matter if the column is NOT NULL). */ sqlnul (&(select_dp->T[i]), &(select_dp->T[i]), &null_ok); switch (select_dp->T[i]) { case 1 : /* CHAR datatype: no change in length needed, except possibly for TO_CHAR conversions (not handled here). */ break; case 2 : /* NUMBER datatype: use sqlprc() to extract precision and scale. */ sqlprc (&(select_dp->L[i]), &precision, &scale); /* Allow for maximum size of NUMBER. */ if (precision == 0) precision = 40; /* Also allow for decimal point and possible sign. */ /* convert NUMBER datatype to FLOAT if scale > 0, INT otherwise. */ if (scale > 0) select_dp->L[i] = sizeof(float); else select_dp->L[i] = sizeof(int); break; case 8 : /* LONG datatype */ select_dp->L[i] = 240; break; case 11 : /* ROWID datatype */ select_dp->L[i] = 18; break; case 12 : /* DATE datatype */ select_dp->L[i] = 9; break; case 23 : /* RAW datatype */ break; case 24 : /* LONG RAW datatype */ select_dp->L[i] = 240; break; } /* Allocate space for the select-list data values. sqlald() reserves a pointer location for V[i] but does not allocate the full space for the pointer. */ if (select_dp->T[i] != 2) select_dp->V[i] = (char *) realloc(select_dp->V[i], select_dp->L[i] + 1); else select_dp->V[i] = (char *) realloc(select_dp->V[i], select_dp->L[i]); /* Print column headings, right-justifying number column headings. */ if (select_dp->T[i] == 2) if (scale > 0) printf ("%25.*s ", select_dp->L[i]+3, select_dp->S[i]); else printf ("%25.*s ", select_dp->L[i], select_dp->S[i]); else printf ("%25-.*s ", select_dp->L[i], select_dp->S[i]); /* Coerce ALL datatypes except for LONG RAW and NUMBER to character. */ if (select_dp->T[i] != 24 && select_dp->T[i] != 2) select_dp->T[i] = 1; /* Coerce the datatypes of NUMBERs to float or int depending on the scale. */ if (select_dp->T[i] == 2) if (scale > 0) select_dp->T[i] = 4; /* float */ else select_dp->T[i] = 3; /* int */}
printf ("\n\n");
/* FETCH each row selected and print the column values. */
EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;
for (;;)
{
EXEC SQL FETCH C USING DESCRIPTOR select_dp;
/* Since each variable returned has been coerced to a character string, int, or float very little processing is required here. This routine just prints out the values on the terminal. */ for (i = 0; i < select_dp->F; i++) { if (*select_dp->I[i] < 0) if (select_dp->T[i] == 4) printf ("%25-*c ",(int)select_dp->L[i]+3, ' '); else printf ("%25-*c ",(int)select_dp->L[i], ' '); else if (select_dp->T[i] == 3) /* int datatype */ printf ("%25*d ", (int)select_dp->L[i], *(int *)select_dp->V[i]); else if (select_dp->T[i] == 4) /* float datatype */ printf ("%25*.2f ", (int)select_dp->L[i], *(float *)select_dp->V[i]); else /* character string */ printf ("%25-*s ", (int)select_dp->L[i], select_dp->V [i]); } printf ("\n");
}
end_select_loop:
return;
}
help()
{
puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");
puts("Statements can be continued over several lines, except"); puts("within string literals."); puts("Terminate a SQL statement with a semicolon."); puts("Terminate a PL/SQL block (which can contain embeddedsemicolons)");
puts("with a slash (/)."); puts("Typing \"exit\" (no semicolon needed) exits the program."); puts("You typed \"?\" or \"help\" to get this message.\n\n");}
sql_error()
{
int i;
/* ORACLE error handler */
printf ("\n\n%25.70s\n",sqlca.sqlerrm.sqlerrmc);
if (parse_flag)
printf ("Parse error at character offset %25d in SQL statement.\n", sqlca.sqlerrd[4]);
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK;
longjmp(jmp_continue, 1);
}
Sent via Deja.com http://www.deja.com/
Before you buy.
Received on Fri Jul 07 2000 - 00:00:00 CEST