In PL/SQL, all queries must have an INTO clause containing placeholders (host variables and/or PL/SQL variables) that correspond to items in the select list. For example, the following SELECT statement is not valid inside a PL/SQL block:
SELECT ename, sal FROM emp;
Instead, it must be coded as follows:
SELECT ename, sal INTO :my_ename, :my_sal FROM emp;
In the last statement, my_ename and my_sal are SQL placeholders that correspond to the ename and sal columns in the select list. You must bind these placeholders using the OBNDRA, OBINDPS, or OBNDRV call. You can bind host arrays to PL/SQL tables using the OBNDRA or OBINDPS call.
Also, you must use named placeholders such as my_ename in PL/SQL blocks. Numbered placeholders such as 10 and the corresponding OBNDRN call are not supported for PL/SQL blocks.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <oratypes.h> #include <ocidfn.h> #ifdef __STDC__ #include <ociapr.h> #else #include <ocikpr.h> #endif #include <ocidem.h> Cda_Def cda; Lda_Def lda; ub1 hda[256]; text sqlstm[2048]; void error_handler(); main() { int acct_number; text trans_type[1]; float trans_amt; text status[80]; if (olog(&lda, hda, "scott/tiger", -1, (text *) 0, -1, (text *) 0, -1, OCI_LM_DEF)) { printf("Connect failed.\n"); exit(EXIT_FAILURE); } if (oopen(&cda, &lda, (text *) 0, -1, -1, (text *) 0, -1)) { printf("Error opening cursor. Exiting...\n"); exit(EXIT_FAILURE); } printf("\nConnected to Oracle.\n"); /* Construct a PL/SQL block. */ strcpy(sqlstm, "DECLARE\ old_bal NUMBER(9,2);\ err_msg CHAR(70);\ nonexistent EXCEPTION;\ BEGIN\ :xtrans_type := UPPER(:xtrans_type);\ IF :xtrans_type = 'C' THEN\ UPDATE ACCTS SET BAL = BAL + :xtrans_amt\ WHERE ACCTID = :xacct_number;\ IF SQL%ROWCOUNT = 0 THEN\ RAISE nonexistent;\ ELSE\ :xstatus := 'Credit applied';\ END IF;\ ELSIF :xtrans_type = 'D' THEN\ SELECT BAL INTO old_bal FROM accts\ WHERE acctid = :xacct_number;\ IF old_bal = :xtrans_amt THEN\ UPDATE accts SET bal = bal - :xtrans_amt\ WHERE acctid = :xacct_number;\ :xstatus := 'Debit applied';\ ELSE\ :xstatus := 'Insufficient funds';\ END IF;\ ELSE\ :xstatus := 'Invalid type: ' || :xtrans_type;\ END IF;\ COMMIT;\ EXCEPTION\ WHEN NO_DATA_FOUND OR nonexistent THEN\ :xstatus := 'Nonexistent account';\ WHEN OTHERS THEN\ err_msg := SUBSTR(SQLERRM, 1, 70);\ :xstatus := 'Error: ' || err_msg;\ END;"); /* Parse the PL/SQL block. */ if (oparse(&cda, sqlstm, -1, 0, 2)) { error_handler(&cda); exit(EXIT_FAILURE); } /* Bind the status variable. */ if (obndrv(&cda, ":xstatus", -1, status, 70, 5, -1, (text *) 0, (text *) 0, -1, -1)) { error_handler(&cda); exit(EXIT_FAILURE); } /* Bind the transaction type variable. */ if (obndrv(&cda, ":xtrans_type", -1, trans_type, 1, 1, -1, (text *) 0, (text *) 0, -1, -1)) { error_handler(&cda); exit(EXIT_FAILURE); } /* Bind the account number. */ if (obndrv(&cda, ":xacct_number", -1, &acct_number, sizeof (int), 3, -1, (text *) 0, (text *) 0, -1, -1)) { error_handler(&cda); exit(EXIT_FAILURE); } /* Bind the transaction amount variable. */ if (obndrv(&cda, ":xtrans_amt", -1, &trans_amt, sizeof (float), 4, -1, (text *) 0, (text *) 0, -1, -1)) { error_handler(&cda); exit(EXIT_FAILURE); } for (;;) { printf("\nAccount number: "); scanf("%d", &acct_number); fflush(stdin); if (acct_number == 0) break; printf("Transaction type (D or C): "); scanf("%c", trans_type); fflush(stdin); printf("Transaction amount: "); scanf("%f", &trans_amt); fflush(stdin); /* Execute the block. */ if (oexec(&cda)) error_handler(&cda); printf("%s\n", status); } printf("Have a good day!\n"); exit(EXIT_SUCCESS); } void error_handler(cursor) Cda_Def *cursor; { sword n; text msg[512]; printf("\n-- ORACLE error--\n"); printf("\n"); n = oerhms(&lda, cursor->rc, msg, (sword) sizeof msg); fprintf(stderr, "%s\n", msg); if (cursor->fc > 0) fprintf(stderr, "Processing OCI function %s", oci_func_tab[cursor->fc]); }
strcpy(plsql_block, "BEGIN raise_salary(:emp_id, :amount); END;");
Then, the PL/SQL string can be bound and executed like a SQL statement.