山科 氷魚 (YAMASHINA Hio)
and 1 contributors

NAME

Tripletail::DB - DBIのラッパ

SYNOPSIS

  $TL->startCgi(
      -DB      => 'DB',
      -main        => \&main,
  );
  
  sub main {
    my $DB = $TL->getDB('DB');
    
    $DB->setDefaultSet('R_Trans');
    $DB->tx(sub{
      my $sth = $DB->execute(q{SELECT a, b FROM foo WHERE a = ?}, 999);
      while (my $hash = $sth->fetchHash) {
        print $hash->{a};
      }
      # commit is done implicitly.
    });
    
    $DB->tx('W_Trans' => sub{
      $DB->execute(q{UPDATE counter SET counter = counter + 1 WHERE id = ?}, 1);
      $DB->commit; # can commit explicitly.
    }
  }

DESCRIPTION

接続/切断は自動で行われる。

手動で接続/切断する場合は、connect/disconnectを使う。

実行クエリの処理時間・実行計画・結果を記録するデバッグモード。
prepare/executeを分けない。fetchは分ける。
拡張プレースホルダ機能
  $db->execute(q{select * from a where mode in (??)}, ['a', 'b'])

と記述すると、

  $db->execute(q{select * from a where mode in (?, ?)}, 'a', 'b')

のように解釈される。

プレースホルダの値渡しの際に型指定が可能
  $db->execute(q{select * from a limit ??}, ['a', \'SQL_INTEGER'])
リクエスト処理完了後のトランザクション未完了やunlock未完了を自動検出
DBグループ・DBセット・DBコネクション

Tripletail::DBでは、レプリケーションを利用してロードバランスすることを支援するため、 1つのDBグループの中に、複数のDBセットを定義することが可能となっている。 DBセットの中には、複数のDBコネクションを定義できる。

更新用DBセット、参照用DBセット、などの形で定義しておき、プログラム中で トランザクション単位でどのDBセットを使用するか指定することで、 更新用クエリはマスタDB、参照用クエリはスレーブDB、といった 使い分けをすることが可能となる。

DBセットには複数のDBコネクションを定義でき、複数定義した場合は プロセス単位でプロセスIDを元に1つのコネクションが選択される。 (プロセスIDを定義数で割り、その余りを使用して決定する。)

同じDBグループの中の複数のDBセットで同じDBコネクション名が使用された場合は、 実際にDBに接続されるコネクション数は1つとなる。 このため、縮退運転時に参照用DBセットのDBコネクションを更新用の ものに差し替えたり、予め将来を想定して多くのDBセットに分散 させておくことが可能となっている。

DBセットの名称はSET_XXXX(XXXXは任意の文字列)でなければならない。 DBコネクションの名称はCON_XXXX(XXXXは任意の文字列)でなければならない。

DBIからの移行

TripletailのDBクラスはDBIに対するラッパの形となっており、多くのインタフェースはDBIのものとは異なる。 ただし、いつでも $DB->getDbh() メソッドにより元のDBIオブジェクトを取得できるので、DBIのインタフェースで利用することも可能となっている。

DBIのインタフェースは以下のようなケースで利用できる。ただし、DBIを直接利用する場合は、TLの拡張プレースホルダやデバッグ機能、トランザクション整合性の管理などの機能は利用できない。

ラッパに同等の機能が用意されていない場合。

(例)$DB->{mysql_insertid}

高速な処理が必要で、ラッパのオーバヘッドを回避したい場合。

DBIに対するラッパであるため、大量のSQLを実行する場合などはパフォーマンス上のデメリットがある。

DBIでのSELECTは、以下のように置き換えられる。

 # DBI
 my $sth = $DB->prepare(q{SELECT * FROM test WHERE id = ?});
 $sth->execute($id);
 while(my $data = $sth->fetchrow_hashref) {
 }
 # TL
 my $sth = $DB->execute(q{SELECT * FROM test WHERE id = ?}, $id);
 while(my $data = $sth->fetchHash) {
 }

TLではprepare/executeは一括で行い、prepared statement は利用できない。

INSERT・UPDATEは、以下のように置き換えられる。

 # DBI
 my $sth = $DB->prepare(q{INSERT INTO test VALUES (?, ?)});
 my $ret = $sth->execute($id, $data);
 # TL
 my $sth = $DB->execute(q{INSERT INTO test VALUES (?, ?)}, $id, $data);
 my $ret = $sth->ret;

prepare/executeを一括で行うのは同様であるが、executeの戻り値は$sthオブジェクトであり、影響した行数を取得するためには $sth->ret メソッドを呼ぶ必要がある。

プレースホルダの型指定は以下のように行う。

 # DBI
 my $sth = $DB->prepare(q{SELECT * FROM test LIMIT ?});
 $sth->bind_param(1, $limit, { TYPE => SQL_INTEGER });
 $sth->execute;
 # TL
 my $sth = $DB->execute(q{SELECT * FROM test LIMIT ??}, [$limit, \'SQL_INTEGER']);

TLの拡張プレースホルダ(??で表記される)を利用し、配列のリファレンスの最後に型をスカラのリファレンスの形で渡す。 拡張プレースホルダでは、複数の値を渡すことも可能である。

 # DBI
 my $sth = $DB->prepare(q{SELECT * FROM test LIMIT ?, ?});
 $sth->bind_param(1, $limit, { TYPE => SQL_INTEGER });
 $sth->bind_param(2, $offset, { TYPE => SQL_INTEGER });
 $sth->execute;
 # TL
 my $sth = $DB->execute(q{SELECT * FROM test LIMIT ??}, [$limit, $offset, \'SQL_INTEGER']);

最後にINSERTした行のAUTO_INCREMENT値の取得などは、拡張ラッパでは制御できないので、DBIのハンドラを直接利用する。

 # DBI
 my $id = $DB->{mysql_insertid};
 # TL
 my $id = $DB->getDbh()->{mysql_insertid};

トランザクションには $DB->tx(sub{...}) メソッドを用いる。 DBセットを指定する時には $DB->tx(dbset_name=>sub{...}) となる。 渡したコードをトランザクション内で実行する。 die なしにコードを抜けた時に自動的にコミットされる。 途中で die した場合にはトランザクションはロールバックされる。

 # DBI
 $DB->do(q{BEGIN WORK});
 #   do something.
 $DB->commit;
 
 # TL
 $DB->tx(sub{
   # do something.
 });

begin() メソッドも実装はされているがその使用は非推奨である。 また、 $DB->execute(q{BEGIN WORK}); は無効にされている。

拡張プレースホルダ詳細

"execute" に渡されるSQL文には、通常のプレースホルダの他に、 拡張プレースホルダ "??" を埋め込む事が出来る。 拡張プレースホルダの置かれた場所には、パラメータとして通常のスカラー値でなく、 配列へのリファレンスを与えなければならない。配列が複数の値を持っている場合には、 それらが通常のプレースホルダをカンマで繋げたものに展開される。

例: 以下の二文は等価

  $DB->execute(
      q{SELECT * FROM a WHERE a IN (??) AND b = ?},
      ['AAA', 'BBB', 'CCC'], 800);
  $DB->execute(
      q{SELECT * FROM a WHERE a IN (?, ?, ?) AND b = ?},
      'AAA', 'BBB', 'CCC', 800);

パラメータとしての配列の最後の項目が文字列へのリファレンスである時、その文字列は SQL型名として扱われる。配列が複数の値を持つ時には、その全ての要素に対して 型指定が適用される。型名はDBI.pmで定義される。

例:

  $DB->execute(q{SELECT * FROM a LIMIT ??}, [20, \'SQL_INTEGER']);
  ==> SELECT * FROM a LIMIT 20
  
  $DB->execute(q{SELECT * FROM a LIMIT ??}, [20, 5, \'SQL_INTEGER']);
  ==> SELECT * FROM a LIMIT 20, 5

配列内の要素を更に2要素の配列とし、二番目の要素を文字列へのリファレンスと する事で、要素の型を個別に指定出来る。

例:

  $DB->execute(
      q{SELECT * FROM a WHERE a IN (??) AND b = ?},
      [[100, \'SQL_INTEGER'], 'foo', \'SQL_VARCHAR'], 800);
  ==> SELECT * FROM a WHERE a IN (100, 'foo') AND b = '800'

METHODS

Tripletail::DB メソッド

$TL->getDB
   $DB = $TL->getDB
   $DB = $TL->getDB($inigroup)

Tripletail::DB オブジェクトを取得。 引数にはIniで設定したグループ名を渡す。 引数省略時は 'DB' グループが使用される。

$TL->startCgi / $TL->trapError の関数内でDBオブジェクトを取得する場合に使用する。

$TL->newDB
   $DB = $TL->newDB
   $DB = $TL->newDB($inigroup)

新しく Tripletail::DB オブジェクト作成。 引数にはIniで設定したグループ名を渡す。 引数省略時は 'DB' グループが使用される。

動的にコネクションを作成したい場合などに使用する。 この方法で Tripletail::DB オブジェクトを取得した場合、"connect" / "disconnect" を呼び出し、接続の制御を行う必要がある。

connect

DBに接続する。

$TL->startCgi / $TL->trapError の関数内でDBオブジェクトを取得する場合には自動的に接続が管理されるため、このメソッドを呼び出してはならない。

$TL->newDB で作成した Tripletail::DB オブジェクトに関しては、このメソッドを呼び出し、DBへ接続する必要がある。

connect時には、AutoCommit 及び RaiseError オプションは 1 が指定され、PrintError オプションは 0 が指定される。

disconnect

DBから切断する。

$TL->startCgi / $TL->trapError の関数内でDBオブジェクトを取得する場合には自動的に接続が管理されるため、このメソッドを呼び出してはならない。

$TL->newDB で作成した Tripletail::DB オブジェクトに関しては、このメソッドを呼び出し、DBへの接続を切断する必要がある。

tx
  $DB->tx(sub{...})
  $DB->tx('SET_W_Trans' => sub{...})

指定されたDBセット名でトランザクションを開始し、その中でコードを 実行する。トランザクション名(DBセット名) はiniで定義されていな ければならない。名前を省略した場合は、デフォルトのDBセットが使われるが、 setDefaultSetによってデフォルトが選ばれていない場合には例外を発生させる。

コードを die なしに終了した時にトランザクションは暗黙にコミットされる。 die した場合にはロールバックされる。 コードの中で明示的にコミット若しくはロールバックを行うこともできる。 明示的にコミット若しくはロールバックをした後は、 tx を抜けるまで DB 操作は禁止される。 この間の DB 操作は例外を発生させる。

rollback
  $DB->rollback

現在実行中のトランザクションを取り消す。

commit
  $DB->commit

現在実行中のトランザクションを確定する。

inTx
  $DB->inTx() and die "double transaction";
  $DB->inTx('SET_W_Trans') or die "transaction required";

既にトランザクション中であるかを確認する。 既にトランザクション中であれば真を、 他にトランザクションが走っていなければ偽を返す。 トランザクションの指定も可能。 異なるDBセット名のトランザクションが実行中だった場合には 例外を発生させる。

begin
  $DB->begin
  $DB->begin('SET_W_Trans')

非推奨。"tx" を使用のこと。

指定されたDBセット名でトランザクションを開始する。トランザクション名 (DBセット名) はiniで定義されていなければならない。 名前を省略した場合は、デフォルトのDBセットが使われるが、 setDefaultSetによってデフォルトが選ばれていない場合には例外を発生させる。

CGIの中でトランザクションを開始し、終了せずにMain関数を抜けた場合は、自動的に rollbackされる。

トランザクション実行中にこのメソッドを呼んだ場合には、例外を発生させる。 1度に開始出来るトランザクションは、1つのDBグループにつき1つだけとなる。

setDefaultSet
  $DB->setDefaultSet('SET_W_Trans')

デフォルトのDBセットを選択する。ここで設定されたDBセットは、引数無しのbegin() や、beginせずに行ったexecuteの際に使われる。このメソッドは Main関数 の先頭で呼ばれる事を想定している。

execute
  $DB->execute($sql, $param...)
  $DB->execute(\'SET_W_Trans' => $sql, $param...)

SELECT/UPDATE/DELETEなどのSQL文を実行する。 第1引数にSQL、第2引数以降にプレースホルダの引数を渡す。 ただし、第1引数にリファレンスでDBセットを渡すことにより、 トランザクション外での実行時にDBセットを指定することが可能。

第2引数以降の引数では、拡張プレースホルダが使用できる。 "拡張プレースホルダ詳細" を参照。

既にトランザクションが実行されていれば、そのトランザクションの DBセットでSQLが実行される。

トランザクションが開始されておらず、かつ "lock" により テーブルがロックされていれば、ロックをかけているDBセットでSQLが実行される。

いずれの場合でもない場合は、"setDefaultSet" で指定された トランザクションが使用される。 "setDefaultSet" による設定がされていない場合は、例外を発生させる。

このメソッドを使用して、LOCK/UNLOCK/BEGIN/COMMITといったSQL文を 実行してはならない。実行しようとした場合は例外を発生させる。 代わりに専用のメソッドを使用する事。

selectAllHash
  $DB->selectAllHash($sql, $param...)
  $DB->selectAllHash(\'SET_W_Trans' => $sql, $param...)

SELECT結果をハッシュの配列へのリファレンスで返す。 データがない場合は [] が返る。

  my $arrayofhash = $DB->selectAllHash($sql, $param...);
  foreach my $hash (@$arrayofhash){
     $TL->log(DBDATA => "name of id $hash->{id} is $hash->{name}");
  }
selectAllArray
  $DB->selectAllArray($sql, $param...)
  $DB->selectAllArray(\'SET_W_Trans' => $sql, $param...)

SELECT結果を配列の配列へのリファレンスで返す。 データがない場合は [] が返る。

  my $arrayofarray = $DB->selectAllArray($sql, $param...);
  foreach my $array (@$arrayofarray){
     $TL->log(DBDATA => $array->[0]);
  }
selectRowHash
  $DB->selectRowHash($sql, $param...)
  $DB->selectRowHash(\'SET_W_Trans' => $sql, $param...)

SELECT結果の最初の1行をハッシュへのリファレンスで返す。 実行後、内部でfinishする。 データがない場合は undef が返る。

  my $hash = $DB->selectRowHash($sql, $param...);
  $TL->log(DBDATA => "name of id $hash->{id} is $hash->{name}");
selectRowArray
  $DB->selectRowArray($sql, $param...)
  $DB->selectRowArray(\'SET_W_Trans' => $sql, $param...)

SELECT結果の最初の1行を配列へのリファレンスで返す。 実行後、内部でfinishする。 データがない場合は undef が返る。

  my $array = $DB->selectRowArray($sql, $param...);
  $TL->log(DBDATA => $array->[0]);
lock
  $DB->lock(set => 'SET_W_Trans', read => ['A', 'B'], write => 'C')

指定されたDBセットに対してLOCK TABLESを実行する。setが省略された場合はデフォルト のDBセットが選ばれる。CGIの中でロックした場合は、 Main関数 を抜けた時点で自動的にunlockされる。

ロック実行中にこのメソッドを呼んだ場合には、例外を発生させる。 1度に開始出来るロックは、1つのDBグループにつき1つだけとなる。

unlock
  $DB->unlock

UNLOCK TABLES を実行する。 ロックがかかっていない場合は例外を発生させる。

setBufferSize
  $DB->setBufferSize($kbytes)

バッファサイズをKB単位でセットする。行を1行読み込んだ結果 このサイズを上回る場合、dieする。 0 または undef をセットすると、制限が解除される。

symquote
  $DB->symquote($sym)

文字列を識別子としてクォートする。

mysqlの場合は `a b c` となり、それ以外の場合は "a b c" となる。

getType
  $DB->getType;

DBのタイプを返す。(mysql, pgsql, ...)

getDbh
  $dbh = $DB->getDbh
  $dbh = $DB->getDbh('SET_W_Trans')

DBセット内のDBハンドルを返す。 返されるオブジェクトは DBI ネイティブのdbhである。

ネイティブのDBハンドルを使用してクエリを発行した場合、デバッグ機能(プロファイリング等)の機能は使用できません。 また、トランザクションやロック状態の管理もフレームワークで行えなくなるため、注意して使用する必要があります。

getLastInsertId
  $id = $DB->getLastInsertId()

セッション内の最後の自動採番の値を取得.

Tripletail::DB::STH メソッド

fetchHash
  $sth->fetchHash

ハッシュへのリファレンスで1行取り出す。

fetchArray
  $sth->fetchArray

配列へのリファレンスで1行取り出す。

ret
  $sth->ret

最後に実行した execute の戻り値を返す。

rows
  $sth->rows

DBIと同様。

finish
  $sth->finish

DBIと同様。

nameArray
  $sth->nameArray

$sth->{NAME_lc} を返す。

nameHash
  $sth->nameHash

$sth->{NAME_lc_hash} を返す。

Ini パラメータ

DBセット・DBコネクション

DBグループのパラメータのうち、半角小文字英数字のみで構成された パラメータは予約済みで、DBグループの動作設定に使用する。 DBセットは、予約済みではない名前であれば任意の名称が使用でき、 値としてDBコネクションのINIグループ名をカンマ区切りで指定する。

例:

  [DB]
  namequery=1
  type=mysql
  defaultset=SET_R_Trans
  SET_W_Trans=CON_DBW1
  SET_R_Trans=CON_DBR1,CON_DBR2
  
  [CON_DBW1]
  dbname=test
  user=daemon
  host=192.168.0.100
  
  [CON_DBR1]
  dbname=test
  user=daemon
  host=192.168.0.110
  
  [CON_DBR2]
  dbname=test
  user=daemon
  host=192.168.0.111

以下は特別なパラメータ:

namequery
  namequery = 1

これを1にすると、実行しようとしたクエリのコマンド名の直後に /* foo.pl:111 [DB.R_Transaction1.DBR1] */ のようなコメントを挿入する。 デフォルトは0。

type
  type = mysql

DBの種類を選択する。 mysql, pgsql, oracle, sqlite, mssql が使用可能。 必須項目。

defaultset
  defaultset = SET_W_Trans

デフォルトのDBセットを設定する。 ここで設定されたDBセットは、引数無しのbegin()や、beginせずに行ったexecuteの際に使われる。

DB定義

dbname
  dbname = test

DB名を設定する。

host
  host = localhost

DBのアドレスを設定する。 デフォルトはlocalhost。

user
  user = www

DBに接続する際のユーザー名を設定する。

password
  password = PASS

DBに接続する際のパスワードを設定する。 省略可能。

SQL Server 設定

試験的に SQL Server との接続が実装されています. DBD::ODBC と, Linux であれば unixODBC + freetds で, Windows であれば 組み込みの ODBC マネージャで動作します.

設定例:

 # <tl.ini>
 [DB]
 type=mssql
 defaultset=SET_W_Trans
 SET_W_Trans=CON_RW
 [CON_RW]
 # dbname に ODBC-dsn を設定.
 dbname=test
 user=test
 password=test
 # freetds経由の時は, そちらのServernameも指定.
 tdsname=tds_test

freetdsでの接続文字コードの設定は freetds.conf で 設定します.

 ;; <freetds.conf>
 [tds_test]
 host = 10.0.0.1
 ;;port = 1433
 tds version = 7.0
 client charset = UTF-8

SEE ALSO

Tripletail

AUTHOR INFORMATION

    Copyright 2006 YMIRLINK Inc. All Rights Reserved.

    This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself

    このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。

    Address bug reports and comments to: tl@tripletail.jp

    HP : http://tripletail.jp/