1 /*
2 FileName : dyn4.pc
3 Function : sample program of dynamic SQL method4
4 Usage : dyn4 [-uUSERNAME -pPASSWORD] (default: scott/tiger)
5 */
6 #include <stdio.h>
7 #include <string.h>
8 #include <setjmp.h>
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <sqlca.h>
12 #include <sqlda.h>
13 #include <sqlcpr.h>
14 #include <ocidfn.h>
15
16 #define MAX_ITEMS 40 /* Max number of select-list items/bind variables. */
17 #define MAX_VNAME 30 /* Max lengths of the _names_ of select-list items */
18 #define MAX_INAME 30 /* Max lengths of the _names_ of indicator variables */
19 #define MAX_USERNM 32
20 #define MAX_STMT 1024
21 #define MAX_LBUF 256
22 #define MAX_FMT1 12
23 #define ERR_CONN 1
24 #define ERR_ALLOC 2
25 #define ERR_OVR_MAX_ITEMS 10
26 #define DEF_PRECISION 10
27 #define DEF_USERNAME "scott"
28 #define DEF_PASSNAME "tiger"
29
30 void sql_error(char *);
31 void set_bind_variables(void);
32 void help(void);
33 int connect(void);
34 int alloc_descriptors(int, int, int);
35 int get_dyn_statement(void);
36 int do_select(void);
37
38 #define SZ_STTSTR 4 /* EXIT/QUIT/HELP */
39 char *stt_command[] = {"EXIT","QUIT","HELP"};
40 #define SZ_STTCMDS (sizeof(stt_command)/sizeof(char *))
41
42 #define SZ_CMDSTR 6 /* SELECT/DELETE/INSERT/UPDATE */
43 char *dml_command[] = {"SELECT", "INSERT", "UPDATE", "DELETE"};
44 #define SZ_CMDS (sizeof(dml_command)/sizeof(char *))
45 #define CMD_SELECT 0
46
47 EXEC SQL BEGIN DECLARE SECTION;
48 char dyn_statement[MAX_STMT],username[MAX_USERNM],password[MAX_USERNM];
49 EXEC SQL END DECLARE SECTION;
50
51 int parse_flag = 0; /* flag to parse for the error routine. */
52 SQLDA *bind_dp,*select_dp;
53
54 int main(int argc,char *argv[]) {
55 int i,ret,cmd;
56
57 get_opt(argc,argv);
58 if (connect() != 0) die_err(ERR_CONN);
59
60 /* Allocate memory for the select and bind descriptors. */
61 if (alloc_descriptors(MAX_ITEMS, MAX_VNAME, MAX_INAME) != 0)
62 die_err(ERR_ALLOC);
63
64 /* Process SQL statements. */
65 for (;;) {
66 ret = -1;
67 if (get_dyn_statement() != 0) break; /* Get the statement */
68 cmd = chk_command(dyn_statement,SZ_CMDSTR,dml_command,SZ_CMDS);
69
70 /* Prepare the statement and declare a cursor. */
71 EXEC SQL WHENEVER SQLERROR GOTO err_loop;
72
73 parse_flag = 1; /* Set a flag for sql_error(). */
74 EXEC SQL PREPARE S FROM :dyn_statement;
75 parse_flag = 0; /* Unset the flag. */
76
77 get_sql_stmt();
78 EXEC SQL DECLARE C CURSOR FOR S;
79
80 /* Set the bind variables for any placeholders in the SQL statement. */
81 set_bind_variables();
82
83 /* Open the cursor and execute the statement. */
84 EXEC SQL OPEN C USING DESCRIPTOR bind_dp;
85
86 /* Call the function that processes the select-list. */
87 if (cmd == CMD_SELECT) ret = do_select();
88
89 /* Tell user how many rows processed. */
90 if (ret == 0) {
91 printf("\n%d row%c processed.\n", sqlca.sqlerrd[2],
92 sqlca.sqlerrd[2] > 1 ? 's' : '\0');
93 }
94 EXEC SQL CLOSE C;
95 continue;
96 err_loop:
97 sql_error("main :");
98 }
99
100 /* free the memory allocated for the bind and select descriptors. */
101 for (i = 0; i < MAX_ITEMS; i++) {
102 if (bind_dp->I[i] != (short *) 0) free(bind_dp->I[i]);
103 if (select_dp->I[i] != (short *) 0) free(select_dp->I[i]);
104 if (bind_dp->V[i] != (char *) 0) free(bind_dp->V[i]);
105 if (select_dp->V[i] != (char *) 0) free(select_dp->V[i]);
106 }
107
108 /* Free space used by the descriptors themselves. */
109 sqlclu(bind_dp);
110 sqlclu(select_dp);
111
112 EXEC SQL WHENEVER SQLERROR CONTINUE;
113
114 EXEC SQL COMMIT WORK RELEASE;
115 exit(0);
116 }
117
118 /*-------------------------------------------------------------------
119 Function : connect to Oracle
120 -------------------------------------------------------------------*/
121 int connect() {
122 static char *suc_msg = "\nConnected to ORACLE as user %s.\n";
123
124 EXEC SQL WHENEVER SQLERROR GOTO con_err;
125
126 EXEC SQL CONNECT :username IDENTIFIED BY :password;
127 printf(suc_msg, username);fflush(stdout);
128 return 0;
129
130 con_err:
131 sql_error("connect : ");
132 return -1;
133 }
134
135 /*---------------------------------------------------------------------------
136 Function : Allocate the BIND and SELECT descriptors using SQLSQLDAAlloc().
137 Return : 0 = success
138 -1 = error
139 SQLSQLDAAlloc(SQL_SINGLE_RCTX,max_list,max_listname,max_ind_name);
140 ---------------------------------------------------------------------------*/
141 alloc_descriptors(int size, int max_vname,int max_iname) {
142 int i;
143
144 if ((bind_dp = SQLSQLDAAlloc(
145 SQL_SINGLE_RCTX,size,max_vname,max_iname)) == (SQLDA *) 0) {
146 fprintf(stderr, "Cannot allocate memory for bind descriptor.");
147 return -1;
148 }
149
150 if ((select_dp = SQLSQLDAAlloc(
151 SQL_SINGLE_RCTX,size, max_vname, max_iname)) == (SQLDA *) 0) {
152 fprintf(stderr, "Cannot allocate memory for select descriptor.");
153 return -1;
154 }
155
156 /* Allocate the pointers to the indicator variables, and the actual data. */
157 for (i = 0; i < MAX_ITEMS; i++) {
158 bind_dp ->I[i] = (short *) malloc(sizeof(short));
159 select_dp->I[i] = (short *) malloc(sizeof(short));
160 }
161 return 0;
162 }
163
164 /*-----------------------------------------------------------------------
165 Function : input SQL statement
166 Return : 0 : success
167 -1 : exit
168 -----------------------------------------------------------------------*/
169 int get_dyn_statement() {
170 char *cp, linebuf[MAX_LBUF];
171 int i,iter, plsql,cmd,len;
172
173 for (plsql = 0, iter = 1; ;) {
174 if (iter == 1) {
175 printf("\nSQL> ");fflush(stdout);
176 dyn_statement[0] = '\0';
177 }
178
179 fgets(linebuf, MAX_LBUF, stdin);
180 len = strlen(linebuf);
181 cp = strrchr(linebuf, '\n');
182 if (cp) {
183 if (cp != linebuf) *cp = ' ';
184 else continue;
185 }
186 for (i=0; i<len; i++) linebuf[i] = toupper(linebuf[i]);
187
188 cmd = chk_command(linebuf,SZ_STTSTR,stt_command,SZ_STTCMDS);
189 if ((cmd == 0)||(cmd == 1)) return -1;
190 else if (cmd == 2) { /* help */
191 help();
192 iter = 1;
193 continue;
194 }
195
196 if (strstr(linebuf, "BEGIN")) {
197 plsql = 1;
198 }
199
200 strcat(dyn_statement, linebuf);
201
202 if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||
203 (!plsql && (cp = strrchr(dyn_statement, ';')))) {
204 *cp = '\0';
205 break;
206 }
207 else {
208 iter++;
209 printf("%3d ", iter);
210 }
211 }
212 return 0;
213 }
214
215 /*---------------------------------------------------------------------
216 Function : set bind variables (SQLDA bind_dp)
217 SQL : select * from emp where deptno = :dd and job = :jj;
218 SQLDA :
219 struct SQLDA {
220 int N; \ プレースホルダ配列の最大値
221 char *V[]; @& バインド変数の値を格納するアドレスの配列
222 int L[]; @ V のバッファ長の配列
223 short T[]; @ V の型の配列 (SQLT_CHR,SQLT_NUM,SQLT_INT ..)
224 NOT NULL 制約がないと上位ビットが ON となっている
225 short I[]; @& 標識変数の値を格納するアドレスの配列
226 int F; # DESCRIBE でのプレースホルダ配列の数(実際の数)
227 char *S[]; # プレースホルダ名を格納するアドレスの配列
228 short M[]; S のバッファ長の配列
229 short C[]; # S に実際格納されたバイト数の配列
230 char X[]; 標識変数名を格納するアドレス配列
231 short Y[]; X のバッファ長の配列
232 short Z[]; X に実際格納されたバイト数の配列
233 };
234
235 \ : set user before EXEC SQL
236 # : set (EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp)
237 @ : set user
238 ---------------------------------------------------------------------*/
239 void set_bind_variables() {
240 int i, n;
241 char bind_var[64],*cp;
242
243 /* Describe any bind variables (input host variables) */
244 EXEC SQL WHENEVER SQLERROR GOTO err_set;
245
246 bind_dp->N = MAX_ITEMS; /* Initialize count of array elements. */
247 EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;
248
249 /* If F is negative, there were more bind variables
250 than originally allocated by sqlald(). */
251 if (bind_dp->F < 0) {
252 fprintf (stderr,"\nToo many bind variables (%d), maximum is %d\n.",
253 -bind_dp->F, MAX_ITEMS);
254 return;
255 }
256
257 /* Set the maximum number of array elements in the
258 descriptor to the number found. */
259 bind_dp->N = bind_dp->F;
260
261 /*
262 EXEC SQL DESCRIBE BIND 前に設定しておく部分
263 N プレースホルダ配列の最大値
264
265 EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp で設定される部分
266 F DESCRIBE でのプレースホルダ配列の数(実際の数)
267 S[i] SQL 文のプレースホルダ名を格納するアドレス
268 C[i] S[i] に実際格納されたバイト数の配列
269
270 EXEC SQL OPEN C USING DESCRIPTOR bind_dp 前に設定しておく部分
271 V[i] バインド変数の値を格納するアドレス
272 L[i] V[i] のバッファ長
273 T[i] V の型の配列 (SQLT_CHR,SQLT_NUM,SQLT_INT ..)
274 I[i] 標識変数の値を格納するアドレス
275 */
276 for (i = 0; i < bind_dp->F; i++) {
277 printf ("\nEnter value for bind variable %.*s: ",
278 (int)bind_dp->C[i], bind_dp->S[i]);
279 fgets(bind_var, sizeof bind_var, stdin);
280
281 /* Set it in the descriptor. (remove the new line character) */
282 bind_dp->L[i] = n = strlen(bind_var) - 1;
283
284 /* allocate the buffer for the value. */
285 bind_dp->V[i] = cp = (char *) malloc((n + 1));
286 if (! cp) die_err(ERR_ALLOC);
287 strcpy(bind_dp->V[i], bind_var); /* set value */
288
289 /* Set the indicator variable's value. */
290 if ((strncmp(bind_dp->V[i], "NULL", 4) == 0) ||
291 (strncmp(bind_dp->V[i], "null", 4) == 0))
292 *bind_dp->I[i] = -1;
293 else
294 *bind_dp->I[i] = 0;
295
296 bind_dp->T[i] = 1; /* Set the bind datatype to 1 for CHAR. */
297 }
298 return;
299 err_set:
300 sql_error("set_bind_variables : ");
301 return;
302 }
303
304 /*----------------------------------------------------------------
305 Function : process to SELECT_statement
306 1. get table_list [DESCRIBE]
307 2. print result_set
308 2.1 allocate memory
309 2.2 print column headings
310 2.3 make format of row datas.
311 2.4 print minus-bars of each column
312 2.5 print each row datas
313 Return : 0 = success
314 others = error
315 SQLDA:
316 struct SQLDA {
317 int N; \ 選択項目配列の最大値
318 char *V[]; @& 選択項目の値を格納するアドレスの配列
319 int L[]; @ V のバッファ長の配列
320 short T[]; @ V の型の配列 (SQLT_CHR,SQLT_NUM,SQLT_INT ..)
321 NOT NULL 制約がないと上位ビットが ON となっている
322 short I[]; @& 標識変数の値を格納するアドレスの配列
323 int F; # DESCRIBE での選択項目配列の数(実際の数)
324 char *S[]; # 選択項目名を格納するアドレスの配列
325 short M[]; S のバッファ長の配列
326 short C[]; # S に実際格納されたバイト数の配列
327 char X[]; 標識変数名を格納するアドレス配列
328 short Y[]; X のバッファ長の配列
329 short Z[]; X に実際格納されたバイト数の配列
330 };
331
332 \ : set user before EXEC SQL DESCRIBE
333 # : set (EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp)
334 @ : set user
335 & : set (EXEC SQL FETCH C USING DESCRIPTOR select_dp)
336 ----------------------------------------------------------------*/
337 int do_select() {
338 int i,j, null_ok, precision, scale, bsize,pre[MAX_ITEMS],len;
339 char fmt[MAX_ITEMS][MAX_FMT1];
340
341 /* 1. describe the select-list items. */
342 EXEC SQL WHENEVER SQLERROR GOTO err_sel;
343 select_dp->N = MAX_ITEMS;
344 EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;
345
346 /* F に実際の項目数が設定されます。
347 F が負の場合は選択項目数が MAX_ITEMS より多かった場合です */
348 if (select_dp->F < 0) {
349 fprintf (stderr,"\nToo many select-list items (%d), maximum is %d\n",
350 -(select_dp->F), MAX_ITEMS);
351 return ERR_OVR_MAX_ITEMS;
352 }
353 select_dp->N = select_dp->F;
354
355 /*
356 SQLNumberPrecV6() : 精度とスケールを取得
357 SQLColumnNullCheck() : T[i] の NULL/NOT NULL のビットを off
358
359 CHAR The length is defined at CREATE time.
360 NUMBER NUMBER(n,m) n:精度 m:スケール
361 スケールが 0 なら int そうでなかったら float
362 DATE 本来のバイト数は 7 なので多めにして 9
363 ROWID 18
364 LONG & LONG RAW 最大値として 240
365 */
366 /* 2. print result_set */
367 printf("\n");
368 for (i = 0; i < select_dp->F; i++) {
369 char title[MAX_VNAME],*cp;
370
371 fmt[i][0] = '\0';
372
373 /* T[i] の NULL/NOT NULL のビットを off にしてから使う */
374 SQLColumnNullCheck(SQL_SINGLE_RCTX,(unsigned short *)&(select_dp->T[i]),
375 (unsigned short *)&(select_dp->T[i]), &null_ok);
376 switch (select_dp->T[i]) {
377 case SQLT_NUM :
378 /* NUMBER datatype: SQLNumberPrecV6() で精度とスケールを取得 */
379 SQLNumberPrecV6 (SQL_SINGLE_RCTX,
380 (unsigned int *)&(select_dp->L[i]), &precision, &scale);
381 if (precision == 0) precision = DEF_PRECISION;
382 /* NUMBER datatype to FLOAT if scale > 0, INT otherwise. */
383 if (scale > 0) select_dp->L[i] = sizeof(float);
384 else select_dp->L[i] = sizeof(int);
385 pre[i] = precision;
386 break;
387
388 case SQLT_RID : /* ROWID datatype */
389 select_dp->L[i] = 18;
390 break;
391
392 case SQLT_DAT : /* DATE datatype */
393 select_dp->L[i] = 10; /* yyyy/mm/dd */
394 break;
395
396 case SQLT_BIN : /* RAW datatype */
397 case SQLT_CHR : /* CHAR datatype */
398 break;
399
400 case SQLT_LNG : /* LONG datatype */
401 case SQLT_LBI : /* LONG RAW datatype */
402 select_dp->L[i] = 240;
403 break;
404 }
405
406 /* 2.1 allocate memory */
407 if (select_dp->T[i] != SQLT_NUM) bsize = select_dp->L[i] + 1;
408 else bsize = select_dp->L[i];
409 select_dp->V[i] = cp = (char *) malloc(bsize);
410 if (! cp) die_err(ERR_ALLOC);
411
412 /* 2.2 print column headings */
413 strncpy(title, select_dp->S[i], select_dp->C[i]);
414 title[select_dp->C[i]] = '\0';
415 if (select_dp->T[i] == SQLT_NUM) len = pre[i];
416 else len = select_dp->L[i];
417 printf ("%-*.*s ", len,len, title);
418
419 /* LONG RAW と NUMBER 以外は char に設定 */
420 if (select_dp->T[i] != SQLT_LBI && select_dp->T[i] != SQLT_NUM)
421 select_dp->T[i] = SQLT_CHR;
422
423 /* NUMBER でスケールが正の場合は float,それ以外は int に設定 */
424 if (select_dp->T[i] == SQLT_NUM) {
425 if (scale > 0)
426 select_dp->T[i] = SQLT_FLT; /* float */
427 else
428 select_dp->T[i] = SQLT_INT; /* int */
429 }
430
431 /* 2.3 make format of row datas. */
432 switch (select_dp->T[i]) {
433 case SQLT_FLT : /* %X.Xf */
434 snprintf(fmt[i],MAX_FMT1,"%%%d.%df ",precision,scale);
435 break;
436 case SQLT_INT : /* %Xd */
437 snprintf(fmt[i],MAX_FMT1,"%%%dd ",precision);
438 break;
439 default: /* %-X.Xs */
440 snprintf(fmt[i],MAX_FMT1,"%%-%d.%ds ",
441 (int)select_dp->L[i],(int)select_dp->L[i]);
442 break;
443 }
444 }
445 printf ("\n");
446
447 /* 2.4 print minus-bars of each column */
448 for (i = 0; i < select_dp->F; i++) {
449 if ((select_dp->T[i] == SQLT_FLT) || (select_dp->T[i] == SQLT_INT))
450 len = pre[i];
451 else len = select_dp->L[i];
452 for (j=0; j<len; j++) printf ("%c",'-');
453 printf (" ");
454 }
455 printf ("\n");
456 fflush(stdout);
457
458 /* 2.5 print each row datas */
459 EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;
460
461 for (;;) {
462 EXEC SQL FETCH C USING DESCRIPTOR select_dp;
463
464 /* 標識変数の値が負の場合、値は予測不能 */
465 for (i = 0; i < select_dp->F; i++) {
466 if (*select_dp->I[i] < 0) {
467 if ((select_dp->T[i] == SQLT_FLT)||
468 (select_dp->T[i] == SQLT_INT)) len = pre[i];
469 else len = select_dp->L[i];
470 printf ("%-*c ",len, ' ');
471 }
472 else {
473 switch (select_dp->T[i]) {
474 case SQLT_FLT :
475 printf(fmt[i],*(float *)select_dp->V[i]);
476 break;
477 case SQLT_INT :
478 printf(fmt[i],*(int *)select_dp->V[i]);
479 break;
480 default:
481 printf(fmt[i],select_dp->V[i]);
482 break;
483 }
484 }
485 }
486 printf ("\n");
487 fflush(stdout);
488 }
489 end_select_loop:
490 return 0;
491 err_sel:
492 sql_error("do_select : ");
493 return -1;
494 }
495
496 /*----------------------------------------------------------------------
497 Function : check oracle command or not
498 Param : buf = SQL statement
499 len = length of candidate string
500 cand = candidate array
501 cand_no = no. of cand
502 Return : >=0 : oracle exec command
503 <0 : not
504 ----------------------------------------------------------------------*/
505 int chk_command(char *buf,int len,char *cand[],int cand_no) {
506 int i,ret=-1,buflen;
507 char cmdstr[MAX_LBUF];
508
509 buflen = strlen(buf);
510 if (buflen > len) {
511 strncpy(cmdstr,buf,len);
512 cmdstr[len] = '\0';
513 for (i=0; i<cand_no; i++) {
514 if (! strcmp(cmdstr,cand[i])) {
515 ret = i;
516 break;
517 }
518 }
519 }
520 return ret;
521 }
522
523 int get_sql_stmt() {
524 char sbuf[MAX_STMT];
525 size_t size=MAX_STMT,sqlcode;
526
527 sqlgls(sbuf,&size,&sqlcode);
528 sbuf[size] = '\0';
529 printf("SQL[%d]=%s\n",sqlcode,sbuf);
530 }
531
532 static char *help_msg[]={
533 "Enter a SQL statement or a PL/SQL block at the SQL> prompt.",
534 "Statements can be continued over several lines, except within string literals.",
535 "Terminate a SQL statement with a semicolon.",
536 "Terminate a PL/SQL block (which can contain embedded semicolons) with a slash (/).",
537 "Typing \"exit\" (no semicolon needed) exits the program.",
538 "You typed \"?\" or \"help\" to get this message.\n",
539 };
540 #define SZ_HELP_MSG (sizeof(help_msg)/sizeof(char *))
541
542 void help() {
543 int i;
544
545 for (i=0; i<SZ_HELP_MSG; i++) fprintf(stderr,"%s\n",help_msg[i]);
546 }
547
548 /*----------------------------------------------------------------------
549 Function : error handler of oracle
550 ----------------------------------------------------------------------*/
551 void sql_error(char *msg) {
552 int len=MAX_LBUF,size;
553 char msgbuf[MAX_LBUF];
554
555 fprintf(stderr,"\n\n",msg);
556 SQLErrorGetText(SQL_SINGLE_RCTX,msgbuf,&len,&size);
557 fprintf(stderr,"\n%s\n",msgbuf);
558 if (parse_flag)
559 fprintf(stderr,"Parse error at character offset %d in SQL statement.\n",
560 sqlca.sqlerrd[4]);
561
562 EXEC SQL WHENEVER SQLERROR CONTINUE;
563 EXEC SQL ROLLBACK WORK;
564 }
565
566
567 extern int optind;
568 extern char *optarg;
569 char *opt = "u:p:"; /* input options */
570 /*------------------------------------------------------------------
571 Function : analyze input options USERNAME/PASSWORD
572 if set nothing, set scott/tiger for default.
573 ------------------------------------------------------------------*/
574 int get_opt(int argc,char *argv[]) {
575 int c;
576
577 /* analize input options */
578 memset(username,0,MAX_USERNM);
579 memset(password,0,MAX_USERNM);
580 while((c=getopt(argc,argv,opt)) != EOF) {
581 switch(c) {
582 case 'u': /* username */
583 strcpy(username,optarg);
584 break;
585
586 case 'p': /* password */
587 strcpy(password,optarg);
588 break;
589 }
590 }
591 if (argc != 3) {
592 strcpy(username,DEF_USERNAME);
593 strcpy(password,DEF_PASSNAME);
594 }
595 }
596
597 int die_err(int no) {
598 fprintf(stderr,"Error code is %d\n",no);
599 exit(no);
600 }
[ 実行結果 ]
Connected to ORACLE as user scott.
SQL> select * from emp where deptno =:dd;
SQL[4]=SELECT * FROM EMP WHERE DEPTNO =:DD
Enter value for bind variable DD: 20
EMPN ENAME JOB MGR HIREDATE SAL COMM DE
---- ---------- --------- ---- ---------- ------- ------- --
7369 SMITH CLERK 7902 1980/12/17 800.00 20
7566 JONES MANAGER 7839 1981/04/02 2975.00 20
7788 SCOTT ANALYST 7566 1982/12/09 3000.00 20
7876 ADAMS CLERK 7788 1983/01/12 1100.00 20
4 rows processed.
SQL> exit