#ifdef RCSID static char *RCSid = "$Header: cdemo2.c 7010300.1 94/02/24 18:40:41 snataraj Generic $ "; #endif /* RCSID */ /* Copyright (c) 1991 by Oracle Corporation */ /* NAME cdemo2.c - C demo Program # 2 MODIFIED (MM/DD/YY) emendez 02/07/94 - fix for bug 196094 emendez 02/02/94 - Fix for bug 157576 gdoherty 01/31/94 - make oci header inclusion for ansi or k+r adaptive tssmith 03/29/93 - Removing ANSI material lfeng 03/08/93 - merge changes from branch 1.6.312.1 lfeng 02/10/93 - portability mods rkooi2 11/27/92 - Changing e... datatypes to s... lfeng 11/20/92 - add portability mods rkooi2 10/22/92 - Changes for portability rkooi2 10/18/92 - Changes to make it portable sjain 08/18/92 - Changge connect to conect_user sjain 03/16/92 - Creation */ /* This program accepts arbitrary SQL statements from the user, and processes the statement. Statements may be entered on multiple lines, and must be terminated by a semi-colon. If a query, the results are printed. */ #include #include #include /* Include OCI-specific headers. */ #include #include #ifdef __STDC__ #include #else #include #endif #include /* Constants used in this program. */ #define MAX_BINDS 12 #define MAX_ITEM_BUFFER_SIZE 33 #define MAX_SELECT_LIST_SIZE 12 #define MAX_SQL_IDENTIFIER 31 #define PARSE_NO_DEFER 0 #define PARSE_V7_LNG 2 /* Define one logon data area and one cursor data area Also define a host data area for orlon. (See ocidfn.h for declarations). */ Lda_Def lda; Cda_Def cda; ub1 hda[HDA_SIZE]; /* Declare an array of bind values. */ text bind_values[MAX_BINDS][MAX_ITEM_BUFFER_SIZE]; /* Declare structures for query information. */ struct describe { sb4 dbsize; sb2 dbtype; sb1 buf[MAX_ITEM_BUFFER_SIZE]; sb4 buflen; sb4 dsize; sb2 precision; sb2 scale; sb2 nullok; }; struct define { ub1 buf[MAX_ITEM_BUFFER_SIZE]; float flt_buf; sword int_buf; sb2 indp; ub2 col_retlen, col_retcode; }; /* Define arrays of describe and define structs. */ struct describe desc[MAX_SELECT_LIST_SIZE]; struct define def[MAX_SELECT_LIST_SIZE]; /* Declare this programs functions. */ sword connect_user(); sword describe_define(); sword do_binds(); void do_exit(); void oci_error(); sword get_sql_statement(); void print_header(); void print_rows(); /* Globals */ static text sql_statement[2048]; static sword sql_function; static sword numwidth = 8; main() { sword col, errno, n, ncols; text *cp; /* Connect to ORACLE. */ if (connect_user()) exit(-1); /* Open a cursor, exit on error (unrecoverable). */ if (oopen(&cda, &lda, (text *) 0, -1, -1, (text *) 0, -1)) { printf("Error opening cursor. Exiting...\n"); ologof(&lda); exit(-1); } /* Process user's SQL statements. */ for (;;) { /* Get the statement, exit on "exit". */ if (get_sql_statement()) do_exit(0); /* Parse the statement; do not defer the parse, so that errors come back right away. */ if (oparse(&cda, (text *) sql_statement, (sb4) -1, (sword) PARSE_NO_DEFER, (ub4) PARSE_V7_LNG)) { oci_error(&cda); continue; } /* Save the SQL function code right after parse. */ sql_function = cda.ft; /* Bind any input variables. */ if ((ncols = do_binds(&cda, sql_statement)) == -1) continue; /* If the statement is a query, describe and define all select-list items before doing the oexec. */ if (sql_function == FT_SELECT) if ((ncols = describe_define(&cda)) == -1) continue; /* Execute the statement. */ if (oexec(&cda)) { oci_error(&cda); continue; } /* Fetch and display the rows for the query. */ if (sql_function == FT_SELECT) { print_header(ncols); print_rows(&cda, ncols); } /* Print the rows-processed count. */ if (sql_function == FT_SELECT || sql_function == FT_UPDATE || sql_function == FT_DELETE || sql_function == FT_INSERT) printf("\n%d row%c processed.\n", cda.rpc, cda.rpc == 1 ? '\0' : 's'); else printf("\nStatement processed.\n"); } /* end for (;;) */ } /* end main() */ sword connect_user() { text username[132]; text password[132]; sword n; /* Three tries to connect. */ for (n = 3; --n >= 0; ) { printf("Username: "); gets((char *) username); printf("Password: "); gets((char *) password); if (orlon(&lda, hda, username, -1, password, -1, -1)) { printf("Cannot connect as %s.\n", username); printf("Try again.\n\n"); } else { return 0; } } printf("Connection failed. Exiting...\n"); return -1; } /* Describe select-list items. */ sword describe_define(cda) Cda_Def *cda; { sword col, deflen, deftyp; static ub1 *defptr; /* Describe the select-list items. */ for (col = 0; col < MAX_SELECT_LIST_SIZE; col++) { desc[col].buflen = MAX_ITEM_BUFFER_SIZE; if (odescr(cda, col + 1, &desc[col].dbsize, &desc[col].dbtype, &desc[col].buf[0], &desc[col].buflen, &desc[col].dsize, &desc[col].precision, &desc[col].scale, &desc[col].nullok)) { /* Break on end of select list. */ if (cda->rc == VAR_NOT_IN_LIST) break; else { oci_error(cda); return -1; } } /* adjust sizes and types for display */ switch (desc[col].dbtype) { case NUMBER_TYPE: desc[col].dbsize = numwidth; /* Handle NUMBER with scale as float. */ if (desc[col].scale != 0) { defptr = (ub1 *) &def[col].flt_buf; deflen = (sword) sizeof(float); deftyp = FLOAT_TYPE; desc[col].dbtype = FLOAT_TYPE; } else { defptr = (ub1 *) &def[col].int_buf; deflen = (sword) sizeof(sword); deftyp = INT_TYPE; desc[col].dbtype = INT_TYPE; } break; default: if (desc[col].dbtype == DATE_TYPE) desc[col].dbsize = 9; if (desc[col].dbtype == ROWID_TYPE) desc[col].dbsize = 18; defptr = def[col].buf; deflen = desc[col].dbsize > MAX_ITEM_BUFFER_SIZE ? MAX_ITEM_BUFFER_SIZE : desc[col].dbsize + 1; deftyp = STRING_TYPE; break; } if (odefin(cda, col + 1, defptr, deflen, deftyp, -1, &def[col].indp, (text *) 0, -1, -1, &def[col].col_retlen, &def[col].col_retcode)) { oci_error(cda); return -1; } } return col; } /* Bind input variables. */ sword do_binds(cda, stmt_buf) Cda_Def *cda; text *stmt_buf; { sword i, in_literal, n; text *cp, *ph; /* Find and bind input variables for placeholders. */ for (i = 0, in_literal = FALSE, cp = stmt_buf; *cp && i < MAX_BINDS; cp++) { if (*cp == '\'') in_literal = ~in_literal; if (*cp == ':' && !in_literal) { for (ph = ++cp, n = 0; *cp && (isalnum(*cp) || *cp == '_') && n < MAX_SQL_IDENTIFIER; cp++, n++ ) ; *cp = '\0'; printf("Enter value for %s: ", ph); gets((char *) &bind_values[i][0]); /* Do the bind, using obndrv(). NOTE: the bind variable address must be static. This would not work if bind_values were an auto on the do_binds stack. */ if (obndrv(cda, ph, -1, &bind_values[i][0], -1, VARCHAR2_TYPE, -1, (sb2 *) 0, (text *) 0, -1, -1)) { oci_error(cda); return -1; } i++; } /* end if (*cp == ...) */ } /* end for () */ return i; } /* Clean up and exit. LDA and CDA are global. */ void do_exit(rv) sword rv; { if (oclose(&cda)) fputs("Error closing cursor!\n", stdout); if (ologof(&lda)) fputs("Error logging off!\n", stdout); exit(rv); } void oci_error(cda) Cda_Def *cda; { text msg[512]; sword n; fputs("\n-- ORACLE ERROR --\n", stderr); n = oerhms(&lda, cda->rc, msg, (sword) sizeof (msg)); fprintf(stderr, "%.*s", n, msg); fprintf(stderr, "Processing OCI function %s\n", oci_func_tab[cda->fc]); fprintf(stderr, "Do you want to continue? [yn]: "); fgets((char *) msg, (int) sizeof (msg), stdin); if (*msg != '\n' && *msg != 'y' && *msg != 'Y') do_exit(1); fputc('\n', stdout); } sword get_sql_statement() { text cbuf[1024]; text *cp; sword stmt_level; for (stmt_level = 1; ;) { if (stmt_level == 1) { /* Init statement buffer and print prompt. */ *sql_statement = '\0'; fputs("\nOCISQL> ", stdout); } else { printf("%3d ", stmt_level); } /* Get (part of) a SQL statement. */ gets((char *) cbuf); if (*cbuf == '\0') continue; if (strncmp((char *) cbuf, "exit", 4) == 0) return -1; /* Concatenate to statement buffer. */ if (stmt_level > 1) strcat((char *) sql_statement, " "); strcat((char *) sql_statement, (char *) cbuf); /* Check for possible terminator. */ cp = &sql_statement[strlen((char *) sql_statement) - 1]; while (isspace(*cp)) cp--; if (*cp == ';') { *cp = '\0'; break; } stmt_level++; } return 0; } void print_header(ncols) sword ncols; { sword col, n; fputc('\n', stdout); for (col = 0; col < ncols; col++) { n = desc[col].dbsize - desc[col].buflen; if (desc[col].dbtype == FLOAT_TYPE || desc[col].dbtype == INT_TYPE) { printf("%*c", n, ' '); printf("%*.*s", desc[col].buflen, desc[col].buflen, desc[col].buf); } else { printf("%*.*s", desc[col].buflen, desc[col].buflen, desc[col].buf); printf("%*c", n, ' '); } fputc(' ', stdout); } fputc('\n', stdout); for (col = 0; col < ncols; col++) { for (n = desc[col].dbsize; --n >= 0; ) fputc('-', stdout); fputc(' ', stdout); } fputc('\n', stdout); } void print_rows(cda, ncols) Cda_Def *cda; sword ncols; { sword col, n; for (;;) { fputc('\n', stdout); /* Fetch a row. Break on end of fetch, disregard null fetch "error". */ if (ofetch(cda)) { if (cda->rc == NO_DATA_FOUND) break; if (cda->rc != NULL_VALUE_RETURNED) oci_error(cda); } for (col = 0; col < ncols ; col++) { /* Check col. return code for null. If null, print n spaces, else print value. */ if (def[col].indp < 0) printf("%*c", desc[col].dbsize, ' '); else { switch (desc[col].dbtype) { case FLOAT_TYPE: printf("%*.*f", numwidth, 2, def[col].flt_buf); break; case INT_TYPE: printf("%*d", numwidth, def[col].int_buf); break; default: printf("%s", def[col].buf); n = desc[col].dbsize - strlen(def[col].buf); if (n > 0) printf("%*c", n, ' '); break; } } fputc(' ', stdout); } } /* end for (;;) */ }