8.4 OCIにおけるLOB用のデータ・インタフェース

この項では、LOB用のデータ・インタフェースに組み込まれているOCI関数について説明します。これらのOCI関数は、VARCHARデータ型の場合とまったく同じようにLOBデータ型に対して機能します。

これらの関数を使用すると、LOBに対してOCIでINSERTUPDATEおよびフェッチ操作を実行できます。これらの手法は、文字データまたはバイナリ・データを格納するために他のデータ型で使用する手法と同じです。

ノート:

配列のバインドおよび定義インタフェースを使用すると、LOBを含む複数の行を1回のラウンドトリップで挿入および選択できます。

8.4.1 OCIにおけるLOBのバインド

この項では、OCIでLOBデータ型をバインドするために使用できる操作について説明します。

  • INSERT操作およびUPDATE操作に対する標準、ピース単位およびコールバックのバインド

  • INSERT操作およびUPDATE操作に対する配列のバインド

  • PL/SQLとOCIの境界でのパラメータの受渡し

ピース単位操作は、ポーリングまたはコールバックによって実行できます。これらの操作をサポートするために、次のOCI関数では、表8-1に示すLONGデータ型とLOBデータ型が受け入れられます。

  • OCIBindByName()およびOCIBindByPos()

    これらの関数を使用して、INSERT操作およびUPDATE操作用にSQL文またはPL/SQLブロック内のプログラム変数とプレースホルダ間の対応付けを行います。

  • OCIBindDynamic()

    このコールを使用して、INSERT操作またはUPDATE操作用に動的にデータを割り当てるためのコールバックを登録します。

  • OCIStmtGetPieceInfo()およびOCIStmtSetPieceInfo()

    これらのコールを使用して、ピース単位操作のためのピース情報を取得または設定します。

8.4.2 OCIにおけるLOBの定義

この項で説明するOCI関数は、LOB型をデータ型および出力バッファに関連付けます。

LOB用のデータ・インタフェースを使用すると、表8-1に示すように、次のOCI関数でLONGデータ型とLOBデータ型を受け入れることができます。

次の機能を使用できます

  • OCIDefineByPos()

    このコールを使用して、SELECTリストにある項目を型および出力データ・バッファに対応付けます。

  • OCIDefineDynamic()

    このコールを使用して、OCIDefineByPos()関数のコールでOCI_DYNAMIC_FETCHモードが選択された場合、SELECT操作に対してユーザー・コールバックを登録します。OCIDataServerLengthGet()関数を使用すると、動的定義コールバックの使用中にLOB長を取得できます。

これらの関数をLOB型に使用すると、LOBロケータではなくLOBデータがバッファに選択されます。OCIでは、LOB用のデータ・インタフェースを使用して読み取る量を指定できないことに注意してください。指定できるのは、バッファ長のみです。バッファに収まる量のデータのみが読み取られ、データが切り捨てられます。

8.4.3 LOB用データ・インタフェースでのOCIのマルチバイト文字セットの併用

この項では、OCIクライアントでマルチバイト文字セットを使用する場合のLOB用のデータ・インタフェースの機能について説明します。

クライアントの文字セットがマルチバイト形式の場合、データ・インタフェースに含まれる関数は、VARCHAR2データ型の場合と同様にLOBデータ型を操作します。

  • マルチバイト文字セットでのピース単位フェッチの場合、マルチバイト文字が途中で切れることがあり、一部のバイトが1つのバッファの最後に、残りのバイトが次のバッファに入ることがあります。

  • 通常のフェッチの場合、最後の文字のバイトの一部をバッファで保持できない場合には、バッファに収まるかぎりのバイトが戻されるため、部分的な文字にしかなりません。

8.4.4 LOB長の取得

この項では、OCIアプリケーションでのLOB長のフェッチ方法について説明します。

LOBデータ長をフェッチするには、OCIServerDataLengthGet() OCI関数を使用します。データ・インタフェースを使用してLOB列にアクセスすると、サーバーは最初にLOBデータ長を送信し、次にLOBデータを送信します。変換が行われる前に、サーバーはまずLOBデータ長を通信します。OCIクライアントにより、取得したLOB長がdefineハンドルに格納されます。OCIアプリケーションでは、OCIServerDataLengthGet()関数を使用してLOB長にアクセスできます。

LOB長には、すべてのフェッチ・モード(単一ピース、ピース単位およびコールバック)でアクセスできます。また、サーバーとのラウンドトリップを発生させることなく、コールバックの内部でそれにアクセスできます。ただし、フェッチ操作の前は、それを使用しないでください。ピース単位操作またはコールバック操作の場合は、最初のピースがフェッチされた直後にそれを使用する必要があります。

8.4.5 OCI関数を使用したLOB列のINSERTまたはUPDATEの実行

この項では、データ・インタフェースを使用してLOB列またはLOB属性のINSERT操作またはUPDATE操作を実行する各種の方法について説明します。

この項で説明する操作は、OCI環境を初期化して必要なハンドルをすべて割当て済であることを前提としています。

8.4.5.1 1つのピースでの単純なINSERT操作またはUPDATE操作の実行

この項では、LOB用のデータ・インタフェースを使用して、単一のピースで単純なINSERT操作またはUPDATE操作を実行するステップを示します。

  1. OCIStmtPrepare()OCI_DEFAULTモードでコールして、文を準備します。
  2. OCIBindByName()またはOCIBindbyPos()OCI_DEFAULTモードでコールして、LOBを文字データまたはバイナリ・データとしてバインドするためにプレースホルダをバインドします。
  3. OCIStmtExecute()をコールして、実際のINSERT操作またはUPDATE操作を実行します。

LOB列に対するINSERT操作およびUPDATE操作の文字データのバインドの例を次に示します。

void simple_insert() 
{ 
/* Insert of data into LOB attributes is allowed. */
   ub1 buffer[8000]; 
   text *insert_sql = (text *)"INSERT INTO Print_media (ad_header) \
               VALUES (adheader_typ(NULL, NULL, NULL,:1))"; 
   OCIStmtPrepare(stmthp, errhp, insert_sql, strlen((char*)insert_sql),  
            (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); 
   OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (dvoid *)buffer, 2000,  
             SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); 
   OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (const OCISnapshot*) 0, 
                  (OCISnapshot*)0, OCI_DEFAULT); 
}

8.4.5.2 ポーリングによるピース単位INSERT操作およびUPDATE操作の使用

この項では、LOB用のデータ・インタフェースを使用して、ポーリングによるピース単位INSERTまたはUPDATE操作を実行するステップを示します。

  1. OCIStmtPrepare()OCI_DEFAULTモードでコールして、文を準備します。
  2. OCIBindByName()またはOCIBindbyPos()OCI_DATA_AT_EXECモードでコールして、LOBを文字データまたはバイナリ・データとしてバインドします。
  3. OCIStmtExecute()をデフォルト・モードでコールします。OCIStmtExecute()から戻される値がOCI_NEED_DATAの間は、ループ内で次の各手順を実行します。OCIStmtExecute()から値OCI_SUCCESSが戻された場合は、ループを終了します。
    • OCIStmtGetPieceInfo()をコールして、挿入するピースの情報を取り出します。

    • OCIStmtSetPieceInfo()をコールして、挿入するピースの情報を設定します。

次の例に、LOB用のデータ・インタフェースとともにポーリングによるピース単位INSERTを使用する例を示します。

void piecewise_insert()
{
  text *sqlstmt = (text *)"INSERT INTO Print_media(Product_id, Ad_id,\
                  Ad_sourcetext) VALUES (:1, :2, :3)";
  ub2 rcode;
  ub1 piece, i;
  word product_id = 2004;
  word ad_id = 2;
  ub4 buflen;
  char buf[5000];
 
  OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen((char *)sqlstmt), 
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
  OCIBindByPos(stmthp, &bndhp[0], errhp, (ub4) 1,
               (dvoid *) &product_id, (sb4) sizeof(product_id), SQLT_INT,
               (dvoid *) 0, (ub2 *)0, (ub2 *)0,
               (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
  OCIBindByPos(stmthp, &bndhp[1], errhp, (ub4) 2,
               (dvoid *) &ad_id, (sb4) sizeof(ad_id), SQLT_INT,
               (dvoid *) 0, (ub2 *)0, (ub2 *)0,
               (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
  OCIBindByPos(stmthp, &bndhp[2], errhp, (ub4) 3,
               (dvoid *) 0, (sb4) 15000, SQLT_LNG,
               (dvoid *) 0, (ub2 *)0, (ub2 *)0,
               (ub4) 0, (ub4 *) 0, (ub4) OCI_DATA_AT_EXEC);
 
  i = 0;
  while (1)
  {
    i++;
    retval = OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                            (CONST OCISnapshot*) 0, (OCISnapshot*) 0,
                            (ub4) OCI_DEFAULT);
    switch(retval)
    {
    case OCI_NEED_DATA:
      memset((void *)buf, (int)'A'+i, (size_t)5000);
      buflen = 5000;
      if (i == 1) piece = OCI_FIRST_PIECE;
      else if (i == 3) piece = OCI_LAST_PIECE;
      else piece = OCI_NEXT_PIECE;
 
      if (OCIStmtSetPieceInfo((dvoid *)bndhp[2],
                              (ub4)OCI_HTYPE_BIND, errhp, (dvoid *)buf,
                              &buflen, piece, (dvoid *) 0, &rcode))
        {
          printf("ERROR: OCIStmtSetPieceInfo: %d \n", retval);
          break;
        }
      
      break;
    case OCI_SUCCESS:
      break;
    default:
      printf( "oci exec returned %d \n", retval);
      report_error(errhp);
      retval = OCI_SUCCESS;
    } /* end switch */
    if (retval == OCI_SUCCESS) 
      break;
  } /* end while(1) */
}

8.4.5.3 コールバックによるピース単位INSERT操作およびUPDATE操作の実行

この項では、LOB用のデータ・インタフェースを使用して、コールバックによるピース単位のINSERTまたはUPDATE操作を実行するステップを示します。

  1. OCIStmtPrepare()OCI_DEFAULTモードでコールして、文を準備します。
  2. OCIBindByName()またはOCIBindbyPos()OCI_DATA_AT_EXECモードでコールして、LOB列を文字データまたはバイナリ・データとしてバインドするためにプレースホルダをバインドします。
  3. OCIBindDynamic()をコールしてコールバックを指定します。
  4. OCIStmtExecute()をデフォルト・モードでコールします。
SQL/PLSQL操作へのOCIでの純粋なINバインドの場合、出力コールバックを指定する必要はありません。Oracle Database 21cリリース以降では、SQL/PLSQL操作へのOCIでの純粋なOUTバインドの場合、入力コールバックを提供する必要はありません。

次の例に、コールバックによるピース単位INSERTを使用したLOB列への文字データのバインドを示します。

void callback_insert()
{
  word buflen = 15000;
  word product_id = 2004;
  word ad_id = 3;
  text *sqlstmt = (text *) "INSERT INTO Print_media(Product_id, Ad_id,\
                  Ad_sourcetext) VALUES (:1, :2, :3)";
  word pos = 3;
 
  OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen((char *)sqlstmt),
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)
 
  OCIBindByPos(stmthp, &bndhp[0], errhp, (ub4) 1,
               (dvoid *) &product_id, (sb4) sizeof(product_id), SQLT_INT,
               (dvoid *) 0, (ub2 *)0, (ub2 *)0,
               (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
  OCIBindByPos(stmthp, &bndhp[1], errhp, (ub4) 2,
               (dvoid *) &ad_id, (sb4) sizeof(ad_id), SQLT_INT,
               (dvoid *) 0, (ub2 *)0, (ub2 *)0,
               (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
  OCIBindByPos(stmthp, &bndhp[2], errhp, (ub4) 3,
               (dvoid *) 0, (sb4) buflen, SQLT_CHR,
               (dvoid *) 0, (ub2 *)0, (ub2 *)0,
               (ub4) 0, (ub4 *) 0, (ub4) OCI_DATA_AT_EXEC);
 
  OCIBindDynamic(bndhp[2], errhp, (dvoid *) (dvoid *) &pos,
                 insert_cbk, (dvoid *) 0, (OCICallbackOutBind) 0);
 
  OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                 (const OCISnapshot*) 0, (OCISnapshot*) 0,
                 (ub4) OCI_DEFAULT);
} /* end insert_data() */
 
/* Inbind callback to specify input data. */
static sb4 insert_cbk(dvoid *ctxp, OCIBind *bindp, ub4 iter, ub4 index,
                       dvoid **bufpp, ub4 *alenpp, ub1 *piecep, dvoid **indpp)
{
  static int a = 0;
  word   j;
  ub4    inpos = *((ub4 *)ctxp);
  char   buf[5000];
 
  switch(inpos)
  {
  case 3:
    memset((void *)buf, (int) 'A'+a, (size_t) 5000);
    *bufpp = (dvoid *) buf;
    *alenpp = 5000 ;
    a++;
    break;
  default: printf("ERROR: invalid position number: %d\n", inpos);
  }
 
  *indpp = (dvoid *) 0;
  *piecep = OCI_ONE_PIECE;
  if (inpos == 3)
  {
    if (a<=1)
    {
      *piecep = OCI_FIRST_PIECE;
      printf("Insert callback: 1st piece\n");
    }
    else if (a<3)
    {
      *piecep = OCI_NEXT_PIECE;
      printf("Insert callback: %d'th piece\n", a);
    }
    else {
      *piecep = OCI_LAST_PIECE;
      printf("Insert callback: %d'th piece\n", a);
      a = 0;
    }
  }
  return OCI_CONTINUE;
}

8.4.5.4 配列のINSERT操作およびUPDATE操作の実行

LOB用のデータ・インタフェースを使用して配列INSERTまたはUPDATE操作を実行するには、この項で説明する方法のいずれかを使用します。

OCIStmtExecute()コールで、INSERT操作またはUPDATE操作をOCIBindArrayOfStruct()とともに使用するか、iter値が1より大きい反復回数(iter)を指定します。配列のバインドを使用する場合は、LOBデータは、単一ピース、ピース単位またはコールバックを使用して挿入されるかどうかに関係なく、1回のラウンドトリップで複数行が挿入されます。

次の例に、配列のINSERT操作を使用したLOB列への文字データのバインドを示します。

void array_insert()
{
  ub4 i;
  word buflen;
  word arrbuf1[5];
  word arrbuf2[5];
  text arrbuf3[5][5000];
  text *insstmt = (text *)"INSERT INTO Print_media(Product_id, Ad_id,\
                  Ad_sourcetext) VALUES (:PID, :AID, :SRCTXT)";
 
  OCIStmtPrepare(stmthp, errhp, insstmt,
                 (ub4)strlen((char *)insstmt), (ub4) OCI_NTV_SYNTAX,
                 (ub4) OCI_DEFAULT);
 
  OCIBindByName(stmthp, &bndhp[0], errhp,
                (text *) ":PID", (sb4) strlen((char *) ":PID"),
                (dvoid *) &arrbuf1[0], (sb4) sizeof(arrbuf1[0]), SQLT_INT,
                (dvoid *) 0, (ub2 *)0, (ub2 *) 0,
                (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
 
  OCIBindByName(stmthp, &bndhp[1], errhp,
                (text *) ":AID", (sb4) strlen((char *) ":AID"),
                (dvoid *) &arrbuf2[0], (sb4) sizeof(arrbuf2[0]), SQLT_INT,
                (dvoid *) 0, (ub2 *)0, (ub2 *) 0,
                (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
 
  OCIBindByName(stmthp, &bndhp[2], errhp,
                (text *) ":SRCTXT", (sb4) strlen((char *) ":SRCTXT"),
                (dvoid *) arrbuf3[0], (sb4) sizeof(arrbuf3[0]), SQLT_CHR,
                (dvoid *) 0, (ub2 *) 0, (ub2 *) 0,
                (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT);
 
  OCIBindArrayOfStruct(bndhp[0], errhp sizeof(arrbuf1[0]),
                       indsk, rlsk, rcsk);
  OCIBindArrayOfStruct(bndhp[1], errhp, sizeof(arrbuf2[0]),
                       indsk, rlsk, rcsk);
  OCIBindArrayOfStruct(bndhp[2], errhp, sizeof(arrbuf3[0]),
                       indsk, rlsk, rcsk);
 
  for (i=0; i<5; i++)
  {
    arrbuf1[i] = 2004;
    arrbuf2[i] = i+4;
    memset((void *)arrbuf3[i], (int)'A'+i, (size_t)5000);
  }
  OCIStmtExecute(svchp, stmthp, errhp, (ub4) 5, (ub4) 0,
                 (const OCISnapshot*) 0, (OCISnapshot*) 0,
                 (ub4) OCI_DEFAULT);
 
}

8.4.6 OCIデータ・インタフェースを使用したLOBデータのフェッチ

この項では、データ・インタフェースを使用してOCIで永続LOBまたは一時LOBからデータをフェッチする方法について説明します。

8.4.6.1 1つのピースでの単純なフェッチ操作の実行

LOBのデータ・インタフェースを使用して、1つのピースのLOBに対して単純なフェッチ操作を実行するには、この項に示すステップに従います。

  1. OCIStmtPrepare()OCI_DEFAULTモードでコールして、SELECT文を準備します。
  2. OCIDefineByPos()OCI_DEFAULTモードでコールして、LOBを文字データまたはバイナリ・データとして定義するために、選択リストの位置を定義します。
  3. OCIStmtExecute()をコールしてSELECT文を実行します。
  4. OCIStmtFetch()をコールして実際のフェッチを実行します。

次の例に、単純フェッチを使用した永続LOBまたは一時LOBの選択を示します。

void simple_fetch()
{
  word retval;
  text buf[15000];
  /*
    This statement returns a persistent LOB, but can be modified to return a temporary LOB
    using the query 'SELECT SUBSTR(Ad_sourcetext,5) FROM Print_media WHERE Product_id = 2004'
  */
  text *selstmt = (text *) "SELECT Ad_sourcetext FROM Print_media WHERE\
                  Product_id = 2004";
 
  OCIStmtPrepare(stmthp, errhp, selstmt, (ub4)strlen((char *)selstmt),
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
 
  retval = OCIStmtExecute(svchp, stmthp, errhp, (ub4) 0, (ub4) 0,
                          (const OCISnapshot*) 0, (OCISnapshot*) 0,
                          (ub4) OCI_DEFAULT);
  while (retval == OCI_SUCCESS || retval == OCI_SUCCESS_WITH_INFO)
  {
    OCIDefineByPos(stmthp, &defhp, errhp, (ub4) 1, (dvoid *) buf,
                   (sb4) sizeof(buf), (ub2) SQLT_CHR, (dvoid *) 0,
                   (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT);
    retval = OCIStmtFetch(stmthp, errhp, (ub4) 1,
                          (ub4) OCI_FETCH_NEXT, (ub4) OCI_DEFAULT);
    if (retval == OCI_SUCCESS || retval == OCI_SUCCESS_WITH_INFO)
      printf("buf = %.*s\n", 15000, buf);
  }
}

8.4.6.2 ポーリングによるピース単位のフェッチの実行

LOB用のデータ・インタフェースを使用して、ポーリングによるLOB列に対するピース単位フェッチ操作を実行するには、この項に示すステップに従います。

  1. OCIStmtPrepare()OCI_DEFAULTモードでコールして、SELECT文を準備します。
  2. OCIDefinebyPos()OCI_DYNAMIC_FETCHモードでコールして、LOB列を文字データまたはバイナリ・データとして定義するために、選択リストの位置を定義します。
  3. OCIStmtExecute()をコールしてSELECT文を実行します。
  4. OCIStmtFetch()をデフォルト・モードでコールします。OCIServerDataLengthGet()を使用してLOB長を取得し、それを使用して、LOBデータを保持するためのバッファを割り当てることもできます。OCIStmtFetch()から戻される値がOCI_NEED_DATAの間は、ループ内で次の各手順を実行します。OCIStmtFetch()から値OCI_SUCCESSが戻された場合は、ループを終了します。
    • OCIStmtGetPieceInfo()をコールして、フェッチするピースの情報を取り出します。

    • OCIStmtSetPieceInfo()をコールして、フェッチするピースの情報を設定します。

次の例に、ポーリングによるピース単位フェッチを使用した、文字バッファへのLOB列の選択を示します。

void piecewise_fetch()
{
  text buf[15000];
  ub4 buflen=5000;
  word retval;
  text *selstmt = (text *) "SELECT Ad_sourcetext FROM Print_media
                  WHERE Product_id = 2004 AND Ad_id = 2";
 
  OCIStmtPrepare(stmthp, errhp, selstmt,
                 (ub4) strlen((char *)selstmt),
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
 
  OCIDefineByPos(stmthp, &dfnhp, errhp, (ub4) 1,
                 (dvoid *) NULL, (sb4) 100000, SQLT_LNG,
                 (dvoid *) 0, (ub2 *) 0,
                 (ub2 *) 0, (ub4) OCI_DYNAMIC_FETCH);
 
  retval = OCIStmtExecute(svchp, stmthp, errhp, (ub4) 0, (ub4) 0,
                          (CONST OCISnapshot*) 0, (OCISnapshot*) 0,
                          (ub4) OCI_DEFAULT);
 
  retval = OCIStmtFetch(stmthp, errhp, (ub4) 1 ,
                        (ub2) OCI_FETCH_NEXT, (ub4) OCI_DEFAULT);
 
  while (retval != OCI_NO_DATA && retval != OCI_SUCCESS)
  {
    ub1 piece;
    ub4 iter;
    ub4 idx;
  
    genclr((void *)buf, 5000);
    switch(retval)
    {
    case OCI_NEED_DATA:
      OCIStmtGetPieceInfo(stmthp, errhp, &hdlptr, &hdltype,
                          &in_out, &iter, &idx, &piece);
      buflen = 5000;
      OCIStmtSetPieceInfo(hdlptr, hdltype, errhp,
                          (dvoid *) buf, &buflen, piece,
                          (CONST dvoid *) &indp1, (ub2 *) 0);
      retval = OCI_NEED_DATA;
      break;
    default:
      printf("ERROR: piece-wise fetching, %d\n", retval);
      return;
    } /* end switch */
    retval = OCIStmtFetch(stmthp, errhp, (ub4) 1 ,
                          (ub2) OCI_FETCH_NEXT, (ub4) OCI_DEFAULT);
    printf("Data : %.5000s\n", buf);
  } /* end while */
}

8.4.6.3 コールバックによるピース単位のフェッチの実行

この項にリストされているステップに従って、LOB用のデータ・インタフェースを使用して、コールバックによるLOB列に対するピース単位フェッチ操作を実行します。

  1. OCIStmtPrepare()OCI_DEFAULTモードでコールして、文を準備します。
  2. OCIDefinebyPos()OCI_DYNAMIC_FETCHモードでコールして、LOB列を文字データまたはバイナリ・データとして定義するために、選択リストの位置を定義します。
  3. OCIStmtExecute()をコールしてSELECT文を実行します。
  4. OCIDefineDynamic()をコールしてコールバックを指定します。
  5. OCIStmtFetch()をデフォルト・モードでコールします。
  6. コールバックの内部で、OCIServerDataLengthGet()を使用して最初のフェッチの間にLOB長を取得することもできます。この値を使用して、LOBデータを保持するためのバッファを割り当てることができます。

次の例に、コールバックによるピース単位フェッチを使用した、LOBバッファへのLOB列の選択を示します。

char buf[5000];
void callback_fetch()
{
  word outpos = 1;
  text *sqlstmt = (text *) "SELECT Ad_sourcetext FROM Print_media WHERE
                  Product_id = 2004 AND Ad_id = 3";
  
  OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen((char *)sqlstmt),
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
  OCIDefineByPos(stmthp, &dfnhp[0], errhp, (ub4) 1,
                 (dvoid *) 0, (sb4)3 * sizeof(buf), SQLT_CHR,
                 (dvoid *) 0, (ub2 *)0, (ub2 *)0,
                 (ub4) OCI_DYNAMIC_FETCH);
  
  OCIDefineDynamic(dfnhp[0], errhp, (dvoid *) &outpos,
                   (OCICallbackDefine) fetch_cbk);
 
  OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                 (const OCISnapshot*) 0, (OCISnapshot*) 0,
                 (ub4) OCI_DEFAULT);
  buf[ 4999 ] = '\0';
  printf("Select callback: Last piece: %s\n", buf);
}
 
/* -------------------------------------------------------------- */
/* Fetch callback to specify buffers. */
/* -------------------------------------------------------------- */
static sb4 fetch_cbk(dvoid *ctxp, OCIDefine *dfnhp, ub4 iter, dvoid **bufpp, 
                      ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcpp)
{
  static int a = 0;
  ub4 outpos = *((ub4 *)ctxp);
  ub4 len = 5000;
  switch(outpos)
  {
  case 1:
    a ++;
    *bufpp = (dvoid *) buf;
    *alenpp = &len;
    break;
  default:
    *bufpp = (dvoid *) 0;
    *alenpp = (ub4 *) 0;
    printf("ERROR: invalid position number: %d\n", outpos);
  }
  *indpp = (dvoid *) 0;
  *rcpp = (ub2 *) 0;
 
  buf[len] = '\0';
  if (a<=1)
  {
    *piecep = OCI_FIRST_PIECE;
    printf("Select callback: 0th piece\n");
  }
  else if (a<3)
  {
    *piecep = OCI_NEXT_PIECE;
    printf("Select callback: %d'th piece: %s\n", a-1, buf);
  }
  else {
    *piecep = OCI_LAST_PIECE;
    printf("Select callback: %d'th piece: %s\n", a-1, buf);
    a = 0;
  }
  return OCI_CONTINUE;
}

次の例に、コールバックによるピース単位フェッチを使用した、文字バッファへのLOB列の選択と、LOBデータ長のフェッチを示します。


#define MAX_BUF_SZ 1048576  /* Max allocation size = 1M */
char *buffer = NULL;
ub8   buf_len = 0;

/* Define callback function */
sb4 DefineCbk(void *cbctx, OCIDefine *defnhp, ub4 iter,
              void **bufp, ub4 **alenp, ub1 *piecep, 
              void **indp, ub2 **rcodep)
{
  static sword piece = 1;
  boolean isValidLen = FALSE;
  buf_len = 0;
 
  if (piece == 1) 
  { 
    OCIServerDataLengthGet(defnhp, &isValidLen, (ub8 *) &buf_len,
                           (OCIError *)cbctx, 0);

    if (buf_len > MAX_BUF_SZ)
      buf_len = MAX_BUF_SZ;

    buffer = (char *)malloc(buf_len);
    *bufp = buffer;
    *alenp = (ub4 *) &buf_len;
  }
  else
  {
    printf("Data = %s\n",buffer);
    buf_len = MAX_BUF_SZ;
  }
  piece++;
  return OCI_CONTINUE;
}

void define_callback()
{
  text      *sqlstmt = (text *)"select lobcol from lob_table";

  OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen( sqlstmt),
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
  OCIDefineByPos(stmthp, &defhp1, errhp, (ub4)1, (dvoid *)0,
                 (sb4) (10 * MAX_BUF_SZ), SQLT_STR, (dvoid *) 0,
                 (ub2 *) 0, (ub2 *) 0, (ub4)OCI_DYNAMIC_FETCH);
  OCIDefineDynamic(defhp1,errhp, errhp, 
                   (OCICallbackDefine)DefineCbk);
  OCIStmtExecute(svchp, stmthp, errhp, (ub4) 0, (ub4) 0,
                 (CONST OCISnapshot *) 0, (OCISnapshot *) 0,
                 (ub4) OCI_DEFAULT);
  OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT); 

  buffer[buf_len] = '\0';
  printf(" Data = %s\n",buffer);
  if (buffer)
    free(buffer);
}

8.4.6.4 配列フェッチ操作の実行

LOB用のデータ・インタフェースを使用してOCIで配列フェッチ操作を実行するには、この項で説明する方法のいずれかを使用します。

OCIStmtExecute()コールで、次に説明する方法をOCIDefineArrayOfStruct()とともに使用するか、iterの値を使用して反復回数(iter)を1より大きい値に指定します。配列の定義を使用する場合は、LOBデータは、単一ピース、ピース単位またはコールバックを使用してフェッチされるかどうかに関係なく、1回のラウンドトリップで複数行がフェッチされます。

次の例に、配列フェッチを使用した文字バッファへのLOB列の選択を示します。

void array_fetch()
{
  word i;
  text arrbuf[5][5000];
  text *selstmt = (text *) "SELECT Ad_sourcetext FROM Print_media WHERE
                  Product_id = 2004 AND Ad_id >=4";
 
  OCIStmtPrepare(stmthp, errhp, selstmt, (ub4)strlen((char *)selstmt),
                 (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
 
  OCIStmtExecute(svchp, stmthp, errhp, (ub4) 0, (ub4) 0,
                 (const OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT);
 
  OCIDefineByPos(stmthp, &defhp1, errhp, (ub4) 1,
                   (dvoid *) arrbuf[0], (sb4) sizeof(arrbuf[0]),
                   (ub2) SQLT_CHR, (dvoid *) 0,
                   (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT);
 
  OCIDefineArrayOfStruct(dfnhp1, errhp, sizeof(arrbuf[0]), indsk,
                         rlsk, rcsk);
 
  retval = OCIStmtFetch(stmthp, errhp, (ub4) 5,
                        (ub4) OCI_FETCH_NEXT, (ub4) OCI_DEFAULT);
  if (retval == OCI_SUCCESS || retval == OCI_SUCCESS_WITH_INFO)
  {
     printf("%.5000s\n", arrbuf[0]);
     printf("%.5000s\n", arrbuf[1]);
     printf("%.5000s\n", arrbuf[2]);
     printf("%.5000s\n", arrbuf[3]);
     printf("%.5000s\n", arrbuf[4]);
  }
}

8.4.7 OCIからのPL/SQLバインドおよびCバインド

この項では、LOBに関してOCIからのPL/SQLおよびCバインドについて学習します。

OCIからPL/SQLプロシージャをコールして、INバインドまたはOUTバインド(あるいはIN OUTバインド)を行う場合、次のいずれかを行えます。

  • PL/SQLプロシージャの仮パラメータがSQLT_CLOBである場合、変数をSQLT_CHRまたはSQLT_LNGとしてバインドする。

  • 仮パラメータがSQLT_BLOBである場合、変数をSQLT_BINまたはSQLT_LBIとしてバインドする。

次に、有効な2つの例を示します。

begin foo(:1); end;形式でのPL/SQLアウトバインドのコール

begin foo(:1); end;形式でPL/SQLアウトバインドをコールする例を次に示します。

text *sqlstmt = (text *)"BEGIN get_lob(:c); END; " ;

call foo(:1);形式でのPL/SQLアウトバインドのコール

call foo(:1);形式でPL/SQLアウトバインドをコールする例を次に示します。

text *sqlstmt = (text *)"CALL get_lob(:c);" ;

どちらの例でも、プログラムの後半には次の文が含まれます。

OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen((char *)sqlstmt),
               (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
   curlen = 0;
OCIBindByName(stmthp, &bndhp[3], errhp,
        (text *) ":c", (sb4) strlen((char *) ":c"),
        (dvoid *) buf5, (sb4) LONGLEN, SQLT_CHR,
        (dvoid *) 0, (ub2 *) 0, (ub2 *) 0,
        (ub4) 1, (ub4 *) &curlen, (ub4) OCI_DATA_AT_EXEC);

PL/SQLプロシージャget_lob()を次に示します。

procedure get_lob(c INOUT CLOB) is  -- This might have been column%type 
  BEGIN
  ... /* The procedure body could be in PL/SQL or C*/
  END;