Yet another OSS DB:Firebird(終)

ユーザー定義関数で日付時刻やBLOBを扱う

Firebird日本ユーザー会
アナハイムテクノロジー
はやしつとむ
2009/7/21

BLOB UDFのサンプル(1)

 それでは、BLOBを扱うUDFの例を見ていきましょう。まずは、Ian Newby氏作のBLOB UDFsから、一番簡単なサンプルを示します。

wmudflib.c(35)
long EXPORT fn_blob_length (ARG(BLOB, sourceBlob))
ARGLIST(BLOB sourceBlob) { 
  if (!sourceBlob->blob_handle) {
    return 0L;
  }
  return (sourceBlob->blob_total_length);  
}

 blob_handleがNULLであれば、0を返して抜けます。そうでない場合は、sourceBlobのblob_total_lengthを返すというものです。

 Delphiで同じことをやってみます。

library UDF_sample3;

uses
  SysUtils,
  IBExternals;

{$R *.res}

function tomneko_blob_length(SourceBlob:PBlob):Integer;stdcall;
begin
  Result := 0;
  if (SourceBlob^.BlobHandle^ = 0) then
    Exit
  else
    Result := SourceBlob^.TotalSize;
end;

exports
  tomneko_blob_length;

begin
end.

●登録用スクリプト:

DECLARE EXTERNAL FUNCTION tomneko_blob_length
BLOB
RETURNS INTEGER BY VALUE
ENTRY_POINT 'Tomneko_Blob_Length' 
MODULE_NAME 'UDF_sample3.dll';

●使用例:

SQL> SELECT TOMNEKO_BLOB_LENGTH(BLOB1) FROM T_BLOB;

TOM_BLOB_LENGTH
===============
          89334
          89334
          89334
          89334
          89334

BLOB UDFのサンプル(2)

 次に、同じくIan Newby氏作のBLOB UDFsから、BLOBから文字列への変換を行うUDFと、逆に文字列からBLOBを返すUDFを見てみます。

wmudflib.c(100)
char* EXPORT fn_blob_string(ARG(BLOB, sourceBlob), ARG(char*, sResult))
ARGLIST(BLOB sourceBlob)
ARGLIST(char *sResult) {
  long start;
  long end;
  start = 1;
  end = fn_blob_length(sourceBlob);
  fn_blob_substr(sourceBlob, &start, &end, sResult);
  return sResult;
}

  fn_blob_string()関数は、BLOBを文字列として返します。実際にBLOBに格納されているデータが文字列かどうかの判断はしていないので、注意が必要です。

 内容的には、先ほど示したfn_blob_length()関数を使ってblobの長さを得てから、fn_blob_substr()関数を利用して最初から最後までのデータを取り出しています。fn_blob_substr()関数は以下のような内容です。

wmudflib.c(47)
char* EXPORT fn_blob_substr(ARG(BLOB, sourceBlob), ARG(long*, startPos), ARG(long*, endPos), ARG(char*, sResult))
ARGLIST(BLOB sourceBlob)
ARGLIST(long *startPos)
ARGLIST(long *endPos)
ARGLIST(char *sResult) {
  char *pbuffer, *pOffset, *pResultOffset;
  long i = 0;
  long curr_bytecount = 0;
  long startChar, endChar; 
  long length, actual_length;
  *sResult = 0;
  if (!sourceBlob->blob_handle) {
    return sResult;
  }
  length = sourceBlob->blob_max_segment;
  if (*startPos > *endPos || *startPos < 1L || *endPos < 1L)  {
    return sResult;
  }
  if (sourceBlob->blob_total_length < (long)*startPos) {
    return sResult;
  }
  startChar = *startPos;
  if (sourceBlob->blob_total_length < (long)*endPos) {
    endChar = sourceBlob->blob_total_length;
  } else {
    endChar = *endPos;
  }
  pbuffer = (char *) malloc (length + 1L); 
  pResultOffset = sResult;
  while ((*sourceBlob->blob_get_segment) (sourceBlob->blob_handle, pbuffer, length, &actual_length)) {    
//    pbuffer [actual_length] = 0;
    pOffset = pbuffer;
    while (*pOffset && (curr_bytecount < endChar)) {
      curr_bytecount++;
      if (curr_bytecount >= startChar) {
        *pResultOffset++ = *pOffset;
      }
      pOffset++;
    }
    if (curr_bytecount >= endChar) {
      *pResultOffset = 0;
      break;
    }
  }
  free (pbuffer);
  return sResult;
}

 fn_blob_substr()関数では、引数のチェックを行ったあと、sourceBlobで渡されたBLOB制御構造体のblob_get_segmentを利用して、BLOBのデータを取り出し、sResultで渡される戻り値へコピーを行っています。blog_get_segment()はBLOBを最後まで読み出すと0を返すので、それを見ながらループすればいいわけです。

wmudflib.c(153)
   BLOB EXPORT fn_string_blob(ARG(char*, sourceString), ARG(BLOB, sResult))
   ARGLIST(char *sourceString) 
   ARGLIST(BLOB sResult) {
 (*sResult->blob_put_segment) (sResult->blob_handle, sourceString, strlen(sourceString));
 return sResult;
 }

 fn_string_blob()関数は、単純に、BLOB制御構造体のblob_put_segment()関数を利用して、BLOBにデータを書き込んでFirebirdへ返しているだけです。

 それでは、これらを先ほどのUDF_sample3に追加してみましょう。Delphi2009に対応するため、PCharではなくPAnsiCharを使用し、必要な個所でPByteへのキャストを行っています。C/C++からのポーティングは多少面倒になってしまったようです。

library UDF_sample3;

uses
  SysUtils,
  IBExternals;

{$R *.res}

function Tomneko_Blob_Length(SourceBlob:PBlob):Integer;stdcall;
begin
  Result := 0;
  if (SourceBlob^.BlobHandle^ = 0) then
    Exit
  else
    Result := SourceBlob^.TotalSize;
end;

Procedure Tomneko_Blob_substr(SourceBlob:PBlob; var startPos, endPos:Integer; sResult:PAnsiChar);stdcall;
var
  pbuffer, pResultOffset, pOffset:PAnsiChar;
  curr_bytecount, startChar, endChar, length, actual_length:Integer;
begin
  curr_bytecount := 0;
  sResult^ := #0;
  if (sourceBlob^.BlobHandle^ = 0) then
  begin
    Exit;
  end;
  length := sourceBlob^.MaxSegmentLength;

  if ((startPos > endPos) or (startPos < 1) or (endPos < 1)) then
  begin
    exit;
  end;
  if (sourceBlob^.TotalSize < startPos) then
  begin
    exit;
  end;
  startChar := startPos;

  if (sourceBlob^.TotalSize < endPos) then
  begin
    endChar := sourceBlob^.TotalSize;
  end else
  begin
    endChar := endPos;
  end;

  GetMem(pbuffer, length + 1);

  pResultOffset := sResult;
  while (sourceBlob^.GetSegment(sourceBlob^.BlobHandle, PByte(pbuffer), length, actual_length) <> 0) do
  begin
    pOffset := pbuffer;
    while ((pOffset <> #0) and (curr_bytecount < endChar )) do
    begin
      Inc(curr_bytecount);
      if (curr_bytecount >= startChar ) then
      begin
        pResultOffset^ := pOffset^;
        inc(pResultOffset);
      end;
      Inc(pOffset);
      if (curr_bytecount = actual_length) then Break;
    end;

    if (curr_bytecount >= endChar) then
    begin
      pResultOffset^ := #0;
      break;
    end;
  end;
  freeMem(pbuffer);
  exit;
end;

Procedure Tomneko_Blob_string(sourceBlob:PBlob; sResult:PAnsiChar);stdcall;
var
  startPos, endPos:Integer;
begin
  startPos := 1;
  endPos := Tomneko_blob_length(sourceBlob);
  Tomneko_blob_substr(sourceBlob, startPos, endPos, sResult);
end;

Procedure Tomneko_string_Blob(sourceString:PAnsiChar; sResult:PBlob);stdcall;
begin
  sResult^.PutSegment(sResult^.BlobHandle, PByte(sourceString), strlen(PChar(sourceString)));
end;

exports
  Tomneko_Blob_Length,
  Tomneko_Blob_substr,
  Tomneko_Blob_string,
  Tomneko_string_Blob;

end.

登録用スクリプト:

DECLARE EXTERNAL FUNCTION TOMNEKO_BLOB_SUBSTR
  BLOB, 
  INTEGER, 
  INTEGER, 
  CSTRING(256) CHARACTER SET UTF8
  RETURNS PARAMETER 4
ENTRY_POINT 'Tomneko_Blob_substr' 
MODULE_NAME 'UDF_sample3';

DECLARE EXTERNAL FUNCTION TOMNEKO_BLOB_TO_STRING
  BLOB, 
  CSTRING(256) CHARACTER SET ASCII
  RETURNS PARAMETER 2
ENTRY_POINT 'Tomneko_Blob_string' 
MODULE_NAME 'UDF_sample3';

DECLARE EXTERNAL FUNCTION TOMNEKO_STRING_TO_BLOB
  CSTRING(32000) CHARACTER SET ASCII, 
  BLOB
  RETURNS PARAMETER 2
ENTRY_POINT 'Tomneko_string_Blob' 
MODULE_NAME 'UDF_sample3';

●使用例:

SQL> CREATE TABLE T_BLOB (BLOB1 BLOB SUB_TYPE 1);
SQL> INSERT INTO T_BLOB 
VALUES (TOMNEKO_STRING_TO_BLOB('TOMNEKO'));
SQL> SELECT TOMNEKO_BLOB_TO_STRING(BLOB1) FROM T_BLOB;
TOMNEKO_BLOB_TO_STRING
==================
TOMNEKO
前のページへ 3/4 次のページへ

Index
ユーザー定義関数で日付時刻やBLOBを扱う

Page 1
Firebirdでの日付時刻型の扱い

Page 2
EncodeDate関数
UDFでBLOBを扱う
→ Page 3
BLOB UDFのサンプル(1)
BLOB UDFのサンプル(2)

Page 4
最後に

Yet another OSS DB:Firebird


Database Expert フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Database Expert 記事ランキング

本日月間