標識変数とは
プリコンパイラ製品独特の概念として標識変数(indicator variable)というものがあります。 標識変数はホスト変数(ORACLEとクライアントプログラム間で受け渡し可能な変数)のステータスを表示、 設定するものでC言語等では変数の値としてNULLを表すことができないためNULLを扱うプログラムではホスト変数に標識変数を紐づける必要があります。標識変数の使い方
以下のようにホスト変数に続けてINDICATOR句とともに付与することで使用可能です。 (フェッチした段階で標識変数に値が自動的にセットされる)EXEC SQL FETCH emp_cursor INTO :empno , :ename INDICATOR :ind_ename, :comm INDICATOR :ind_comm;
以下のようにINDICATOR句を省略してホスト変数に続けて記述することも可能です。
EXEC SQL FETCH emp_cursor INTO :empno , :ename:ind_ename, :comm:ind_comm;
標識変数の値の意味
0・・・正常-1・・・NULL
-2・・・LONG型で切り捨てられた(文字数不明)
>0・・・切り捨てられた(値は文字数)
サンプルプログラム
以下はscottユーザでselect empno,ename,comm を実行するプログラムです。#include <stdio.h> #include <stdlib.h> #include <string.h> //接続定義 #define ORAUSER "scott" #define ORAPASSWD "tiger" #define ORASYSID_T "ORCL102" EXEC SQL INCLUDE sqlca; EXEC SQL BEGIN DECLARE SECTION; VARCHAR h_user[64]; VARCHAR h_pswd[64]; VARCHAR h_sid[64]; long empno; short ind_empno; VARCHAR ename[10+1]; short ind_ename[10+1]; float comm; short ind_comm; EXEC SQL END DECLARE SECTION; //oracleエラー時にコールされる関数 void sql_error(char* msg) { char err_msg[128]; size_t buf_len, msg_len; EXEC SQL WHENEVER SQLERROR CONTINUE; printf("\n%s\n", msg); buf_len = sizeof (err_msg); sqlglm(err_msg, &buf_len, &msg_len); printf("%.*s\n", msg_len, err_msg); EXEC SQL ROLLBACK RELEASE; exit(EXIT_FAILURE); } //メイン関数 int main() { //ユーザ名、パスワードセット strcpy((char *)h_user.arr,ORAUSER); h_user.len = strlen((char *)h_user.arr); strcpy((char *)h_pswd.arr,ORAPASSWD); h_pswd.len = strlen((char *)h_pswd.arr); strcpy((char *)h_sid.arr,ORASYSID_T); h_sid.len = strlen((char *)h_sid.arr); EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n"); //接続 EXEC SQL CONNECT :h_user IDENTIFIED BY :h_pswd; //select EXEC SQL DECLARE emp_cursor CURSOR FOR SELECT empno,ename,comm FROM scott.emp; EXEC SQL OPEN emp_cursor; EXEC SQL WHENEVER SQLERROR CONTINUE; printf("empno,ename,comm\n"); for (;;){ EXEC SQL FETCH emp_cursor INTO :empno , :ename INDICATOR :ind_ename, :comm:ind_comm; if (sqlca.sqlcode == 0){ if (ind_comm == -1) { printf("%d,%.*s,*null*\n", empno,ename.len,ename.arr,comm); }else{ printf("%d,%.*s,%8.2f\n", empno,ename.len,ename.arr,comm); } }else if (sqlca.sqlcode == 1403){ printf("end of data\n"); break; }else{ sql_error("error"); } } EXEC SQL COMMIT WORK RELEASE; printf("END\n"); return 0; }
コンパイル+実行
[ora102@linux1 20131103]$ make -f $ORACLE_HOME/precomp/demo/proc/demo_proc.mk OBJS=select.o EXE=select build make -f /app/oracle/product/10.2.0/db_1/precomp/demo/proc/demo_proc.mk PROCFLAGS="" PCCSRC=select I_SYM=include= pc1 make[1]: ディレクトリ `/home/ora102/kensho/20131103' に入ります proc iname=select include=. include=/app/oracle/product/10.2.0/db_1/precomp/public include=/app/oracle/product/10.2.0/db_1/rdbms/public include=/app/oracle/product/10.2.0/db_1/rdbms/demo include=/app/oracle/product/10.2.0/db_1/plsql/public include=/app/oracle/product/10.2.0/db_1/network/public Pro*C/C++: Release 10.2.0.4.0 - Production on 月 11月 4 02:52:01 2013 Copyright (c) 1982, 2007, Oracle. All rights reserved. システムのデフォルト・オプション値: /app/oracle/product/10.2.0/db_1/precomp/admin/pcscfg.cfg make[1]: ディレクトリ `/home/ora102/kensho/20131103' から出ます /usr/bin/gcc -O2 -fPIC -DPRECOMP -I. -I/app/oracle/product/10.2.0/db_1/precomp/public -I/app/oracle/product/10.2.0/db_1/rdbms/public -I/app/oracle/product/10.2.0/db_1/rdbms/demo -I/app/oracle/product/10.2.0/db_1/plsql/public -I/app/oracle/product/10.2.0/db_1/network/public -DLINUX -D_GNU_SOURCE -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -DSLTS_ENABLE -DSLMXMX_ENABLE -D_REENTRANT -DNS_THREADS -c select.c /usr/bin/gcc -o select select.o -L/app/oracle/product/10.2.0/db_1/lib/ -lclntsh `cat /app/oracle/product/10.2.0/db_1/lib/ldflags` `cat /app/oracle/product/10.2.0/db_1/lib/sysliblist` -ldl -lm [ora102@linux1 20131103]$ ./select empno,ename,comm 7369,SMITH,*null* 7499,ALLEN, 300.00 7521,WARD, 500.00 7566,JONES,*null* 7654,MARTIN, 1400.00 7698,BLAKE,*null* 7782,CLARK,*null* 7788,SCOTT,*null* 7839,KING,*null* 7844,TURNER, 0.00 7876,ADAMS,*null* 7900,JAMES,*null* 7902,FORD, 100.10 7934,MILLER,*null* end of data END
標識変数を使わないとどうなるか
デフォルト設定では標識変数を使わないでNULL値をfetchした場合「ORA-01405: フェッチした列の値がNULLです」のエラーとなりますが、 MODE=ORACLE DBMS=V8 UNSAFE_NULL=YESのオプションでプリコンパイルするとORA-01405の発生を抑止することができます。 この場合でエラーは発生しませんがNULLデータをフェッチした場合ホスト変数に格納されるデータは不正な値となります。 本検証環境で実際に確認した限り初期値または前回の値のままで表示されました。[ora102@linux1 20131103]$ ./select empno,ename,comm 7369,SMITH, 0.00 7499,ALLEN, 300.00 7521,WARD, 500.00 7566,JONES, 500.00 7654,MARTIN, 1400.00 7698,BLAKE, 1400.00 7782,CLARK, 1400.00 7788,SCOTT, 1400.00 7839,KING, 1400.00 7844,TURNER, 0.00 7876,ADAMS, 0.00 7900,JAMES, 0.00 7902,FORD, 100.10 7934,MILLER, 100.10 end of data END