16 PSBTREE: 拡張可能索引付けの例

これは、ODCIIndexインタフェース・ルーチンのC言語実装を含む拡張可能索引付けの例です。

16.1 PSBTREEの例について

拡張可能索引付けインタフェース・ルーチンをC言語で実装する方法を考えます。この例では、すべての実装に共通するトピックを重点的に取り上げ、ドメイン固有の詳細は取り上げません。

この例のコードはファイルextdemo6.sqlのデモ・ディレクトリにあります。これは、前述の例(デモ・ディレクトリ内のextdemo2.sql)をレンジ・パーティション表のローカル・ドメイン索引に対する索引タイプのサポートに追加して拡張します。

16.2 索引タイプの設計

ここで実装するPSBtreeという索引タイプの動作は、Bツリー索引と同様です。eq(等しい)、lt (より小さい)、gt(より大きい)という、3つのユーザー定義演算子をサポートします。これらの演算子はVARCHAR2データ型のオペランドを操作します。

索引データは、<key, rid>という形式のレコードで構成されます。ここでkeyは索引付けされた列の値、ridは対応する行のROWIDです。索引タイプの実装を簡略化するために、索引データはシステム・パーティション表に格納されます。

索引がシステム管理のローカルドメイン索引の場合は、パーティションごとに1つシステム・パーティション表が作成され、そのパーティションの索引データが格納されます。そのため、索引操作ルーチンは、単にPSBtreeの操作を索引データを格納する表のパーティションの操作に変換します。

ユーザーがPSBtree索引(ローカル索引)を作成すると、索引付けされた列とrowid列で構成されるn個の表パーティションが作成されます(nは実表内のパーティション数)。実表にデータが挿入されると、影響を受ける索引表パーティションに適切なデータが挿入されます。削除および更新も同様に処理されます。PSBtreeに対してユーザー定義演算子(gtlteqのいずれか)に基づいて問合せが実行されると、該当する問合せが索引表パーティションに対して発行され、条件を満たすすべての行が取得されます。適切なパーティション・プルーニングが行われ、関連する、つまり「興味のある」パーティションに対応する索引表パーティションのみがアクセスされます。

16.3 演算子の実装

PSBtree索引タイプは、3つの演算子(eqgtおよび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.1.3 GREATER THAN演算子の実装

gtのファンクション実装は、ファンクション(bt_gt)により提供されます。このファンクションは、2つのVARCHAR2パラメータを取り、最初のパラメータが2番目のパラメータより大きい場合は1、それ以外の場合は0を戻します。

CREATE FUNCTION bt_gt(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を実装するには、ODCIIndexXXX()ルーチンを実装する必要があります。索引ルーチンは、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 qxiqtimstruct 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 qxiqtimqxiqtinおよび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.4.7 PSBTREEの索引タイプの実装

索引タイプ・オブジェクトを作成し、サポートする演算子のリストを指定する必要があります。また、ODCIIndexXXX()インタフェース・ルーチンを実装する実装タイプの名前を指定します。

CREATE INDEXTYPE psbtree
FOR
     eq(VARCHAR2, VARCHAR2),
     lt(VARCHAR2, VARCHAR2),
     gt(VARCHAR2, VARCHAR2)
USING psbtree_im
WITH LOCAL RANGE PARTITION
WITH SYSTEM MANAGED STORAGE TABLES

16.5 PSBTREEの使用

索引タイプ・オブジェクトを作成し、サポートする演算子のリストを指定する必要があります。また、ODCIIndexXXX()インタフェース・ルーチンを実装する実装タイプの名前を指定します。このステップは「PSBTREEの索引タイプの実装」の項に示されています。

一般的な使用例の1つとして、レンジ・パーティション表を作成して移入する場合(「PSBTREEのパーティション表の作成と移入」の項参照)が挙げられます。

f2psbtree索引を作成できます。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