BlogもどきのWeblog

備忘録と日々の呟きとメモとCGIの実験場とされる何か。
プロフィール

GLANSHE
絵描き担当らしい

Total: 14421
Today: 17
Yesterday: 81
最新の記事

コンテンツ

最新のコメント

最新のトラックバック

カテゴリー

リンク

ニコ動MyListLink

Perl + DBI + oracleで詰まった話
LOBというデータ型があるのですが、これを普通に取得しようとすると失敗する事が多いです。

DBD::Oracle::st fetchrow_hashref failed: ERROR fetching field 3 of 5. LOB value truncated from 829 to 80. DBI attribute LongReadLen too small and/or LongTruncOk not set [for Statement "select * from test_table"]

何を言ってるかというと、LOB型のデータ829バイトを80バイトに切り詰めようとして失敗してるそうです。
DBIの属性である「LongReadLen」の値が小さすぎ、もしくは「LongTrunkOk」が未設定である事が原因だそうです。

何で切り詰めようとしてるのかは、大して調べなかったのでわからないのですが、とにかく切り詰めるのをやめさせればよさそうです。

切り詰めるサイズは、データベースハンドルのLongReadLen属性に入っているので、これを大きくしてやればいいっぽいです。

# バイト数で指定
$dbh->{LongReadLen} = 1024 * 100;

参考
DBIではBLOBデータをどのように扱えばよいのですか?
DBD::Oracle


time stamp:2010/03/31 22:41:15
トラックバック(0)|コメント(0)
コメントを書く
この記事のトラックバックURL:

DBD::Oracle
PerlからOracleに接続するためのモジュールDBD::Oracleの話。

PerlからDBに接続するためには、基本的にDBIモジュールを使います。
接続するDBによって、 DBD::mysql とか、 DBD::pg とか DBD::DB2 とかがありますが、当然 DBD::Oracle なんてのもあるわけです。

今回、フリーの Oracle 10g Express Edition をインストールしてPerlから接続しようと、DBD::Oracleモジュールをインストールしようとしてまたハマりました。


とりあえずやったこと
# cpan

cpan[1]> install DBI

問題なく終了

cpan[2]> install DBD::Oracle

Trying to find an ORACLE_HOME
Your LD_LIBRARY_PATH env var is set to ''

ORACLE_HOMEが設定されていないと怒られる。
oracle関連の環境変数が設定されてないと駄目らしい。

仕方がないので、環境変数を設定する。

# export ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server
# export ORACLE_SID=XE
# export NLS_LANG=JAPANESE_JAPAN.AL32UTF8

# env
NLS_LANG=JAPANESE_JAPAN.AL32UTF8
ORACLE_SID=XE
ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server
他省略

再度インストール
cpan[1]> install DBD::Oracle

Using Oracle in /usr/lib/oracle/xe/app/oracle/product/10.2.0/server
DEFINE _SQLPLUS_RELEASE = "1002000100" (CHAR)
Oracle version 10.2.0.1 (10.2)

Unable to locate an oracle.mk, proc.mk or other suitable *.mk
file in your Oracle installation. (I looked in
/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/rdbms/demo/demo_rdbms32.mk /usr/share/oracle/xe/app/oracle/product/10.2.0/server/demo.mk /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/precomp/demo/proc/proc.mk /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/precomp/demo/proc/demo_proc.mk /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/proc/lib/proc.mk /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/proc16/lib/proc16.mk /usr/share/oracle/10.2/client/demo.mk /usr/share/oracle/10.2/client64/demo.mk under /usr/lib/oracle/xe/app/oracle/product/10.2.0/server)

The oracle.mk (or demo_rdbms.mk) file is part of the Oracle
RDBMS product. The proc.mk (or demo_proc.mk) file is part of
the Oracle Pro*C product. You need to build DBD::Oracle on a
system which has one of these Oracle components installed.
(Other *.mk files such as the env_*.mk files will not work.)
Alternatively you can use Oracle Instant Client.

こんな感じでまた怒られた。
どうやら、demo*.mkとかいうファイルを探しに行って見つからないらしい。

よくわからないけど、最初に探しに行ってる場所の

/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/rdbms/demo/demo_rdbms32.mk

このファイルと同じディレクトリに、

demo_xe.mk

ってファイルがあったので、試しにdemo_rdbms32.mkと言う名前でコピーしてみる。

# cd /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/rdbms/demo/
# cp demo_xe.mk demo_rdbms32.mk

再度インストール

cpan[1]> install DBD::Oracle

make -- OK

Warningがいっぱい出たけどmakeが通ったっぽい

Warning (usually harmless): 'YAML' not installed, will not store persistent state
Running make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/01base................# Test loading DBI, DBD::Oracle and version
t/01base................ok
t/10general.............DBI connect('','scott/tiger',...) failed: ORA-01034: ORACLE not available
ORA-27121: unable to determine size of shared memory segment
Linux Error: 13: Permission denied (DBD ERROR: OCISessionBegin) at t/10general.t line 22
FAILED--Further testing stopped: Unable to connect to Oracle (ORA-01034: ORACLE not available
make: *** [test_dynamic] Error 9
PYTHIAN/DBD-Oracle-1.24a.tar.gz
make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
reports PYTHIAN/DBD-Oracle-1.24a.tar.gz
Warning (usually harmless): 'YAML' not installed, will not store persistent state
Running make install
make test had returned bad status, won't install without force
Failed during this command:
PYTHIAN/DBD-Oracle-1.24a.tar.gz : make_test NO

でもmake_testが失敗。

notest install DBD::Oracle でも問題ないみたいな事をどこかで見たのでそうやってみる

cpan[2]> notest install DBD::Oracle
Running install for module 'DBD::Oracle'
Running make for P/PY/PYTHIAN/DBD-Oracle-1.24a.tar.gz
Has already been unwrapped into directory /root/.cpan/build/DBD-Oracle-1.24-SI0Gyg
Has already been made
Skipping test because of notest pragma
Running make install
make test had returned bad status, won't install without force

まだだめっぽい。
よくわからないけど force でやればいいの?
もうだんだんどうでもよくなってきたのでforceでインストールしてみる

cpan[3]> force install DBD::Oracle

Writing /usr/local/lib/perl/5.10.0/auto/DBD/Oracle/.packlist
Appending installation info to /usr/local/lib/perl/5.10.0/perllocal.pod
PYTHIAN/DBD-Oracle-1.24a.tar.gz
make install -- OK
Warning (usually harmless): 'YAML' not installed, will not store persistent state
Failed during this command:
PYTHIAN/DBD-Oracle-1.24a.tar.gz : make_test FAILED but failure ignored because 'force' in effect

通った…のか?

テストしてみる

[test.pl]
#!/usr/bin/perl

use DBI;

print "Content-type: text/html\n\n";

$ENV{'ORACLE_HOME'} = "/usr/lib/oracle/xe/app/oracle/product/10.2.0/server";
$ENV{'ORACLE_SID'} = "XE";
$ENV{'NLS_LANG'} = "JAPANESE_JAPAN.JA16EUC";

my $dbh = DBI->connect("dbi:Oracle:XE", "user", "pass") or die $@;

$sth = $dbh->prepare("select * from dual");
$nRes = $sth->execute;

while($raRes = $sth->fetchrow_arrayref) {
  print join(", ", @$raRes), "\n";
}

$sth->finish;
$dbh->disconnect;

exit;

出力結果
X

出たー。


■まとめ

DBD::Oracleをインストールする時は、oracle関連の環境変数を設定してからインストールする必要がある。

demo_rdbms32.mk が必要らしい。
あれば問題ないが、ない場合はエラーとなる。
Oracle 10g Express Edition を使用している場合は、

/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/rdbms/demo/demo_xe.mk

で代用できる(っぽい)ので、名前を demo_rdbms32.mk としてコピーする。

まだテストでエラーが起こるので、 force でインストールする。

■警告
Warning読んでないので、何かおかしな事が起こっているかもしれません。

インストールのログを見たい人はこちら


time stamp:2010/03/28 22:02:48
トラックバック(0)|コメント(0)
コメントを書く
この記事のトラックバックURL:

[Perl]モジュールの動的ロードの方法メモ
Perlでモジュールの動的ロードをする話。

$modName = "Test::Module";
require $modName;

これが通らない。いや通らないのは別にいい。
本来の使い方が違うだけなのだから。
と言う訳でその方法を調べてみた。


他の人はどうやってモジュールの動的ロードを行っているのかと思って色々調べていると、次のような方法があるらしい。

1. evalに文字列としてスクリプトを与え、文字列展開を行ってモジュールをロードする方法

requireの仕様として

require Test::Module;

というように直接モジュール名を指定した場合は、ライブラリにあるTest/Module.pmをロードしてくれる。
但し、

require $modName;

とした場合は、$modNameを文字列そのものとして扱うため、ライブラリにあるTest::Moduleファイルを探しに行ってしまう。
当然、そんなファイルはある訳がないので、

Can't locate Test::Module in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .)

こんな感じのエラーが出る訳だ。

そのため、

eval "require $modName";

とすることで、文字列展開したスクリプトをevalで実行することで、うまくモジュールをロードしてもらおうと言うのがこの方法。

しかし、この方法は遅いらしいし(未検証)、$modNameにPerlスクリプトが書いてあった場合、それも実行してしまうため、$modNameが自由に書き換えられる状態ではセキュリティ的にも危ないので、あまり使い方としてはよくないらしい。(人から聞いた話)

2. Test/Module.pmを直接requireする方法

use './lib';

とかをして、自分でモジュール用のファイルディレクトリを掘ってその中のモジュールファイルをrequireしたい場合などに使える方法

既に言ったが、

require $modName;

では、$modNameを文字列そのものとして扱うため、この中にモジュールファイルを指すパスを指定してやれば、正しく動く。

大昔のjcode.plのロードと同じ使い方。

require './jcode.pl';
require './lib/Test/Module.pm'

但し、これはモジュールが存在している場所を開発者が知らない場合は使えない。
モジュールとして既にインストールされている場合、モジュールファイル自体はシステムのディレクトリの方にあるため、自分でそれを拾いに行くのは難しい。
もしくは、許可されていない可能性もある。
自分の場合はここに入っていた
/usr/local/share/perl/5.10.0


上記は間違いでした。
require "Test/Module.pm";
で、Perlのライブラリとして登録されている場所を探索し、存在した場合それを読み込むようです。

3. UNIVERSAL::requireを使う方法

今はこれがモダンな方法らしい?具体的な意味はよく知らない。

use UNIVERSAL::require;

$modName = "Test::Module";
$modName->require;

のように書けば、指定したモジュールをロードしてくれる。

これを使おうとしてやってみたところ、UNIVERSALとか書いてあるくせに標準で入ってなかった…

use UNIVERSAL::require;

$modName = "Test::Module";
$modName->require;

結果
Can't locate UNIVERSAL/require.pm in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .)

しかもUNIVERSALであるので勝手にロードされてると思い込んで使おうとしたら、ロードされてなかった…
明示的にUNIVERSAL::requireをuseしてやる必要があるらしい

#use UNIVERSAL::require;

$modName = "Test::Module";
$modName->require;

結果
Can't locate object method "require" via package "Test::Module" (perhaps you forgot to load "Test::Module"?)

但し、このUNIVERSAL::requireは、結局内部的に

my $file = $module . '.pm';
$file =~ s{::}{/}g;

return eval { 1 } if $INC{$file};

my $return = eval qq{
  CORE::require(\$file);
};

こーいう事をしてるらしい。
安全性は高くなったものの、ロード時間に関しては1の方法と大して変わらないのかも(未検証)。

参考
UNIVERSAL::requireによるモジュールの動的ロード
Perl::Critic / UNIVERSAL::require


time stamp:2010/03/27 11:07:13
トラックバック(0)|コメント(0)
コメントを書く
この記事のトラックバックURL:

リファレンスを適当にまとめたもの
モジュールを作っているうちに、参照渡しと値渡しがわけがわからなくなってきたのでまとめておいてみる。

Perlは基本的に値渡しを行っている。
参照渡しをする場合は明示的にリファレンスを渡す必要がある。
渡された側でも、リファレンスを扱っているという認識で処理を行う必要がある。


■参照渡しをしない場合

my $v1 = 10;
my $v2 = 20;

my $result = &test($v1, $v2);

print $v1 . "\n";
print $v2 . "\n"
print $result . "\n";

sub test{
  my ($val1, $val2) = @_;
  $val1 *= 2;
  return $val1 + $val2;
}
__END__

出力結果
10
20
40


■参照渡しをする場合

my $v1 = 10;
my $v2 = 20;

my $result = &test(\$v1, \$v2);

print $v1 . "\n";
print $v2 . "\n"
print $result . "\n";

sub test{
  my ($val1, $val2) = @_;
  $$val1 *= 2;
  return $$val1 + $$val2;
}
__END__

出力結果
20
20
40


以上のように呼び出し先で、参照している値を弄ることができる。

ただし、以下のようにやると無意味。

■参照渡しの意味がない状態

my $v1 = 10;
my $v2 = 20;

my $result = &test(\$v1, \$v2);

print $v1 . "\n";
print $v2 . "\n"
print $result . "\n";

sub test{
  my ($ref1, $ref2) = @_;
  $val1 = $$ref1;
  $val2 = $$ref2;
  $val1 *= 2;
  return $val1 + $val2;
}
__END__

出力結果
10
20
40

当たり前のことなのだが、呼び出したサブルーチンで、リファレンスをデリファレンスしているため、参照先の値は変わらない。


配列やハッシュの場合は以下のようになる。

my @arr = (10, 20, 30);

&test(\@arr);

foreach my $val (@arr){
  print $_ . "\n";
}

sub test{
  my ($arrRef)= @_;
  my $i;
  for($i = 0; $i < scalar(@$arrRef); $i++){
    $$arrRef[$i] += 5;
  }
}
__END__

出力結果
15
25
35

my %hash = {
  'a' => 10,
  'b' => 20,
  'c' => 30
};

&test(\%hash);

while(my($key, $val) = each(%hash)){
  print $key . ": " . $val . "\n";
}

sub test{
  my ($hashRef) = @_;
  my @hashKeys = keys %$hashRef;
  my $i;
  foreach my $key (@hashKeys){
    $$hashRef{$key} += 7;
  }
}
__END__

出力結果
17
27
37

その他メモ
配列のリファレンスの要素数

@arr = (10, 20, 30);
$arrRef = \@arr;
print $#$arrRef;

出力結果
2

よしこれで再帰関数作れる。


time stamp:2010/03/25 12:19:06
トラックバック(0)|コメント(0)
コメントを書く
この記事のトラックバックURL:

CGI::Session
ここんところ、PerlでJavaServletみたいな事をしようと色々遊んでいるところなのだけども、sessionの取得で予想外の事が起こった。

# セッションIDの取得
$sessionId = $self->getSessionId($cookieData, $formData);
$logger->debug("Before Session ID[" . $sessionId . "]");

# セッションの取得
$session = new CGI::Session($driver, $sessionId, {'Directory' => $dataSource});

# セッションIDは取得できたが、IDとしては無効である場合、新規セッションで作り直す
if(defined($sessionId) && $sessionId ne $session->id()){
  $logger->warn("This Session ID is Invalid");

  $session->close;
  $session->delete;
  $session = new CGI::Session($driver, undef, {'Directory' => $dataSource});

  $logger->warn("Create Other New Session ID[" . $session->id() . "]");
}else{
  if(!defined($sessionId)){
    $logger->info("Create New Session ID[" . $session->id() . "]");
  }else{
    $logger->info("Get Session ID[" . $session->id() . "]");
  }
}
# 有効期限を設定
$session->expire($expire . "s");

こんな感じでCGI::Sessionを使って取得してるんだけど、sessionを取得するのに妙に時間がかかる。

ログ
[2010-03-22 20:28:40.763][DEBUG][SessionManager:231] Before Session ID[f977f2da04ecc84d3e74df0f17885ecd]
[2010-03-22 20:28:40.819][INFO][SessionManager:249] Get Session ID[f977f2da04ecc84d3e74df0f17885ecd]

819 - 763 = 56

56msもかかっている。何故だ。

ファイルでセッション管理してから多少遅いのはわかるけど、そこまで時間かかるものかなー
SessionIdの暗号化とかデコードに時間かかってるのかなー

モジュールは使えるものは使っておいた方がいいのか、
速くする為に自分で作った方がいいのか。

そもそもCGI.pm使ってない時点で便利なもの使ってないんだからSessionも自分で作り直した方がいいのかなー

--------
[2010-03-23 15:01:12]
追記

CGI::Sessionを継承して色々と時間を計った結果、ドライバモジュールのロードで時間がかかっていました。

CGI::Session(Ver. 4.35)では、780行目で

$self->_load_pluggables();

で、ドライバで指定されたモジュールのロードを行っているため、時間がかかっているように見えます。

sub _load_pluggables では指定したモジュールをrequireでロードしているため、実行に時間がかかっているようです。

でも、そもそもモジュールのロードは use しても行われているため、CGIとして動作している場合は特に実行時間に差はありません。

mod_perlとかでモジュールをキャッシュしている場合は、CGI:Sessionを使用するとセッションを取得するたびにrequireされるため、余計な処理が増えていることになります。(キャッシュされているため、実際にロードされるわけではないが…)


time stamp:2010/03/23 00:43:22
トラックバック(0)|コメント(0)
コメントを書く
この記事のトラックバックURL:
何かしら