ヘッダーをスキップ
Oracle Databaseデータ・カートリッジ開発者ガイド
11gリリース1(11.1)
E05688-02
  目次
目次
索引
索引

戻る
戻る
次へ
次へ
 

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

この章では、一部のODCIIndexインタフェース・ルーチンがCで実装されている拡張可能索引付けの例について説明します。

この章の内容は、次のとおりです。

PSBTREEの例の概要

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

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

索引タイプの設計

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

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

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

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

演算子の実装

PSBtree索引タイプは、3つの演算子をサポートします。各演算子には対応するファンクション実装があります。次の項では、eqgtおよびlt演算子のファンクション実装について説明します。

ファンクション実装の作成

この項では、比較演算子のファンクション実装について説明します。例16-1eq操作(等しい)の実装方法を説明し、例16-2lt操作(より小さい)の実装方法を説明し、例16-3gt操作(より大きい)の実装方法を説明します。

例16-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-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 GREATER THAN演算子の実装方法

gtのファンクション実装は、ファンクション(bt_gt)により提供されます。このファンクションは、2つのVARCHAR2パラメータを取り、両者が3以上である場合は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-4eq操作(等しい)の作成方法を説明し、例16-5lt操作(より小さい)の作成方法を説明し、例16-6gt操作(より大きい)の作成方法を説明します。

例16-4 EQUALS演算子の作成方法

CREATE OPERATOR eq
BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER
USING bt_eq;

例16-5 LESS THAN演算子の作成方法

CREATE OPERATOR lt
BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER
USING bt_lt;

例16-6 GREATER THAN演算子の作成方法

CREATE OPERATOR gt
BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER
USING bt_gt;

ODCIIndexインタフェースの実装

次の項に示されているように、PSBTREEを実装するには、ODCIIndexXXX()ルーチンを実装する必要があります。索引ルーチンは、Oracleでサポートされている任意の言語で実装できます。この例では、ODCIGetInterfaces()ルーチンを実装します。索引操作ルーチンと問合せメソッドはCプログラム言語で実装します。これらを実行するには、コンパイル済Cコード用にextdemo6lというライブラリ・オブジェクトを作成するなど、高度な手順が必要です。

PSBTREEの実装タイプの定義

例16-7に示されるように、ODCIIndexインタフェース・ルーチンを実装する実装タイプを定義します。

例16-7 PSBTREE索引タイプの作成方法

CREATE TYPE psbtree_im 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 ODCIIndexExchangePartition (ia sys.ODCIIndexInfo,
    ia1 sys.ODCIIndexInfo, 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-8に示されるように、実装タイプ本体を定義します。

例16-8 PBSTREEの実装本体の作成方法

CREATE OR REPLACE TYPE BODY psbtree_im IS

実装本体でのPL/SQLルーチンの定義

この項の例は、PL/SQLでの索引定義ルーチンの実装方法を示します。例16-9にはODCIGetInterfaces()の実装方法、例16-10にはODCIIndexCreate()の実装方法、例16-11にはODCIIndexDrop()の実装方法、例16-12にはODCIIndexAlter()の実装方法、例16-13にはODCIIndexUpdPartMetadata()の実装方法、例16-14にはODCIIndexExchangePartition()の実装方法を示します。

例16-9 PL/SQLのPBSTREEのODCIGetInterfaces()の実装方法

例16-9に示されるように、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-10 PL/SQLのPBSTREEのODCIIndexCreate()の実装方法

ODCIIndexCreate()ルーチンは、2つの列を持つシステム・パーティション表を作成します。第1列には、索引付けされたVARCHAR2列の値が格納されます。起動コンテキストは、渡された情報を使用して判別されます。DBMSSQLを使用して、動的に構成されたSQL文が実行されます。

STATIC FUNCTION ODCIIndexCreate (
  ia 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_name;
BEGIN
  stmt := '';

  IF (env.CallProperty is null)  THEN
    stmt := 'create table ' ||ia.IndexSchema || '.' || ia.IndexName ||
      '_sbtree(f1 VARCHAR2(1000), f2 rowid)';

  ELSEIF (env.callproperty = sys.ODCIConst.FirstCall) THEN
  stmt := '';
  i := 1;
  FOR c1 in cur1(ia.indexname) LOOP
    IF (i >1) THEN
      stmt := stmt || ',';
    END IF;
    stmt := stmt || 'partition ' || c1.partition_name;
    i := i+1;
  END LOOP;
  stmt := 'create table ' || ia.indexschema || '.' || ia.indexname ||
    '_sbtree (f1 VARCHAR2(1000), f2 rowid) partition by system ' ||
     '( ' || stmt || ')';

  ELSEIF (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-11 PL/SQLのPBSTREEのODCIIndexDrop()の実装方法

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-12 PL/SQLでのPSBTREEのODCIIndexAlter()の実装方法

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).tableschema || '.' || ia.indexcols(1).tablename;
    ELSE
      stmt := 'insert into ' || ia.indexschema || '.' || ia.indexname ||
          '_sbtree partition (' || ia.indexpartition || ') select ' ||
          ia.indexcols(1).colname || ', rowid from ' ||
          ia.indexcols(1).tableschema || '.' || ia.indexcols(1).tablename ||
          ' partition (' || ia.indexcols(1).tablepartition || ')';
    END IF;
  ELSEIF (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-13 PL/SQLのPSBTREEのODCIIndexUpdPartMetadata()の実装方法

パーティション・メンテナンス操作を処理するために、ユーザーのかわりにカーネルがメンテナンス・タスクを実行します。索引タイプは、メタデータ保持のために、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-14 PL/SQLのPSBTREEのODCIIndexExchangePartition()の実装方法

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 Partitions');

  -- construct the sql statement
  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;

  RETURN ODCIConst.Success;
END ODCIIndexExchangePartition;

ODCIIndexXXX()メソッドのC実装の登録

コンパイル済Cメソッドにextdemo6lライブラリ・オブジェクトを作成後、各ルーチンの実装を登録する必要があります。例16-15ODCIIndexInsert()実装の登録方法を、例16-16ODCIIndexDelete()実装の登録方法を、例16-17ODCIIndexUpdate()実装の登録方法を、例16-18ODCIIndexStart()実装の登録方法を、例16-19ODCIIndexFetch()実装の登録方法を、例16-20ODCIIndexClose()実装の登録方法を示します。

例16-15 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-16 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-17 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-18 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-19 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-20 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
);

C実装の追加の構造の定義

struct qxiqtimおよびqciqtin、およびstruct qxiqtcxは、オブジェクト型およびそのnull値のマッピング(例16-21を参照)、およびコールのフェッチ時の状態を保つために使用されます(例16-22を参照)。これらの構造は、実装本体におけるCメソッドの定義に関する項に示されるようなメソッドによって使用されます。

ODCIの型のマッピングに関するCのstructは、ファイルodci.hに定義されています。たとえば、Cのstruct ODCIIndexInfoは、対応するODCIオブジェクト型のマッピングです。Cのstruct ODCIIndexInfo_indは、nullオブジェクトのマッピングです。

例16-21 オブジェクト型および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
 * which will be 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-22 コールのフェッチ時のスキャンの状態の保持方法

キャッシュしてコールのフェッチ時に取り出す必要のある一連の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;

実装本体でのCメソッドの定義

次のメソッドは、C言語で実装されます。例16-23にエラー処理ルーチンの実装方法を示し、例16-24ODCIIndexInsert()を実装し、例16-25ODCIIndexDelete()を実装し、例16-26ODCIIndexUpdate()を実装し、例16-27ODCIIndexStart()を実装し、例16-28ODCIIndexFetch()を実装し、例16-29ODCIIndexClose()を実装します。

例16-23 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-24 CでのPSBTREEのODCIIndexInsert()の実装方法

挿入ルーチン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 (SYS_OP_DOBJTOPNUM(%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-25 CでのPSBTREEのODCIIndexDelete()の実装方法

削除ルーチンは、実表から削除される行に対応する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 (SYS_OP_DOBJTOPNUM(%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-26 CでのPSBTREEのODCIIndexUpdate()の実装方法

更新ルーチンは、実表内で更新される行に対応する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
        (SYS_OP_DOBJTOPNUM(%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-27 CでのPSBTREEのODCIIndexStart()の実装方法

開始ルーチンは、psbtree索引スキャンの設定を実行します。演算子述語、引数および戻り値の境界に関する問合せ情報が、このファンクションにパラメータとして渡されます。索引スキャン・ルーチン間で共有されるスキャン・コンテキストは、psbtree_imタイプのインスタンスです。

このファンクションは、索引表をスキャンするカーソルを設定します。スキャンでは、指定の述語を満たす索引表の行について格納されているROWIDが取得されます。索引表に対する述語は、パラメータとして渡される演算子述語情報に基づいて生成されます。たとえば、eq(col, 'joe') = 1という形式の演算子述語があるとします。この場合、索引表に対する述語はf1 = 'joe'のように設定されます。

例16-21および例16-22ですでに示しているように、このファンクションでは、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        (SYS_OP_DOBJTOPNUM(%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-28 CでのPSBTREEのODCIIndexFetch()の実装方法

開始ルーチンにより設定されたスキャン・コンテキストは、フェッチ・ルーチンにパラメータとして渡されます。このファンクションは、最初にスキャン・コンテキストから4バイトのキーを取得します。スキャン・コンテキストのCマッピングはqxiqtimです(例16-21を参照)。キーは、OCIコンテキストの検索に使用されます。これは、OCIハンドルを保持するqxiqtcx構造(例16-22を参照)のメモリー・アドレスを示します。

このファンクションは、演算子述語を満たす次の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-29 CでのPSBTREEのODCIIndexClose()の実装方法

開始ルーチンにより設定されたスキャン・コンテキストは、クローズ・ルーチンにパラメータとして渡されます。このファンクションは、最初にスキャン・コンテキストから4バイトのキーを取得します。スキャン・コンテキストのCマッピングはqxiqtimです(例16-21を参照)。次に、キーに基づいてOCIコンテキストが検索されます。これは、OCIハンドルを保持するqxiqtcx構造(例16-22を参照)のメモリー・アドレスを示します。

このファンクションは、すべての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);
}

索引タイプの実装

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

例16-30 PSBTREEの索引タイプの実装方法

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

PSBTREEの使用

例16-31に示すように、標準的な使用例の1つは、レンジ・パーティション表を作成し、移入することです。

例16-31 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;

f2psbtree索引を作成できます。例16-32に示すように、CREATE INDEX文には、使用する索引タイプを指定します。

例16-32 列での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');

psbtree演算子の1つを使用する問合せを実行するには、例16-33のコードを使用します。

例16-33 問合せ時の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