16 PSBTREE: 拡張可能索引付けの例
これは、ODCIIndex
インタフェース・ルーチンのC言語実装を含む拡張可能索引付けの例です。
16.2 索引タイプの設計
ここで実装するPSBtree
という索引タイプの動作は、Bツリー索引と同様です。eq
(等しい)、lt
(より小さい)、gt
(より大きい)という、3つのユーザー定義演算子をサポートします。これらの演算子はVARCHAR2
データ型のオペランドを操作します。
索引データは、<key, rid>
という形式のレコードで構成されます。ここでkey
は索引付けされた列の値、rid
は対応する行のROWIDです。索引タイプの実装を簡略化するために、索引データはシステム・パーティション表に格納されます。
索引がシステム管理のローカルドメイン索引の場合は、パーティションごとに1つシステム・パーティション表が作成され、そのパーティションの索引データが格納されます。そのため、索引操作ルーチンは、単にPSBtree
の操作を索引データを格納する表のパーティションの操作に変換します。
ユーザーがPSBtree
索引(ローカル索引)を作成すると、索引付けされた列とrowid
列で構成されるn
個の表パーティションが作成されます(n
は実表内のパーティション数)。実表にデータが挿入されると、影響を受ける索引表パーティションに適切なデータが挿入されます。削除および更新も同様に処理されます。PSBtree
に対してユーザー定義演算子(gt
、lt
、eq
のいずれか)に基づいて問合せが実行されると、該当する問合せが索引表パーティションに対して発行され、条件を満たすすべての行が取得されます。適切なパーティション・プルーニングが行われ、関連する、つまり「興味のある」パーティションに対応する索引表パーティションのみがアクセスされます。
16.3 演算子の実装
PSBtree
索引タイプは、3つの演算子(eq
、gt
およびlt
)をサポートしています。各演算子には対応するファンクション実装があります。
16.3.1 ファンクション実装
比較演算子のファンクション実装について考えます。「EQUALS演算子の実装」の項ではeq
(等しい)演算子、「LESS THAN演算子の実装」の項ではlt
(未満)演算子、「GREATER THAN演算子の実装」の項ではgt
(より大きい)演算子を、それぞれ実装する方法について説明します。
16.3.1.1 EQUALS演算子の実装
eq
のファンクション実装は、ファンクション(bt_eq
)により提供されます。このファンクションは、2つのVARCHAR2
パラメータを取り、両者が等しい場合は1
、それ以外の場合は0
を戻します。
CREATE FUNCTION bt_eq(a VARCHAR2, b VARCHAR2) RETURN NUMBER AS BEGIN IF a = b then RETURN 1; ELSE RETURN 0; END IF; END;
16.3.1.2 LESS THAN演算子の実装
lt
のファンクション実装は、ファンクション(bt_lt
)により提供されます。このファンクションは、2つのVARCHAR2
パラメータを取り、最初のパラメータが2番目のパラメータ未満である場合は1
、それ以外の場合は0
を戻します。
CREATE FUNCTION bt_lt(a VARCHAR2, b VARCHAR2) RETURN NUMBER AS BEGIN IF a < b then RETURN 1; ELSE RETURN 0; END IF; END;
16.3.2 演算子
演算子を作成するには、そのシグネチャ、戻り型およびファンクション実装を指定する必要があります。例16-1ではeq
(等しい)演算子、例16-2ではlt
(より小さい)演算子、例16-3ではgt
(より大きい)演算子の、それぞれの作成方法を説明します。
例16-1 EQUALS演算子の作成
CREATE OPERATOR eq BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER USING bt_eq;
例16-2 LESS THAN演算子の作成
CREATE OPERATOR lt BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER USING bt_lt;
例16-3 GREATER THAN演算子の作成
CREATE OPERATOR gt BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER USING bt_gt;
16.4 ODCIIndexインタフェースの実装
PSBTREE
を実装するには、ODCIIndex
XXX
()
ルーチンを実装する必要があります。索引ルーチンは、Oracleでサポートされている任意の言語で実装できます。ここでは、Cプログラミング言語でODCIGetInterfaces()ルーチンを実装します。これらを実行するには、コンパイル済Cコード用にextdemo6l
というライブラリ・オブジェクトを作成するなど、高度な設定が必要です。
16.4.1 PSBTREEの実装タイプの定義
例16-4に示されるように、ODCIIndex
インタフェース・ルーチンを実装する実装タイプを定義します。
例16-4 PSBTREE索引タイプの作成
CREATE TYPE psbtree_im AUTHID CURRENT_USER AS OBJECT ( scanctx RAW(4), STATIC FUNCTION ODCIGetInterfaces(ifclist OUT SYS.ODCIObjectList) RETURN NUMBER, STATIC FUNCTION ODCIIndexCreate (ia SYS.ODCIIndexInfo, parms VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexAlter (ia sys.ODCIIndexInfo, parms IN OUT VARCHAR2, altopt number, env sys.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexDrop(ia SYS.ODCIIndexInfo, env SYS.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexExchangePartition(ia SYS.ODCIIndexInfo, ia1 SYS.ODCIIndexInfo, env SYS.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexUpdPartMetadata(ia sys.ODCIIndexInfo, palist sys.ODCIPartInfoList, env sys.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexInsert(ia SYS.ODCIIndexInfo, rid VARCHAR2, newval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexDelete(ia SYS.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexUpdate(ia SYS.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, newval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER, STATIC FUNCTION ODCIIndexStart(sctx IN OUT psbtree_im, ia SYS.ODCIIndexInfo, op SYS.ODCIPredInfo, qi sys.ODCIQueryInfo, strt number, stop number, cmpval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER, MEMBER FUNCTION ODCIIndexFetch(nrows NUMBER, rids OUT SYS.ODCIridlist, env SYS.ODCIEnv) RETURN NUMBER, MEMBER FUNCTION ODCIIndexClose(env SYS.ODCIEnv) RETURN NUMBER ); / SHOW ERRORS
16.4.2 実装タイプ本体の作成
例16-5に示されるように、実装タイプ本体を定義します。
例16-5 PBSTREEの実装本体の作成
CREATE OR REPLACE TYPE BODY psbtree_im IS
16.4.3 実装本体でのPL/SQLルーチンの定義
PL/SQLでの索引定義ルーチンの実装方法を考えます。
16.4.3.1 PBSTREEのODCIGetInterfaces()のPL/SQLでの実装
ODCIGetInterfaces()ルーチンはOUT
パラメータを介して予期されるインタフェース名を戻します。
STATIC FUNCTION ODCIGetInterfaces( ifclist OUT sys.ODCIObjectList) RETURN NUMBER IS BEGIN ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCIINDEX2')); RETURN ODCIConst.Success; END ODCIGetInterfaces;
16.4.3.2 PBSTREEのODCIIndexCreate()のPL/SQLでの実装
ODCIIndexCreate()ルーチンは、2つの列を持つシステム・パーティション表を作成します。第1列には、索引付けされたVARCHAR2
列の値が格納されます。起動コンテキストは、渡された情報を使用して判別されます。DBMSSQLを使用して、動的に構成されたSQL文が実行されます。
STATIC FUNCTION ODCIIndexCreate ( ias sys.ODCIIndexInfo, parms VARCHAR2, env sys.ODCIEnv) RETURN NUMBER IS i INTEGER; stmt VARCHAR2(2000); cursor cur1(ianame VARCHAR2) IS SELECT partition_name, parameters FROM user_ind_partitions WHERE index_name = ianame ORDER BY partition_position; cursor cur2(ianame VARCHAR2) IS SELECT subpartition_name, parameters FROM user_ind_subpartitions WHERE index_name = ianame ORDER BY partition_position, subpartition_position; BEGIN stmt := ''; IF (env.CallProperty is null) THEN stmt := 'create table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree(f1 VARCHAR2(1000), f2 rowid)'; ELSIF (env.CallProperty = sys.ODCIConst.FirstCall) THEN stmt := ''; i := 1; IF (bitand(ia.IndexInfoFlags, ODCIConst.CompPartn) = 0) THEN FOR c1 in cur1(ia.IndexName) LOOP IF (i > 1) THEN stmt := stmt || ','; END IF; stmt := stmt || 'partition ' || c2.partition_name; i := i + 1; END LOOP; ELSE FOR c1 in cur1(ia.IndexName) LOOP IF (i > 1) THEN stmt := stmt || ','; END IF; stmt := stmt || 'partition ' || c2.subpartition_name; i := i + 1; END LOOP; END IF; stmt := 'create table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree (f1 VARCHAR2(1000), f2 rowid) partition by system ' || '(' || stmt || ')'; ELSIF (env.CallProperty = sys.ODCIConst.FinalCall) THEN stmt := 'create index ' || ia.IndexSchema || '.' || ia.IndexName || '_sbti on ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree (f1) local'; END IF; dbms_output.put_line('Create'); dbms_output.put_line(stmt); -- execute the statement IF ((env.CallProperty is null) OR (env.CallProperty = sys.ODCIConst.FirstCall) OR (env.CallProperty = sys.ODCIConst.FinalCall) ) THEN execute immediate stmt; IF (env.CallProperty is null) THEN execute immediate 'insert into ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree select ' || ia.IndexCols(1).ColName || ', ROWID from ' || ia.IndexCols(1).TableSchema || '.' || ia.IndexCols(1).TableName; execute immediate 'create index ' || ia.IndexSchema || '.' || ia.IndexName || '_sbti on ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree (f1)'; END IF; END IF; RETURN ODCIConst.Success; END ODCIIndexCreate;
16.4.3.3 PBSTREEのODCIIndexDrop()のPL/SQLでの実装
ODCIIndexDrop()ルーチンは、索引記憶表を削除します。
STATIC FUNCTION ODCIIndexDrop( ia sys.ODCIIndexInfo, env sys.ODCIEnv) RETURN NUMBER IS stmt VARCHAR2(1000); cnum INTEGER; junk INTEGER; BEGIN -- construct the sql statement stmt := ''; IF (env.CallProperty is null) THEN stmt := 'drop table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree'; dbms_output.put_line('Drop'); dbms_output.put_line(stmt); execute immediate stmt; END IF; RETURN ODCIConst.Success; END ODCIIndexDrop;
16.4.3.4 PSBTREEのODCIIndexAlter()のPL/SQLでの実装
ODCIIndexAlter()ルーチンで、索引の再作成や名前の変更など、多くの索引変更タスクを実行できます。
STATIC FUNCTION ODCIIndexAlter ( ia sys.ODCIIndexInfo, parms IN OUT VARCHAR2, altopt NUMBER, env sys.ODCIEnv) RETURN NUMBER IS stmt VARCHAR2(2000); BEGIN stmt := ''; IF (altopt = ODCIConst.AlterIndexRebuild) THEN IF (ia.IndexPartition is null) THEN stmt := 'insert into ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree select ' || ia.IndexCols(1).ColName || ', ROWID from ' || ia.IndexCols(1).ColName || ', ROWID from ' || ia.IndexCols(1).TableSchema || '.' || ia.IndexCols(1).TableName; ELSIF (bitand(ia.IndexInfoFlags, ODCIConst.CompPartn) = 0) THEN stmt := 'insert into ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree select partition (' || ia.IndexPartition || ')' || ia.IndexCols(1).ColName || ', ROWID from ' || ia.IndexCols(1).TableSchema || '.' || ia.IndexCols(1).TableName || ' partition (' || ia.IndexCols(1).TablePartition || ')'; ELSE stmt := 'insert into ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree select partition (' || ia.IndexPartition || ')' || ia.IndexCols(1).ColName || ', ROWID from ' || ia.IndexCols(1).TableSChema || '.' || ia.IndexCols(1).TableName || ' subpartition (' || ia.IndexCols(1).TablePartition || ')'; END IF; ELSIF (altopt = ODCIConst.AlterIndexRename) THEN IF (ia.IndexPartition is not null) THEN stmt := 'alter table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree rename partition ' || ia.IndexPartition || ' to ' || parms; ELSE stmt := 'alter table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree rename to ' || parms || '_sbtree'; END IF; END IF; dbms_output.put_line('Alter'); IF ((altopt = ODCIConst.AlterIndexRebuild) OR (altopt = ODCIConst.AlterIndexRename)) THEN dbms_output.put_line(stmt); execute immediate stmt; END IF; RETURN ODCIConst.Success; END ODCIIndexAlter;
16.4.3.5 PSBTREEのODCIIndexUpdPartMetadata()のPL/SQLでの実装
パーティション・メンテナンス操作を処理するために、ユーザーのかわりにカーネルがメンテナンス・タスクを実行します。索引タイプは、メタデータ保持のために、ODCIIndexUpdPartMetadata()ルーチンを含む必要があります。
STATIC FUNCTION ODCIIndexUpdPartMetadata( ia sys.ODCIIndexInfo, palist sys.ODCIPartInfoList, env sys.ODCIEnv) RETURN NUMBER IS col number; BEGIN dbms_output.put_line('ODCIUpdPartMetadata'); sys.ODCIIndexInfoDump(ia); sys.ODCIPartInfoListDump(palist); sys.ODCIEnvDump(env); RETURN ODCIConst.Success; END ODCIIndexUpdPartMetadata;
16.4.3.6 PSBTREEのODCIIndexExchangePartition()のPL/SQLでの実装
ODCIIndexExchangePartition()は、交換対象の索引パーティションの索引記憶表を、グローバル・ドメイン索引の索引記憶表と交換します。
STATIC FUNCTION ODCIIndexExchangePartition( ia sys.ODCIIndexInfo, ia1 sys.ODCIIndexInfo, env sys.ODCIEnv) RETURN NUMBER IS stmt VARCHAR2(2000); cnum INTEGER; junk INTEGER; BEGIN stmt := ''; dbms_output.put_line('Exchange Partition'); -- construct the sql statements IF bitand(ia.IndexInfoFlags, ODCIConst.CompPartn) = 0 OR bitand(ia.IndexInfoFlags, ODCIConst.SubPartn) = ODCIConst.SubPartn THEN -- non-composite partitioned or exchanging subpartition stmt := 'alter table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree exchange partition ' || ia.IndexPartition || ' with table ' || ia1.IndexSchema || '.' || ia1.IndexName || '_sbtree'; dbms_output.put_line(stmt); execute immediate stmt; ELSE -- composite partition exchange stmt := 'create table temp_exch (f1 VARCHAR2(1000), f2 rowid)'; dbms_output.put_line(stmt); execute immediate stmt; stmt := 'alter table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree exchange partition ' || ia.IndexPartition || ' with table ' || 'temp_exch'; dbms_output.put_line(stmt); execute immediate stmt; stmt := 'alter table ' || ia1.IndexSchema || '.' || ia1.IndexName || '_sbtree exchange partition ' || ia1.IndexPartition || ' with table ' || 'temp_exch'; dbms_output.put_line(stmt); execute immediate stmt; stmt := 'alter table ' || ia.IndexSchema || '.' || ia.IndexName || '_sbtree exchange partition ' || ia.IndexPartition || ' with table ' || 'temp_exch'; dbms_output.put_line(stmt); execute immediate stmt; -- exchange done, drop temporal table stmt := 'drop table temp_exch'; dbms_output.put_line(stmt); execute immediate stmt; END IF; RETURN ODCIConst.Success; END ODCIIndexExchangePartition;
16.4.4 ODCIIndexXXX()メソッドのC実装の登録
コンパイル済Cメソッド用にextdemo6l
ライブラリ・オブジェクトを作成後、各ルーチンの実装を登録する必要があります。「ODCIIndexInsert()の実装の登録」の項ではODCIIndexInsert()の実装、「ODCIIndexDelete()の実装の登録」の項ではODCIIndexDelete()の実装、「ODCIIndexUpdate()の実装の登録」の項ではODCIIndexUpdate()の実装、「ODCIIndexStart()の実装の登録」の項ではODCIIndexStart()の実装、「ODCIIndexFetch()の実装の登録」の項ではODCIIndexFetch()の実装、および「ODCIIndexClose()の実装の登録」の項ではODCIIndexClose()の実装を、それぞれ登録する方法を示します。
16.4.4.1 ODCIIndexInsert()の実装の登録
ODCIIndexInsert()ルーチンの実装を登録します。
STATIC FUNCTION ODCIIndexInsert( ia SYS.ODCIIndexInfo, rid VARCHAR2, newval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER AS EXTERNAL name "qxiqtbspi" library extdemo6l with context parameters ( context, ia, ia indicator struct, rid, rid indicator, newval, newval indicator, env, env indicator struct, return OCINumber );
16.4.4.2 ODCIIndexDelete()の実装の登録
ODCIIndexDelete()ルーチンの実装を登録します。
STATIC FUNCTION ODCIIndexDelete( ia SYS.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER AS EXTERNAL name "qxiqtbspd" library extdemo6l with context parameters ( context, ia, ia indicator struct, rid, rid indicator, oldval, oldval indicator, env, env indicator struct, return OCINumber );
16.4.4.3 ODCIIndexUpdate()の実装の登録
ODCIIndexUpdate()ルーチンの実装を登録します。
STATIC FUNCTION ODCIIndexUpdate( ia SYS.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, newval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER AS EXTERNAL name "qxiqtbspu" library extdemo6l with context parameters ( context, ia, ia indicator struct, rid, rid indicator, oldval, oldval indicator, newval, newval indicator, env, env indicator struct, return OCINumber );
16.4.4.4 ODCIIndexStart()の実装の登録
ODCIIndexStart()ルーチンの実装を登録します。
STATIC FUNCTION ODCIIndexStart( sctx IN OUT psbtree_im, ia SYS.ODCIIndexInfo, op SYS.ODCIPredInfo, qi SYS.ODCIQueryInfo, strt NUMBER, stop NUMBER, cmpval VARCHAR2, env SYS.ODCIEnv) RETURN NUMBER AS EXTERNAL name "qxiqtbsps" library extdemo6l with context parameters ( context, sctx, sctx indicator struct, ia, ia indicator struct, op, op indicator struct, qi, qi indicator struct, strt, strt indicator, stop, stop indicator, cmpval, cmpval indicator, env, env indicator struct, return OCINumber );
16.4.4.5 ODCIIndexFetch()の実装の登録
ODCIIndexFetch()ルーチンの実装を登録します。
MEMBER FUNCTION ODCIIndexFetch( nrows NUMBER, rids OUT SYS.ODCIRidList, env SYS.ODCIEnv) RETURN NUMBER AS EXTERNAL name "qxiqtbspf" library extdemo6l with context parameters ( context, self, self indicator struct, nrows, nrows indicator, rids, rids indicator, env, env indicator struct, return OCINumber );
16.4.4.6 ODCIIndexClose()の実装の登録
ODCIIndexClose()ルーチンの実装を登録します。
MEMBER FUNCTION ODCIIndexClose ( env SYS.ODCIEnv) RETURN NUMBER AS EXTERNAL name "qxiqtbspc" library extdemo6l with context parameters ( context, self, self indicator struct, env, env indicator struct, return OCINumber );
16.4.5 C実装の追加の構造の定義
stuct
qxiqtim
、struct qciqtin
およびstruct
qxiqtcx
は、オブジェクト型とそのnull
値をマップするため(例16-6参照)、および複数のフェッチ・コールの間に状態を保持するため(例16-7参照)に使用されます。これらの構造は、「実装本体でのCメソッドの定義」で説明しているメソッドによって使用されます。
ODCIの型のマッピングに関するCのstruct
は、ファイルodci.h
に定義されています。たとえば、Cのstruct
ODCIIndexInfo
は、対応するODCIオブジェクト型のマッピングです。Cのstruct
ODCIIndexInfo_ind
は、null
オブジェクトのマッピングです。
例16-6 オブジェクト型およびNULL値のマッピングの定義
Cのstruct
, qxiqtim
がオブジェクト型のマッピングとして定義されています。null
オブジェクトに対応する追加のCのstruct
, qxiqtin
があります。オブジェクト型およびnull
オブジェクト用のCのstruct
は、Object Type Translator(OTT)から生成できます。
/* The index implementation type is an object type with a single RAW attribute * used to store the context key value. * C mapping of the implementation type : */ struct qxiqtim{ OCIRaw *sctx_qxiqtim; }; typedef struct qxiqtim qxiqtim; struct qxiqtin{ short atomic_qxiqtin; short scind_qxiqtin; }; typedef struct qxiqtin qxiqtin;
例16-7 フェッチ・コール間のスキャン状態の保持
キャッシュしてフェッチ・コール時に取り出す必要のある一連のOCIハンドルが存在します。Cのstruct
, qxiqtcx
は、必要なスキャン状態をすべて保持するように定義されています。この構造は、fetch
の終了時にも確実に存続するようにOCI_DURATION_STATEMENT
メモリーから割り当てられます。必要な情報を構造に移入した後、構造へのポインタがOCIコンテキスト内で保存されます。このコンテキストは、OCIルーチンのコールにより生成される4バイトのキーで識別されます。この4バイトのキーは、スキャン・コンテキスト(exiting
)で隠されます。このオブジェクトはOracleサーバーに戻され、次回のfetchコールにパラメータとして渡されます。
/* The index scan context - should be stored in "statement" duration memory * and used by start, fetch and close routines. */ struct qxiqtcx { OCIStmt *stmthp; OCIDefine *defnp; OCIBind *bndp; char ridp[19]; }; typedef struct qxiqtcx qxiqtcx;
16.4.6 実装本体でのCメソッドの定義
C言語でPSBEETreeのメソッドを実装する方法について考えます。
16.4.6.1 Cでの一般的なエラー処理ルーチンの実装
次のファンクションを使用して、すべてのOCI
ルーチンからのリターン・コードをチェックして処理します。このファンクションでは、ステータス・コードがチェックされ、エラーがある場合は例外が発生します。
static int qxiqtce( OCIExtProcContext *ctx, OCIError *errhp, sword status) { text errbuf[512]; sb4 errcode = 0; int errnum = 29400; /* choose some oracle error number */ int rc = 0; switch (status) { case OCI_SUCCESS: rc = 0; break; case OCI_ERROR: (void) OCIErrorGet((dvoid *)errhp, (ub4)1, (text *)NULL, &errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); /* Raise exception */ OCIExtProcRaiseExcpWithMsg(ctx, errnum, errbuf, strlen((char *)errbuf)); rc = 1; break; default: (void) sprintf((char *)errbuf, "Warning - some error\n"); /* Raise exception */ OCIExtProcRaiseExcpWithMsg(ctx, errnum, errbuf, strlen((char *)errbuf)); rc = 1; break; } return (rc); }
16.4.6.2 PSBTREEのODCIIndexInsert()のC言語での実装
挿入ルーチンODCIIndexInsert()は、索引表に新規の1行を挿入する文を解析して実行します。新規の行は、索引付けされた列の新規の値と、パラメータとして渡されたrowid
で構成されます。
OCINumber *qxiqtbspi( OCIExtProcContext *ctx, ODCIIndexInfo *ix, ODCIIndexInfo_ind *ix_ind, char *rid, short rid_ind, char *newval, short newval_ind, ODCIEnv *env, ODCIEnv_ind *env_ind) { OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */ OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */ OCIError *errhp = (OCIError *) 0; /* error handle */ OCIStmt *stmthp = (OCIStmt *) 0; /* statement handle */ OCIBind *bndp = (OCIBind *) 0; /* bind handle */ int retval = (int)ODCI_SUCCESS; /* return from this function */ OCINumber *rval = (OCINumber *)0; char insstmt[2000]; /* sql insert statement */ ODCIColInfo *colinfo; /* column info */ ODCIColInfo_ind *colinfo_ind; boolean exists = TRUE; unsigned int partiden; /* table partition iden */ unsigned int idxflag; /* index info flag /* allocate memory for OCINumber first */ rval = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); /* Get oci handles */ if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp))) return(rval); /* set up return code */ if (qxiqtce(ctx, errhp, OCINumberFromInt(errhp, (dvoid *)&retval, sizeof(retval), OCI_NUMBER_SIGNED, rval))) return(rval); /* Convert idxflag to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(ix->IndexInfoFlags), sizeof(idxflag), OCI_NUMBER_UNSIGNED, ( void *)&idxflag))) return(rval); /***************************** * Construct insert Statement * ******************************/ if ((idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN) (void)sprintf(insstmt, "INSERT into %s.%s_sbtree values (:newval, :mrid)", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName)); else { if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, (OCIColl *)ix->IndexCols, (sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind))) return(rval); (void)sprintf(insstmt, "INSERT into %s.%s_sbtree partition (DATAOBJ_TO_PARTITION(%s, :partiden)) VALUES (:newval, :mrid)", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName), OCIStringPtr(envhp, colinfo->TableName)); } /*************************************** * Parse and Execute Create Statement * ****************************************/ /* allocate stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleAlloc((dvoid *)envhp, (dvoid **)&stmthp, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0))) return(rval); /* prepare the statement */ if (qxiqtce(ctx, errhp, OCIStmtPrepare(stmthp, errhp, (text *)insstmt, (ub4)strlen(insstmt), OCI_NTV_SYNTAX, OCI_DEFAULT))) return(rval); if ((idxflag & ODCI_INDEX_RANGE_PARTN) == ODCI_INDEX_RANGE_PARTN) { /* Convert partiden to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(colinfo->TablePartitionIden), sizeof(partiden), OCI_NUMBER_UNSIGNED, ( void *)&partiden))) return(rval); /* Set up bind for partiden */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, text *)":partiden", sizeof(":partiden")-1, (dvoid *)&partiden, (sb4)(sizeof(partiden)), (ub2)SQLT_INT, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); } /* Set up bind for newval */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":newval", sizeof(":newval")-1, (dvoid *)newval, (sb4)(strlen(newval)+1), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); /* Set up bind for rid */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":mrid", sizeof(":mrid")-1, (dvoid *)rid, (sb4)(strlen(rid)+1), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); /* Execute statement */ if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT))) return(rval); /* free stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT))) return(rval); return(rval); }
16.4.6.3 PSBTREEのODCIIndexDelete()のC言語での実装
削除ルーチンは、実表から削除される行に対応する1行を索引表から削除するSQL文を構成します。索引表の行は、このルーチンにパラメータとして渡されるrowid
の値で識別されます。
OCINumber *qxiqtbspd( OCIExtProcContext *ctx, ODCIIndexInfo *ix, ODCIIndexInfo_ind *ix_ind, char *rid, short rid_ind, char *oldval, short oldval_ind, ODCIEnv *env, ODCIEnv_ind *env_ind) { OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */ OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */ OCIError *errhp = (OCIError *) 0; /* error handle */ OCIStmt *stmthp = (OCIStmt *) 0; /* statement handle */ OCIBind *bndp = (OCIBind *) 0; /* bind handle */ int retval = (int)ODCI_SUCCESS; /* return from this function */ OCINumber *rval = (OCINumber *)0; char delstmt[2000]; /* sql delete statement */ ODCIColInfo *colinfo; /* column info */ ODCIColInfo_ind *colinfo_ind; boolean exists = TRUE; unsigned int partiden; /* table partition iden */ unsigned int idxflag; /* index info flag /* Get oci handles */ if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp))) return(rval); /* set up return code */ rval = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (qxiqtce(ctx, errhp, OCINumberFromInt(errhp, (dvoid *)&retval, sizeof(retval), OCI_NUMBER_SIGNED, rval))) return(rval); /* Convert idxflag to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(ix->IndexInfoFlags), sizeof(idxflag), OCI_NUMBER_UNSIGNED, ( void *)&idxflag))) return(rval); /***************************** * Construct delete Statement * ******************************/ if ((idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN) (void)sprintf(delstmt, "DELETE FROM %s.%s_sbtree WHERE f2 = :rr", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName)); else { if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, (OCIColl *)ix->IndexCols, (sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind))) return(rval); (void)sprintf(delstmt, "DELETE FROM %s.%s_sbtree partition (DATAOBJ_TO_PARTITION(%s, :partiden)) WHERE f2 = :rr", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName), OCIStringPtr(envhp, colinfo->TableName)); } /*************************************** * Parse and Execute delete Statement * ****************************************/ /* allocate stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleAlloc((dvoid *)envhp, (dvoid **)&stmthp, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0))) return(rval); /* prepare the statement */ if (qxiqtce(ctx, errhp, OCIStmtPrepare(stmthp, errhp, (text *)delstmt, (ub4)strlen(delstmt), OCI_NTV_SYNTAX, OCI_DEFAULT))) return(rval); if ( (idxflag & ODCI_INDEX_RANGE_PARTN) == ODCI_INDEX_RANGE_PARTN) { /* Convert partiden to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(colinfo->TablePartitionIden), sizeof(partiden), OCI_NUMBER_UNSIGNED, ( void *)&partiden))) return(rval); /* Set up bind for partiden */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":partiden", sizeof(":partiden")-1, (dvoid *)&partiden, sb4)(sizeof(partiden)), (ub2)SQLT_INT, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); } /* Set up bind for rid */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":rr", sizeof(":rr")-1, (dvoid *)rid, (sb4)(strlen(rid)+1), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); /* Execute statement */ if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT))) return(rval); /* free stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT))) return(rval); return(rval); }
16.4.6.4 PSBTREEのODCIIndexUpdate()のC言語での実装
更新ルーチンは、実表内で更新される行に対応する1行を索引表内で更新するSQL文を構成します。索引表の行は、このルーチンにパラメータとして渡されるrowid
の値で識別されます。古い列値(oldval
)が新規の値(newval
)で置換されます。
OCINumber *qxiqtbspu( OCIExtProcContext *ctx, ODCIIndexInfo *ix, ODCIIndexInfo_ind *ix_ind, char *rid, short rid_ind, char *oldval, short oldval_ind, char *newval, short newval_ind, ODCIEnv *env, ODCIEnv_ind *env_ind) { OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */ OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */ OCIError *errhp = (OCIError *) 0; /* error handle */ OCIStmt *stmthp = (OCIStmt *) 0; /* statement handle */ OCIBind *bndp = (OCIBind *) 0; /* bind handle */ int retval = (int)ODCI_SUCCESS; /* return from this function */ OCINumber *rval = (OCINumber *)0; char updstmt[2000]; /* sql upate statement */ ODCIColInfo *colinfo; /* column info */ ODCIColInfo_ind *colinfo_ind; boolean exists = TRUE; unsigned int partiden; /* table partition iden */ unsigned int idxflag; /* index info flag /* Get oci handles */ if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp))) return(rval); /* set up return code */ rval = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (qxiqtce(ctx, errhp, OCINumberFromInt(errhp, (dvoid *)&retval, sizeof(retval), OCI_NUMBER_SIGNED, rval))) return(rval); /* Convert idxflag to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(ix->IndexInfoFlags), sizeof(idxflag), OCI_NUMBER_UNSIGNED, ( void *)&idxflag))) return(rval); /***************************** * Construct update Statement * ******************************/ if ( (idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN) (void)sprintf(updstmt, "UPDATE %s.%s_sbtree SET f1 = :newval WHERE f2 = :rr", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName)); else { if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, OCIColl *)ix->IndexCols, (sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind))) return(rval); (void)sprintf(updstmt, "UPDATE %s.%s_sbtree partition (DATAOBJ_TO_PARTITION(%s, :partiden)) SET f1 = :newval WHERE f2 = :rr", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName), OCIStringPtr(envhp, colinfo->TableName)); } /**************************************** * Parse and Execute Create Statement * ****************************************/ /* allocate stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleAlloc((dvoid *)envhp, (dvoid **)&stmthp, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0))) return(rval); /* prepare the statement */ if (qxiqtce(ctx, errhp, OCIStmtPrepare(stmthp, errhp, (text *)updstmt, (ub4)strlen(updstmt), OCI_NTV_SYNTAX, OCI_DEFAULT))) return(rval); if ( (idxflag & ODCI_INDEX_RANGE_PARTN) == ODCI_INDEX_RANGE_PARTN) { /* Convert partiden to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(colinfo->TablePartitionIden), sizeof(partiden), OCI_NUMBER_UNSIGNED, ( void *)&partiden))) return(rval); /* Set up bind for partiden */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":partiden", sizeof(":partiden")-1, (dvoid *)&partiden, (sb4)(sizeof(partiden)), (ub2)SQLT_INT, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); } /* Set up bind for newval */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":newval", sizeof(":newval")-1, (dvoid *)newval, (sb4)(strlen(newval)+1), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, ( ub4)OCI_DEFAULT))) return(rval); /* Set up bind for rid */ if (qxiqtce(ctx, errhp, OCIBindByName(stmthp, &bndp, errhp, (text *)":rr", sizeof(":rr")-1, (dvoid *)rid, (sb4)(strlen(rid)+1), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); /* Execute statement */ if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT))) return(rval); /* free stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT))) return(rval); return(rval); }
16.4.6.5 PSBTREEのODCIIndexStart()のC言語での実装
開始ルーチンは、psbtree
索引スキャンの設定を実行します。演算子述語、引数および戻り値の境界に関する問合せ情報が、このファンクションにパラメータとして渡されます。索引スキャン・ルーチン間で共有されるスキャン・コンテキストは、psbtree_im
タイプのインスタンスです。
このファンクションは、索引表をスキャンするカーソルを設定します。このスキャンにより、指定された述語を満たす索引表内の行の格納されたROWIDが取得されます。索引表の述語は、パラメータとして渡される演算子述語情報に基づいて生成されます。たとえば、演算子述語が eq(col, 'joe') = 1
という形式の場合は、索引表の述語が f1 = 'joe'
と設定されます。
例16-6および例16-7で示しているように、このファンクションでは、struct
qxiqtim
、qxiqtin
およびqxiqtcx
が使用されます。
OCINumber *qxiqtbsps( OCIExtProcContext *ctx, qxiqtim *sctx, qxiqtin *sctx_ind, ODCIIndexInfo *ix, ODCIIndexInfo_ind *ix_ind, ODCIPredInfo *pr, ODCIPredInfo_ind *pr_ind, ODCIQueryInfo *qy, ODCIQueryInfo_ind *qy_ind, OCINumber *strt, short strt_ind, OCINumber *stop, short stop_ind, char *cmpval, short cmpval_ind, ODCIEnv *env, ODCIEnv_ind *env_ind) { sword status; OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */ OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */ OCIError *errhp = (OCIError *) 0; /* error handle */ OCISession *usrhp = (OCISession *) 0; /* user handle */ qxiqtcx *icx = (qxiqtcx *) 0; /* state to be saved for later calls */ int strtval; /* start bound */ int stopval; /* stop bound */ int errnum = 29400; /* choose some oracle error number */ char errmsg[512]; /* error message buffer */ size_t errmsglen; /* Length of error message */ char relop[3]; /* relational operator used in sql stmt */ char selstmt[2000]; /* sql select statement */ int retval = (int)ODCI_SUCCESS; /* return from this function */ OCINumber *rval = (OCINumber *)0; ub4 key; /* key value set in "sctx" */ ub1 *rkey; /* key to retrieve context */ ub4 rkeylen; /* length of key */ ODCIColInfo *colinfo; /* column info */ ODCIColInfo_ind *colinfo_ind; boolean exists = TRUE; unsigned int partiden; /* table partition iden */ unsigned int idxflag; /* index info flag /* Get oci handles */ if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp))) return(rval); /* set up return code */ rval = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (qxiqtce(ctx, errhp, OCINumberFromInt(errhp, (dvoid *)&retval, sizeof(retval), OCI_NUMBER_SIGNED, rval))) return(rval); /* get the user handle */ if (qxiqtce(ctx, errhp, OCIAttrGet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)&usrhp, (ub4 *)0, (ub4)OCI_ATTR_SESSION, errhp))) return(rval); /**********************************************/ /* Allocate memory to hold index scan context */ /**********************************************/ if (sctx_ind ->atomic_qxiqtin == OCI_IND_NULL || sctx_ind ->scind_qxiqtin == OCI_IND_NULL) { if (qxiqtce(ctx, errhp, OCIMemoryAlloc((dvoid *)usrhp, errhp, (dvoid **)&icx, OCI_DURATION_STATEMENT, (ub4)(sizeof(qxiqtcx)), OCI_MEMORY_CLEARED))) return(rval); icx->stmthp = (OCIStmt *)0; icx->defnp = (OCIDefine *)0; icx->bndp = (OCIBind *)0; } else { /*************************/ /* Retrieve scan context */ /*************************/ rkey = OCIRawPtr(envhp, sctx->sctx_qxiqtim); rkeylen = OCIRawSize(envhp, sctx->sctx_qxiqtim); if (qxiqtce(ctx, errhp, OCIContextGetValue((dvoid *)usrhp, errhp, rkey, (ub1)rkeylen, (dvoid **)&(icx)))) return(rval); } /***********************************/ /* Check that the bounds are valid */ /***********************************/ /* convert from oci numbers to native numbers */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, strt, sizeof(strtval), OCI_NUMBER_SIGNED, (dvoid *)&strtval))) return(rval); if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, stop, sizeof(stopval), OCI_NUMBER_SIGNED, (dvoid *)&stopval))) return(rval); /* verify that strtval/stopval are both either 0 or 1 */ if (!(((strtval == 0) && (stopval == 0)) || ((strtval == 1) && (stopval == 1)))) { strcpy(errmsg, (char *)"Incorrect predicate for sbtree operator"); errmsglen = (size_t)strlen(errmsg); if (OCIExtProcRaiseExcpWithMsg(ctx, errnum, (text *)errmsg, errmsglen) != OCIEXTPROC_SUCCESS) /* Use cartridge error services here */; return(rval); } /*********************************************/ /* Generate the SQL statement to be executed */ /*********************************************/ if (memcmp((dvoid *)OCIStringPtr(envhp, pr->ObjectName), (dvoid *)"EQ", 2) == 0) if (strtval == 1) strcpy(relop, (char *)"="); else strcpy(relop, (char *)"!="); else if (memcmp((dvoid *)OCIStringPtr(envhp, pr->ObjectName), (dvoid *)"LT",2) == 0) if (strtval == 1) strcpy(relop, (char *)"<"); else strcpy(relop, (char *)">="); else if (strtval == 1) strcpy(relop, (char *)">"); else strcpy(relop, (char *)"<="); /* Convert idxflag to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(ix->IndexInfoFlags), sizeof(idxflag), OCI_NUMBER_UNSIGNED, ( void *)&idxflag))) return(rval); if ( (idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN) (void)sprintf(selstmt, "select f2 from %s.%s_sbtree where f1 %s :val", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName), relop); else { if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, OCIColl *)ix->IndexCols, (sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind))) return(rval); /* Convert partiden to integer from OCINumber */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, &(colinfo->TablePartitionIden), sizeof(partiden), OCI_NUMBER_UNSIGNED, ( void *)&partiden))) return(rval); (void)sprintf(selstmt, "select f2 from %s.%s_sbtree partition (DATAOBJ_TO_PARTITION(%s, %d)) where f1 %s :val", OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName), OCIStringPtr(envhp, colinfo->TableName), partiden, relop); } /***********************************/ /* Parse, bind, define and execute */ /***********************************/ if (sctx_ind ->atomic_qxiqtin == OCI_IND_NULL || sctx_ind ->scind_qxiqtin == OCI_IND_NULL) { /* allocate stmt handle */ if (qxiqtce(ctx, errhp, OCIHandleAlloc((dvoid *)envhp, (dvoid **)&(icx->stmthp), (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0))) return(rval); } /* prepare the statement */ if (qxiqtce(ctx, errhp, OCIStmtPrepare(icx->stmthp, errhp, (text *)selstmt, (ub4)strlen(selstmt), OCI_NTV_SYNTAX, OCI_DEFAULT))) return(rval); /* Set up bind for compare value */ if (qxiqtce(ctx, errhp, OCIBindByName(icx->stmthp, &(icx->bndp), errhp, (text *)":val", sizeof(":val")-1, (dvoid *)cmpval, (sb4)(strlen(cmpval)+1), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) return(rval); /* Set up define */ if (qxiqtce(ctx, errhp, OCIDefineByPos(icx->stmthp, &(icx->defnp), errhp, (ub4)1, (dvoid *)(icx->ridp), (sb4) sizeof(icx->ridp), (ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)OCI_DEFAULT))) return(rval); /* execute */ if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, icx->stmthp, errhp, (ub4)0, (ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT))) return(rval); /************************************/ /* Set index context to be returned */ /************************************/ if (sctx_ind ->atomic_qxiqtin == OCI_IND_NULL || sctx_ind ->scind_qxiqtin == OCI_IND_NULL) { /* generate a key */ if (qxiqtce(ctx, errhp, OCIContextGenerateKey((dvoid *)usrhp, errhp, &key))) return(rval); /* set the memory address of the struct to be saved in the context */ if (qxiqtce(ctx, errhp, OCIContextSetValue((dvoid *)usrhp, errhp, OCI_DURATION_STATEMENT, (ub1 *)&key, (ub1)sizeof(key), (dvoid *)icx))) return(rval); /* statement duration memory alloc for key */ if (qxiqtce(ctx, errhp, OCIMemoryAlloc(( void *)usrhp, errhp, ( void **)&(sctx->sctx_qxiqtim), OCI_DURATION_STATEMENT, (sb4)(sizeof(key)+sizeof(ub4)), OCI_MEMORY_CLEARED))) return(rval); /* set the key as the member of "sctx" */ if (qxiqtce(ctx, errhp, OCIRawAssignBytes(envhp, errhp, (ub1 *)&key, ub4)sizeof(key), &(sctx->sctx_qxiqtim)))) return(rval); sctx_ind->atomic_qxiqtin = OCI_IND_NOTNULL; sctx_ind->scind_qxiqtin = OCI_IND_NOTNULL; return(rval); } return(rval); }
16.4.6.6 PSBTREEのODCIIndexFetch()のC言語での実装
開始ルーチンにより設定されたスキャン・コンテキストは、フェッチ・ルーチンにパラメータとして渡されます。このファンクションは、最初にスキャン・コンテキストから4バイトのキーを取得します。スキャン・コンテキストのCマッピングはqxiqtim
です(例16-6を参照)。キーは、OCIコンテキストの検索に使用されます。これは、OCIハンドルを保持するqxiqtcx
構造(例16-7を参照)のメモリー・アドレスを示します。
このファンクションは、演算子述語を満たす次のROWIDバッチを戻します。バッチのサイズとしてnrows
パラメータの値が使用されます。オープン・カーソルからROWIDが繰り返しフェッチされ、rowid
リストに移入されます。バッチがいっぱいになるか、残っているROWIDがなくなると、このファンクションはROWIDをOracleサーバーに戻します。
OCINumber *qxiqtbspf( OCIExtProcContext *ctx, qxiqtim *self, qxiqtin *self_ind, OCINumber *nrows, short nrows_ind, OCIArray **rids, short *rids_ind, ODCIEnv *env, ODCIEnv_ind *env_ind) { sword status; OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */ OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */ OCIError *errhp = (OCIError *) 0; /* error handle */ OCISession *usrhp = (OCISession *) 0; /* user handle */ qxiqtcx *icx = (qxiqtcx *) 0; /* state to be saved for later calls */ int idx = 1; int nrowsval; OCIArray *ridarrp = *rids; /* rowid collection */ OCIString *ridstr = (OCIString *)0; int done = 0; int retval = (int)ODCI_SUCCESS; OCINumber *rval = (OCINumber *)0; ub1 *key; /* key to retrieve context */ ub4 keylen; /* length of key */ /*******************/ /* Get OCI handles */ /*******************/ if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp))) return(rval); /* set up return code */ rval = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (qxiqtce(ctx, errhp, OCINumberFromInt(errhp, (dvoid *)&retval, sizeof(retval), OCI_NUMBER_SIGNED, rval))) return(rval); /* get the user handle */ if (qxiqtce(ctx, errhp, OCIAttrGet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)&usrhp, (ub4 *)0, (ub4)OCI_ATTR_SESSION, errhp))) return(rval); /********************************/ /* Retrieve context from key */ /********************************/ key = OCIRawPtr(envhp, self->sctx_qxiqtim); keylen = OCIRawSize(envhp, self->sctx_qxiqtim); if (qxiqtce(ctx, errhp, OCIContextGetValue((dvoid *)usrhp, errhp, key, (ub1)keylen, (dvoid **)&(icx)))) return(rval); /* get value of nrows */ if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, nrows, sizeof(nrowsval), OCI_NUMBER_SIGNED, (dvoid *)&nrowsval))) return(rval); /****************/ /* Fetch rowids */ /****************/ while (!done) { if (idx > nrowsval) done = 1; else { status =OCIStmtFetch(icx->stmthp, errhp, (ub4)1, (ub2) 0, (ub4)OCI_DEFAULT); if (status == OCI_NO_DATA) { short col_ind = OCI_IND_NULL; /* have to create dummy oci string */ OCIStringAssignText(envhp, errhp, (text *)"dummy", (ub2)5, &ridstr); /* append null element to collection */ if (qxiqtce(ctx, errhp, OCICollAppend(envhp, errhp, (dvoid *)ridstr, (dvoid *)&col_ind, (OCIColl *)ridarrp))) return(rval); done = 1; } else if (status == OCI_SUCCESS) { OCIStringAssignText(envhp, errhp, (text *)icx->ridp, (ub2)18, OCIString **)&ridstr); /* append rowid to collection */ if (qxiqtce(ctx, errhp, OCICollAppend(envhp, errhp, (dvoid *)ridstr, (dvoid *)0, (OCIColl *)ridarrp))) return(rval); idx++; } else if (qxiqtce(ctx, errhp, status)) return(rval); } } /* free ridstr finally */ if (ridstr && (qxiqtce(ctx, errhp, OCIStringResize(envhp, errhp, (ub4)0, &ridstr)))) return(rval); *rids_ind = OCI_IND_NOTNULL; return(rval); }
16.4.6.7 PSBTREEのODCIIndexClose()のC言語での実装
開始ルーチンにより設定されたスキャン・コンテキストは、クローズ・ルーチンにパラメータとして渡されます。このファンクションは、最初にスキャン・コンテキストから4バイトのキーを取得します。スキャン・コンテキストのCマッピングはqxiqtim
です(例16-6を参照)。次に、キーに基づいてOCIコンテキストが検索されます。これは、OCIハンドルを保持するqxiqtcx
構造(例16-7を参照)のメモリー・アドレスを示します。
このファンクションは、すべてのOCIハンドルをクローズして解放します。また、開始ルーチンで割り当てられたメモリーも解放します。
OCINumber *qxiqtbspc( OCIExtProcContext *ctx, qxiqtim *self, qxiqtin *self_ind, ODCIEnv *env, ODCIEnv_ind *env_ind) { sword status; OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */ OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */ OCIError *errhp = (OCIError *) 0; /* error handle */ OCISession *usrhp = (OCISession *) 0; /* user handle */ qxiqtcx *icx = (qxiqtcx *) 0; /* state to be saved for later calls */ int retval = (int) ODCI_SUCCESS; OCINumber *rval = (OCINumber *)0; ub1 *key; /* key to retrieve context */ ub4 keylen; /* length of key */ if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp))) return(rval); /* set up return code */ rval = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (qxiqtce(ctx, errhp, OCINumberFromInt(errhp, (dvoid *)&retval, sizeof(retval), OCI_NUMBER_SIGNED, rval))) return(rval); /* get the user handle */ if (qxiqtce(ctx, errhp, OCIAttrGet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)&usrhp, (ub4 *)0, (ub4)OCI_ATTR_SESSION, errhp))) return(rval); /********************************/ /* Retrieve context using key */ /********************************/ key = OCIRawPtr(envhp, self->sctx_qxiqtim); keylen = OCIRawSize(envhp, self->sctx_qxiqtim); if (qxiqtce(ctx, errhp, OCIContextGetValue((dvoid *)usrhp, errhp, key, (ub1)keylen, (dvoid **)&(icx)))) return(rval); /* Free handles and memory */ if (qxiqtce(ctx, errhp, OCIHandleFree((dvoid *)icx->stmthp, (ub4)OCI_HTYPE_STMT))) return(rval); if (qxiqtce(ctx, errhp, OCIMemoryFree((dvoid *)usrhp, errhp, (dvoid *)icx))) return(rval); /* free the memory allocated for the index context. */ if (qxiqtce(ctx, errhp, OCIContextClearValue((dvoid *)usrhp, errhp, key, (ub1)keylen))) return(rval); return(rval); }
16.5 PSBTREEの使用
索引タイプ・オブジェクトを作成し、サポートする演算子のリストを指定する必要があります。また、ODCIIndex
XXX
()
インタフェース・ルーチンを実装する実装タイプの名前を指定します。このステップは「PSBTREEの索引タイプの実装」の項に示されています。
一般的な使用例の1つとして、レンジ・パーティション表を作成して移入する場合(「PSBTREEのパーティション表の作成と移入」の項参照)が挙げられます。
列f2
でpsbtree
索引を作成できます。CREATE INDEX
文は、使用する索引タイプを指定します(「列のPSBTREE索引の作成」の項参照)。
psbtree
のいずれかの演算子を使用する問合せを実行するには、「問合せでのPSBTREE演算子の使用」の項で示したコードを使用します。
16.5.1 PSBTREEのパーティション表の作成および移入
CREATE TABLE t1 (f1 NUMBER, f2 VARCHAR2(200)) PARTITION BY RANGE(f1) ( PARTITION p1 VALUES LESS THAN (101), PARTITION p2 VALUES LESS THAN (201), PARTITION p3 VALUES LESS THAN (301), PARTITION p4 VALUES LESS THAN (401) ); INSERT INTO t1 VALUES (10, 'aaaa'); INSERT INTO t1 VALUES (200, 'bbbb'); INSERT INTO t1 VALUES (100, 'cccc'); INSERT INTO t1 VALUES (300, 'dddd'); INSERT INTO t1 VALUES (400, 'eeee'); COMMIT;
16.5.2 列のPSBTREE索引の作成
CREATE INDEX it1 ON t1(f2) iINDEXTYPE IS psbtree LOCAL (PARTITION pe1 PARAMETERS('test1'), PARTITION pe2, PARTITION pe3, PARTITION pe4 PARAMETERS('test4')) PARAMETERS('test');
16.5.3 問合せでのPSBTREE演算子の使用
SELECT * FROMM t1 WHERE eq(f2, 'dddd') = 1 AND f1>101 ;
この問合せに対するEXPLAIN PLANの出力は、次のように出力されます。
OPERATION OPTIONS PARTITION_START PARTITION_STOP -------------------------------------------------------------------------------- SELECT STATEMENT PARTITION RANGE ITERATOR 2 4 TABLE ACCESS BY LOCAL INDEX ROWID 2 4 DOMAIN INDEX