動的SQLを使用してデータベースからデータの取り出しを行う場合には、実行結果を受け取る相手指定を指定します。相手指定に実行結果を受け取るためには、選択リストの情報をその相手指定の属性と一致させることが必要です。そこで、データベースからSQLDA構造体に選択リストの情報を取り込んで、属性を変更します。SQLDA構造体の位置づけを以下に示します。
SQLDA構造体は、相手指定または動的パラメタ指定に関する情報を持ちます。
SQLDA構造体は、項目記述子域“SQLVAR”およびSQLVARの最大要素数“SQLN”と有効要素数“SQLD”で構成されています。
SQLDA構造体は以下の形式です。
#define SQL_DESC_ENT_NUM 1 typedef struct sqlextinf { int SQLPOSITION; /* CALL文のパラメタ位置 */ char SQLSYSTEMRSV[252]; /* 予約域 */ } sqlextinf; typedef struct sqlvar { char *SQLDATA; /* 変数データ域 */ short *SQLIND; /* 標識変数データ域 */ int SQLLEN; /* データ長 */ short SQLTYPE; /* データ型のコード */ short SQLSCALE; /* 位取り */ union{ short PRECISION; struct { char PRECISION; char DATETIME_INTERVAL_PRECISION; } INTERVAL; } SQLPRECISION; /* 精度 */ short SQLNULLABLE; /* NULL可能性 */ struct { short SQLNAMEL; /* 列名長 */ char SQLNAMEC[128]; /* 列名 */ } SQLNAME; sqlextinf *SQLEXTINF; /* 拡張情報 */ } sqlvar; typedef struct SQLDA { short SQLN; /* SQLVARの実配列数 */ short SQLD; /* SQLVARのデータの設定数 */ sqlvar SQLVAR[SQL_DESC_ENT_NUM]; } SQLDA;
SQLDA構造体の構成要素の各メンバの意味は以下の通りです。
SQLN
SQLNは、記述され得る動的パラメタ指定の最大数または選択リスト項目の最大数を示します。したがって、SQLNは配列SQLVARの最大要素数を決定します。
SQLD
SQLDは、配列SQLVARの有効要素数を決定します。
被準備文中に選択リスト項目がある場合、SQLDA構造体を指定した出力DESCRIBE文を実行すると、SQLDには選択リスト項目の個数が返却されます。
また、被準備文中に動的パラメタ指定が記述されている場合、SQLDA構造体を指定した入力DESCRIBE文を実行すると、SQLDには動的パラメタ指定の個数が返却されます。
SQLVAR
SQLVARは、動的パラメタ指定または選択リスト項目の各種情報を格納する配列です。
動的パラメタ指定に対してはSQL文が実行される前に、この配列の情報を設定する必要があります。n個の動的パラメタ指定があるような、SQL文を被準備文として実行する場合、SQLVAR[0]からSQLVAR[n-1]に格納されている情報でSQL文が実行されます。
選択リストに対してはSQL文が実行される前に、この配列の情報を設定する必要があります。n個の選択リストがあるようなSQL文を被準備文として実行する場合、SQL文実行後にSQLVAR[0]からSQLVAR[n-1]に指定したデータバッファにデータが格納されます。
項目記述子域“SQLVAR”は、以下の内容で構成されています。
メンバ名 | 意味 | データ型 |
---|---|---|
SQLDATA | ホスト変数のアドレス | - |
SQLIND | 標識変数のアドレス | - |
SQLLEN | 長さ(バイト数) | 2進の精度を持つ真数(注1) |
SQLTYPE | データ型のコード | 2進の精度を持つ真数(注2) |
SQLSCALE | 位取り、または 日時型、時間隔型のコード | 2進の精度を持つ真数(注2) |
SQLPRECISION.PRECISION | 精度 | 2進の精度を持つ真数(注2) |
SQLPRECISION.INTERVAL. DATETIME_INTERVAL_PRECISION | 時間隔先行フィールド精度 | 2進の精度を持つ真数(注3) |
SQLNULLABLE | NULL値を許すかどうか | 2進の精度を持つ真数(注2) |
SQLNAME | 列指定の列名 | 長さ108バイト以下の文字列型 |
SQLEXTINF | 拡張情報 | - |
注1) C言語のint型のデータ型です。
注2) C言語のshort型のデータ型です。
注3) C言語のchar型のデータ型です。
SQLDA構造体のSQLTYPEの値(データ型のコード)とSQLのデータ型の対応は以下のとおりです。
コード | データ型 |
---|---|
1 | CHARACTER |
2 | NUMERIC |
3 | DECIMAL |
4 | INTEGER |
5 | SMALLINT |
7 | REAL |
8 | DOUBLE PRECISION |
9 | DATE, TIMEまたはTIMESTAMP |
10 | INTERVAL |
11 | NATIONAL CHARACTER |
12 | CHARACTER VARYING |
13 | NATIONAL CHARACTER VARYING |
30 | BLOB |
31 | (注) |
50 | ROW_ID |
注) SQL埋込みCプログラムにおいて、8バイトの整数型(long long型など)と対応します。DESCRIBE文では、コードに31は返却されません。
SQLTYPEの値が日時型を示す9の場合、SQLSCALEには、以下のDATETIME_INTERVAL_CODEを格納します。
コード | データ型 |
---|---|
1 | DATE |
2 | TIME |
3 | TIMESTAMP |
SQLTYPEの値が時間隔型を示す10の場合、SQLSCALEには、以下のDATETIME_INTERVAL_CODEを格納します。
コード | 時間隔修飾子 |
---|---|
1 | YEAR |
2 | MONTH |
3 | DAY |
4 | HOUR |
5 | MINUTE |
6 | SECOND |
7 | YEAR TO MONTH |
8 | DAY TO HOUR |
9 | DAY TO MINUTE |
10 | DAY TO SECOND |
11 | HOUR TO MINUTE |
12 | HOUR TO SECOND |
13 | MINUTE TO SECOND |
実行結果の取り出し手順
USING記述子を使用して、実行結果を取り出す方法の順序を以下に示します。ここでは処理の流れを説明します。なお、各SQL文についてはこれ以降で説明します。
図4.1 被準備文の実行結果の取り出し手順
SQL文を動的に変更してデータ操作を行う場合、そのSQL文を実行するための準備処理を行います。
動的SQLの実行の準備を行うには、PREPARE文を使用します。プログラムの実行時にSQL文を作成して実行させる場合に、そのSQL文を“SQL文変数”と呼ばれるホスト変数に文字列として格納します。そして、PREPARE文で実行の準備をします。このとき、SQL文変数に格納されたSQL文は文を識別するSQL文識別子と対応づけられます。
SQL文変数の内容が準備可能動的UPDATE文:位置づけまたは準備可能動的DELETE文:位置づけの場合は、事前に対応するカーソルをオープンしておくことが必要です。また、指定したカーソルは動的カーソルであることが必要です。
なお、すでにPREPARE文で定義されたSQL文識別子に対して、再度PREPARE文を実行する場合は、先の被準備文が解放され、新たに被準備文が準備されます。先の被準備文が動的SELECT文の場合、SQL文識別子に対応するカーソルを参照している準備可能動的UPDATE文:位置づけまたは準備可能動的DELETE文:位置づけの被準備文も同時に解放されます。ただし、被準備文が動的SELECT文の場合、SQL文識別子に対応するカーソルは閉じられた状態であることが必要です。
PREPARE文の指定例を以下に示します。実行準備を行うSQL文は、SQL文変数strに格納されているとします。このSQL文のSQL文識別子をSTMと定義します。
PREPARE STM FROM :str (1) (2)
(1) SQL文識別子
(2) SQL文変数
PREPARE文によって準備された被準備文を解放するには、DEALLOCATE PREPARE文を使用します。
被準備文が動的SELECT文の場合、SQL文識別子に対応するカーソルを参照している準備可能動的UPDATE文:位置づけまたは準備可能動的DELETE文:位置づけの被準備文も同時に解放されます。また、その際のカーソルは、閉じられた状態であることが必要です。
DEALLOCATE PREPARE文の指定例を以下に示します。解放する被準備文は、PREPARE文によってSQL文識別子をSTMと定義された被準備文とします。
DEALLOCATE PREPARE STM (1)
(1) SQL文識別子
“図4.1 被準備文の実行結果の取り出し手順”で示したように、データベースからSQLDA構造体に選択リストの情報を取り込むには、DESCRIBE文を使用します。DESCRIBE文とその機能について以下で説明します。
DESCRIBE文では、PREPARE文で準備された被準備文の選択リストの情報をSQLDA構造体に取り込みます。このSQL文を“出力DESCRIBE文”といいます。
SQL文識別子STM2に対応する被準備文の選択リストの情報を、SQLDA構造体に取り込む出力DESCRIBE文の例を以下に示します。
DESCRIBE OUTPUT STM2 USING SQL DESCRIPTOR :OUTSQLDA (1) (2)
(1) SQL文識別子
(2) SQLDA構造体
SQL文識別子STM2に対する被準備文が以下の場合、SQLDA構造体の内容は次のようになります。
SELECT 製品名, 在庫数量 FROM 在庫管理.在庫表 WHERE 製品番号 = 110 (1)
(1) 選択リスト
SQLD : 2
選択リストの個数2が設定されます。
SQLDA構造体の項目記述子域のメンバの情報
製品名、在庫数量の属性がそれぞれ設定されます。
製品名 : NATIONAL CHARACTER(10)(NOT NULL制約あり)
在庫数量: INTEGER(NOT NULL制約なし)
識別子 | 製品名 | 在庫数量 |
---|---|---|
SQLTYPE | 11 | 4 |
SQLLEN | 20 | 4 |
SQLPRECISION.PRECISION | 0 | 31 |
SQLSCALE | 0 | 0 |
SQLNULLABLE | 0 | 1 |
SQLIND | 値は設定されない | 値は設定されない |
SQLDATA | 値は設定されない | 値は設定されない |
SQLNAME | 製品名 (注) | 在庫数量 (注) |
SQLPRECISION.INTERVAL | 0 | 0 |
注) 長さはSQLNAMELに格納されます。
データの取り出しを連続的に行う場合には、動的SELECT文を準備して実行します。SQLDA構造体を使用して、動的SELECT文を実行するアプリケーションの例を以下に示します。
端末から入力した動的SELECT文の例です。
#include <stdio.h> #include <string.h> #include "qdbnosis.h" EXEC SQL BEGIN DECLARE SECTION; char SQLSTATE[6]; char SQLMSG[256]; VARCHAR str[1024]; /* SQL文変数 */ …(1) SQLDA *sqlda_out; /* SQLDA構造体 */ EXEC SQL END DECLARE SECTION; void main() { short vartype; /* データ型 */ short varleng; /* 文字数 */ short varolen; /* バイト数 */ short varprec; /* 精度 */ short varscal; /* 位取り SQL記述子域 */ char varname[129]; /* 列の名前 */ short vardcod; /* 日時,時間隔 */ short vardpre; /* 時間隔精度 */ short varocount; /* 選択リストの数 */ short i; /* SQL記述子域のカウンタ */ char ocdata[100][15]; /* CHARACTER型 */ char oncdata[100][21]; /* NCHAR型 */ long oidata[100]; /* 真数型 相手指定 */ double ofdata[100]; /* 概数型 */ char oddata[100][22]; /* 日時,時間隔 */ short inddata[100]; /* 標識変数 */ size_t size; /* SQLDAのサイズ */ memset(SQLSTATE, 0x00, 6); …(2) memset(SQLMSG, 0x00, 256); …(2) memset(&str, 0x00, sizeof(str)); …(2) memset(varname, 0x00, sizeof(varname)); …(2) memset(ocdata, 0x00, sizeof(ocdata) ); …(2) memset(oncdata, 0x00, sizeof(oncdata) ); …(2) memset(oddata, 0x00, sizeof(oddata)); …(2) size = sizeof(SQLDA) + sizeof(sqlvar) * (100 - 1); …(3) sqlda_out = (SQLDA *)malloc(size); memset( sqlda_out, 0x00, size ); sqlda_out->SQLN = 100; …(4) EXEC SQL CONNECT TO DEFAULT; …(5) EXEC SQL WHENEVER SQLERROR GOTO :ERR; …(6) printf( "実行する動的SELECT文を指定してください \n" ); gets( str.sqlvar ); …(7) str.sqllen = strlen( str.sqlvar ); EXEC SQL DECLARE CUR1 CURSOR FOR STMID; …(8) EXEC SQL PREPARE STMID FROM :str; …(9) EXEC SQL DESCRIBE OUTPUT STMID USING SQL DESCRIPTOR :sqlda_out; …(10) /**************/ /* 選択リスト */ /**************/ varocount = sqlda_out->SQLD; …(11) for( i = 0; i < varocount; i++ ) { vartype = sqlda_out->SQLVAR[i].SQLTYPE; …(12) varleng = sqlda_out->SQLVAR[i].SQLLEN; …(12) varprec = sqlda_out->SQLVAR[i].SQLPRECISION.PRECISION; …(12) varscal = sqlda_out->SQLVAR[i].SQLSCALE; …(12) vardcod = sqlda_out->SQLVAR[i].SQLSCALE; …(12) vardpre = sqlda_out->SQLVAR[i].SQLPRECISION.INTERVAL. …(12) DATETIME_INTERVAL_PRECISION; /* CHAR型かVCHAR型 */ …(13) if( vartype == 1 || vartype == 12 ) { …(13) sqlda_out->SQLVAR[i].SQLTYPE = 1; …(13) sqlda_out->SQLVAR[i].SQLLEN = 14; …(13) sqlda_out->SQLVAR[i].SQLDATA = (char *)&ocdata[i][0]; …(13) } …(13) /* NCHAR型かNVCHAR型 */ …(13) else if( vartype == 11 || vartype == 13 ) { …(13) sqlda_out->SQLVAR[i].SQLTYPE = 11; …(13) sqlda_out->SQLVAR[i].SQLLEN = 20; …(13) sqlda_out->SQLVAR[i].SQLDATA = (char *)&oncdata[i][0]; …(13) } …(13) /* INTEGER型かSMALLINT型 */ …(13) else if( vartype == 4 || vartype == 5 ) { …(13) sqlda_out->SQLVAR[i].SQLTYPE = 4; …(13) sqlda_out->SQLVAR[i].SQLLEN = 4; …(13) sqlda_out->SQLVAR[i].SQLPRECISION.PRECISION = 31; …(13) sqlda_out->SQLVAR[i].SQLSCALE = 0; …(13) sqlda_out->SQLVAR[i].SQLDATA = (char *)&oidata[i]; …(13) } …(13) /* NUMERIC型かDECIMAL型かREAL型かDOUBLE PRECISION型 */ …(13) else if( vartype == 2 || vartype == 3 || …(13) vartype == 7 || vartype == 8 ) { …(13) sqlda_out->SQLVAR[i].SQLTYPE = 8; …(13) sqlda_out->SQLVAR[i].SQLLEN = 8; …(13) sqlda_out->SQLVAR[i].SQLPRECISION.PRECISION = 52; …(13) sqlda_out->SQLVAR[i].SQLSCALE = 0; …(13) sqlda_out->SQLVAR[i].SQLDATA = (char *)&ofdata[i]; …(13) } …(13) /* 日時型かINTERVAL型 */ …(13) else if( vartype == 9 || vartype == 10 ) { …(13) sqlda_out->SQLVAR[i].SQLTYPE = 12; …(13) sqlda_out->SQLVAR[i].SQLLEN = 21; …(13) sqlda_out->SQLVAR[i].SQLDATA = (char *)&oddata[i][0]; …(13) } …(13) } EXEC SQL OPEN CUR1; …(14) EXEC SQL WHENEVER NOT FOUND GOTO :NOTFND; …(15) for(;;) { EXEC SQL FETCH CUR1 INTO SQL DESCRIPTOR :sqlda_out; …(16) /************/ /* 実行結果 */ /************/ for( i = 0; i < varocount; i++ ) { vartype = sqlda_out->SQLVAR[i].SQLTYPE; memcpy( varname, sqlda_out->SQLVAR[i].SQLNAME.SQLNAMEC, sqlda_out->SQLVAR[i].SQLNAME.SQLNAMEL ); varname[sqlda_out->SQLVAR[i].SQLNAME.SQLNAMEL] = 0x00; /* CHAR型 */ if( vartype == 1 ) { printf( "%s -> %s ", varname, &ocdata[i][0] ); } /* NCHAR型 */ else if( vartype == 11 ) { printf( "%s -> %s ", varname, &oncdata[i][0] ); } /* 真数型 */ else if( vartype == 4 ) { printf( "%s -> %d ", varname, oidata[i] ); } /* 概数型 */ else if( vartype == 8 ) { printf( "%s -> %lf ", varname, ofdata[i] ); } /* 日時型かINTERVAL型 */ else if( vartype == 12 ) { printf( "%s -> %s ", varname, &oddata[i][2] ); } } printf( "\n" ); } NOTFND: EXEC SQL CLOSE CUR1; …(17) EXEC SQL DEALLOCATE PREPARE STMID; …(18) EXEC SQL COMMIT WORK; …(19) EXEC SQL DISCONNECT DEFAULT; …(20) free( sqlda_out ); return; ERR: EXEC SQL WHENEVER SQLERROR CONTINUE; printf( "SQLERROR SQLSTATE = %s SQLMSG = %s\n", SQLSTATE, SQLMSG ); EXEC SQL ROLLBACK WORK; EXEC SQL DISCONNECT DEFAULT; free( sqlda_out ); return; }
(1) SQL文変数はSQL文を格納する領域です。可変長文字列は、コンパイル時に以下の構造体に展開されます。
struct { short sqllen; /* 長さ領域の変数 */ char sqlvar[1024]; /* 文字列領域の変数 */ }str;
(2) 変数を初期化します。
(3) SQLDA構造体の領域を獲得します。
(4) 配列SQLVARの配列数(SQLNの値)を指定します。
(5) コネクションを接続します。
(6) SQL文の実行でデータなし以外のエラーが生じた場合にERRへ進み、エラー情報を出力しプログラムは終了します。
(7) 端末から入力された動的SELECT文を読み込み、strに格納します。
(8) カーソルCUR1を宣言します。カーソル指定としてSQL文識別子STMIDを指定します。
(9) strのSQL文を実行できるようにするための準備処理を行います。
(10) 入力した動的SELECT文の選択リストの情報をSQLDA構造体に取り込みます。
(11) SQLDA構造体に設定されている選択リストの個数(SQLDの値)を取得します。
(12) SQLDA構造体に設定されているそれぞれの選択リストの情報(SQLTYPE、SQLLEN、SQLPRECISION、SQLSCALEの値)を取得します。
(13) SQLDA構造体に実行結果を取り出す相手指定のデータ型や変数のアドレスを設定します。
CHARACTER(n)、CHARACTER VARYING(n)をCHARACTER(14)に変更します。(nは文字列の長さ)
NATIONAL CHARACTER(n)、NATIONAL CHARACTER VARYING(n)をNATIONAL CHARACTER(10)に変更します。(nは文字列の長さ)
INTEGER、SMALLINTをINTEGERに変更します。
NUMERIC(p,q)、DECIMAL(p,q)、REAL、DOUBLE PRECISIONをDOUBLE PRECISIONに変更します。(pは精度、qは位取り)
DATE、TIME、TIMESTAMPおよびINTERVALをCHARACTER VARYING(19)に変更します。(nは文字列の長さ)
(14) カーソルCUR1をオープンします。
(15) 検索の結果データが見つからない場合にNOTFNDに進み、プログラムは終了します。
(16) カーソルCUR1の位置づけおよびデータの読込みを行います。このとき、SQLDA構造体のSQLDATAに設定したアドレスに実行結果の値が設定されます。
(17) カーソルCUR1をクローズします。
(18) SQL文識別子STMIDに対応する被準備文を解放します。
(19) 現行のトランザクションを終了します。
(20) コネクションを切断します。
1行のデータの取り出しを行う場合には、動的単一行SELECT文を準備して実行します。動的単一行SELECT文を実行するには、USING句を指定したEXECUTE文を使用します。SQLDA構造体を使用して、動的単一行SELECT文の指定例を以下に示します。なお、SQLDA構造体を操作するSQL文の詳細については、“図4.1 被準備文の実行結果の取り出し手順”の例を参照してください。
端末から入力した動的単一行SELECT文の例です。
: [端末から入力した動的単一行SELECT文をstrに格納] : EXEC SQL PREPARE STMID FROM :str; size = sizeof(SQLDA) + sizeof(sqlvar) * (100 - 1); sqlda_out = (SQLDA *)malloc(size); EXEC SQL DESCRIBE OUTPUT STMID USING SQL DESCRIPTOR :sqlda_out; ocount = sqlda_out->SQLD; for( i = 0; i < ocount; i++) { vartype = sqlda_out->SQLVAR[i].SQLTYPE; varleng = sqlda_out->SQLVAR[i].SQLLEN; : [選択リストの情報をSQLDA構造体から取得] : sqlda_out->SQLVAR[i].SQLTYPE = 1; sqlda_out->SQLVAR[i].SQLLEN = 14; : [SQLDA構造体の選択リストの情報を変更] : } EXEC SQL EXECUTE STMID INTO SQL DESCRIPTOR :sqlda_out; : [実行結果の値の取得] : free( sqlda_out ); EXEC SQL DEALLOCATE PREPARE STMID; :