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