このサンプル コードでは、テーブルclob_tblについて見ていきます。定義とデータは次のとおりです。
CREATE TABLE clob_tbl(id INTEGER, clob_column_2 CLOB(20)) PRIMARY INDEX(id); INSERT clob_tbl(1, 'First 3 New Rows'); INSERT clob_tbl(2, 'Second 3 New Rows');
テーブル関数ClobTblは次のようにして作成されています。
CALL SQLJ.INSTALL_JAR('CJ!ClobFunctions.jar', 'ClobFunctions',0);
REPLACE FUNCTION ClobTbl(in_parm CLOB(20))
RETURNS TABLE (id INTEGER,
clobcontent VARCHAR(20))
LANGUAGE JAVA
NO SQL
PARAMETER STYLE JAVA
EXTERNAL NAME 'ClobFunctions:ClobFunctions.ClobTbl';
関数ClobTbl()のソース コードは次のとおりです。次のコメント部分は、コードの中で説明する部分を示しています。
| コメント | 詳細 |
|---|---|
/** PROBLEM **/ ... /** END PROBLEM **/ |
これらのコメントは、local_ctx_clobクラスのオブジェクトであるclob_infoのインスタンスを作成し、allocCtx(clob_info)を呼び出して記憶域を割り当てるコードを分離しています。メソッドallocCtx()はclob_infoをバイナリ ストリームにシリアライズし、バイナリ ストリームと同じサイズの記憶域を予約します。クラスlocal_ctx_clobにはjava.sql.Clob[]型の配列があります。ただし、TBL_PRE_INITフェーズでは、java.sql.Clob[]配列にあるすべてのエントリはデフォルトでNULLになります。このため、allocCtx()の呼び出しによりclob_infoがシリアライズされると、シリアライズするjava.sql.Clobオブジェクトは無くなります。これにより、必要以上に細かくシリアライズされたバイナリ ストリームが作成されます。このようにして、割り当てられた記憶域も必要以上に小さくなります。その後、TBL_INITフェーズで最初のlobデータが処理されると、UDFが予約された記憶域にある以上のデータを書き出そうとするため、Tbl.setCtxObject()を呼び出すとエラーになります。 |
/** SOLUTION **/ ... /** END SOLUTION **/ |
これらのコメントは、問題を解決するコードを分離します。このコードは各item_clobオブジェクトをチェックし、メンバーのmyclobがNULLの場合には、ダミーのバイト アレイをシリアライズされたバイナリ ストリームに書き出して、後で利用する際に十分に大きなサイズになるようにします。ダミーのバイト アレイのサイズは64です。このサイズは、完全にシリアライズされたTeradataのjava.sql.Clobとjava.sql.Blobオブジェクトのサイズです。 |
import java.io.*;
import java.sql.*;
import com.teradata.fnc.*;
class item_clob implements Serializable
{
int id;
java.sql.Clob myclob;
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
{
byte[] dummy = new byte[64];
out.defaultWriteObject();
/************************* SOLUTION *************************/
/* Allocate storage for lob by writing the dummy bytes */
/* into its serialized binary stream if myclob is null. */
if (myclob == null)
out.write(dummy);
/*********************** END SOLUTION ***********************/
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
}
}
class local_ctx_clob implements Serializable
{
int Num_Row;
int Cur_Row;
item_clob[] Clob_List;
public local_ctx_clob(){}
public local_ctx_clob(int NR, int CR, item_clob[] Clob_List)
{
this.Num_Row=NR;
this.Cur_Row=CR;
this.Clob_List=Clob_List;
}
}
public class ClobFunctions
{
static int cnt = 0;
/* ClobTbl uses each lob value in clob_tbl.clob_column_2 */
/* to generate 3 new rows for the return table. */
public static void ClobTbl( java.sql.Clob in_parm, int[] id,
String[] clobcontent )
throws SQLException
{
local_ctx_clob clob_info;
int[] phase = new int[1];
Tbl tbl = new Tbl();
try
{
switch (tbl.getPhase(phase))
{
case Tbl.TBL_MODE_CONST:
throw new SQLException("not for const mode", "U0006");
case Tbl.TBL_MODE_VARY:
switch(phase[0])
{
case Tbl.TBL_PRE_INIT:
{
/******************* PROBLEM *******************/
/* Allocate storage space to hold intermediate */
/* data which is used to generate 3 new rows. */
item_clob[] clob_arr = new item_clob[3];
for( int i=0; i<clob_arr.length; i++)
clob_arr[i]=new item_clob();
clob_info = new local_ctx_clob(3, 0, clob_arr);
tbl.allocCtx(clob_info);
/***************** END PROBLEM *****************/
}
break;
case Tbl.TBL_INIT:
{
/* Each lob passed in is stored in an */
/* intermediate structure that is used to */
/* construct 3 new rows in TBL_BUILD phase. */
item_clob[] clob_arr = new item_clob[3];
for( int i=0; i<clob_arr.length; i++) {
clob_arr[i] = new item_clob();
clob_arr[i].myclob = in_parm;
if (in_parm != null) clob_arr[i].id = cnt++;
}
clob_info = new local_ctx_clob(3, 0, clob_arr);
/* Store the intermediate data */
tbl.setCtxObject(clob_info);
}
break;
case Tbl.TBL_BUILD:
{
/* Get intermediate data stored in TBL_INIT */
/* phase and use it to generate 3 rows */
clob_info = (local_ctx_clob)tbl.getCtxObject();
if ( clob_info.Cur_Row >= clob_info.Num_Row ||
clob_info.Clob_List[0].myclob == null) {
throw new SQLException("no more data","02000");
} else {
id[0] =
clob_info.Clob_List[clob_info.Cur_Row].id;
Reader R = clob_info.Clob_List[clob_info.Cur_Row].myclob.getCharacterStream();
char[] chars = new char[20];
R.read(chars);
R.close();
clobcontent[0] = (new String(chars)).trim();
clob_info.Cur_Row++;
}
tbl.setCtxObject(clob_info);
}
break;
case Tbl.TBL_FINI:
clob_info = (local_ctx_clob)tbl.getCtxObject();
clob_info.Cur_Row = 0;
clob_info.Num_Row = 0;
clob_info.Clob_List= null;
break;
case Tbl.TBL_END:
clob_info = (local_ctx_clob)tbl.getCtxObject();
break;
case Tbl.TBL_ABORT:
clob_info = (local_ctx_clob)tbl.getCtxObject();
break;
}
return;
}
}catch(ClassNotFoundException e){
throw new SQLException("Class not located Error", "U0006");
}catch(StreamCorruptedException e){
throw new SQLException("Stream Corrupt Error", "U0006");
}catch(IOException e){
e.printStackTrace();
}
}
}
テーブルclob_tblの内容は次のとおりです。
BTEQ -- Enter your DBC/SQL request or BTEQ command:
sel * from clob_tbl;
*** Query completed. 2 rows found. 2 columns returned.
*** Total elapsed time was 1 second.
id clob_column_2
----------- --------------------
1 First 3 New Rows
2 Second 3 New Rows
ソリューション コードを指定せずにClobTbl()を呼び出すと、エラーになります。
BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT new_tbl.id, new_tbl.clobcontent
FROM TABLE (ClobTbl(clob_tbl.clob_column_2)) AS new_tbl
ORDER by new_tbl.id;
*** Failure 7828 Unexpected Java Exception SQLSTATE 38000: An
java.lang.Error (Tbl.setCtxObject failed because object was larger
than general scratchpad.) exception was thrown.
Statement# 1, Info =0
*** Total elapsed time was 3 seconds.
ソリューション コードを指定してClobTbl()を呼び出すと、正しい答えが得られます。
BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT new_tbl.id, new_tbl.clobcontent
FROM TABLE (ClobTbl(clob_tbl.clob_column_2)) AS new_tbl
ORDER by new_tbl.id;
*** Query completed. 6 rows found. 2 columns returned.
*** Total elapsed time was 1 second.
id clobcontent
----------- --------------------
0 First 3 New Rows
1 First 3 New Rows
2 First 3 New Rows
3 Second 3 New Rows
4 Second 3 New Rows
5 Second 3 New Rows