MySQLの最近のブログ記事

私はDocomoの携帯しか持ってないので、事前にauとsoftbankを持ってる友人に頼んで、メールヘッダなどを調査したらこんな感じ↓

■ Docomo
----------------------------------------------------------------------------------
From ****@docomo.ne.jp Wed May 26 17:54:11 2010
Return-Path: <****@docomo.ne.jp>
X-Original-To: ***@igreks.jp
Delivered-To: ***@igreks.jp
Received: from localhost (localhost.localdomain [127.0.0.1])
by ***.igreks.jp (Postfix) with ESMTP id 5DE338B41B2
for <***@igreks.jp>; Wed, 26 May 2010 17:54:11 +0900 (JST)
X-Virus-Scanned: amavisd-new at igreks.jp
Received: from ***.igreks.jp ([127.0.0.1])
by localhost (igreks.jp [127.0.0.1]) (amavisd-new, port ****)
with ESMTP id kkpANBxiUasa for <****@igreks.jp>;
Wed, 26 May 2010 17:54:11 +0900 (JST)
Received: from docomo.ne.jp (mail108.docomo.ne.jp [203.138.203.8])
by ***.igreks.jp (Postfix) with ESMTP id 3E6618B40B0
for <***@igreks.jp>; Wed, 26 May 2010 17:54:11 +0900 (JST)
Date: Wed, 26 May 2010 17:54:09 +0900 (JST)
From: ****@docomo.ne.jp
To: ***@igreks.jp
Subject: =?iso-2022-jp?B?GyRCMSsbKEI=?=
Message-ID:
MIME-Version: 1.0
Content-Type: text/plain; charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit

$BK\J8(B
----------------------------------------------------------------------------------


■ au
----------------------------------------------------------------------------------
From ****@ezweb.ne.jp Wed May 26 18:24:29 2010
Return-Path: <****@ezweb.ne.jp>
X-Original-To: ***@igreks.jp
Delivered-To: ***@igreks.jp
Received: from localhost (localhost.localdomain [127.0.0.1])
by ***.igreks.jp (Postfix) with ESMTP id F1F818B41B2
for <***@igreks.jp>; Wed, 26 May 2010 18:24:28 +0900 (JST)
X-Virus-Scanned: amavisd-new at igreks.jp
Received: from ***.igreks.jp ([127.0.0.1])
by localhost (igreks.jp [127.0.0.1]) (amavisd-new, port ****)
with ESMTP id CBSY6-Rv+mf6 for <***@igreks.jp>;
Wed, 26 May 2010 18:24:28 +0900 (JST)
Received: from ezweb.ne.jp (nx3oBP07-06.ezweb.ne.jp [59.135.39.240])
by ***.igreks.jp (Postfix) with ESMTP id D7FC88B40B0
for <***@igreks.jp>; Wed, 26 May 2010 18:24:28 +0900 (JST)
Received: from nxev04mp06 (localhost [127.0.0.1])
by nxev04mp06.ezweb.ne.jp (EZweb Mail) with SMTP id 955ED5BC640B4
for <***@igreks.jp>; Wed, 26 May 2010 18:24:28 +0900 (JST)
From: ****@ezweb.ne.jp
To: ***@igreks.jp
Subject: =?iso-2022-jp?B?GyRCMSsbKEI=?=
Message-ID: <2010052618242860697200007c12@nxev04mp06.ezweb.ne.jp>
Date: Wed, 26 May 2010 18:24:28 +0900
Mime-Version: 1.0
Content-Type: text/plain; charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit

$B%6!<%6!<(B
----------------------------------------------------------------------------------


■ Softbank
----------------------------------------------------------------------------------
From ****@softbank.ne.jp Wed May 26 18:24:36 2010
Return-Path: <****@softbank.ne.jp>
X-Original-To: ***@igreks.jp
Delivered-To: ***@igreks.jp
Received: from localhost (localhost.localdomain [127.0.0.1])
by ***.igreks.jp (Postfix) with ESMTP id 975BD8B41B2
for <***@igreks.jp>; Wed, 26 May 2010 18:24:36 +0900 (JST)
X-Virus-Scanned: amavisd-new at igreks.jp
Received: from ***.igreks.jp ([127.0.0.1])
by localhost (igreks.jp [127.0.0.1]) (amavisd-new, port ****)
with ESMTP id HQrmiDMGcT3n for <***@igreks.jp>;
Wed, 26 May 2010 18:24:36 +0900 (JST)
Received: from mmrts049p01c.softbank.ne.jp (mmrts049p01c.softbank.ne.jp [123.108.236.27])
by ***.igreks.jp (Postfix) with SMTP id 6D9898B40B0
for <***@igreks.jp>; Wed, 26 May 2010 18:24:36 +0900 (JST)
Subject: =?ISO-2022-JP?B?GyRCJCYkcyQzGyhC?=
Mime-Version: 1.0
Content-Type:text/plain;charset=ISO-2022-JP
Content-Transfer-Encoding:7bit
Date: Wed, 26 May 2010 18:24:35 +0900
Message-ID: <20100526182435672870.2aea@0016E68F5982>
From: <****@softbank.ne.jp>
To: ***@igreks.jp
Sender:****@softbank.ne.jp
X-Priority: 3

$B%b%j%b%j(B
----------------------------------------------------------------------------------


仕様としては、

・DBはMySQL
・空メの送信先は「reg_kara_mail@igreks.jp」とする
・空メの件名に名前を指定できる。
・登録済みの場合はエラーメールを返す。
・空メ本文にメルマガIDをあらかじめ記載しておく。
・メルマガIDが認識できない場合もエラーメールを返す。
・読者データテーブル名は仮に「user_'メルマガID'」、文字コードはUTF8とする。

これを受けて、スクリプトはざっとこんな感じに。
※おおまかな流れだけで、細かい点は割愛してます


■ 空メール登録処理用CGI(karame.cgi)
-----------------------------------------------------------------------------------
#!/usr/bin/perl

package main;

use strict;
use CGI;
use DBI;
use Unicode::Japanese;

require './lib/get.pl'; #各種データ取得用ライブラリ(詳細割愛)
require './lib/proc.pl'; #各種処理用ライブラリ(詳細割愛)
require './lib/db.pl'; #DB接続、各種SQL実行用ライブラリ(詳細割愛)
require './lib/start.pl'; #メール配信用ライブラリ(詳細割愛)

&db::open(); #DB接続

my $sys_msg = ''; #エラー返信用メッセージ
my ($email, $name, $id);

###### メールから標準入力をパース
my $grep = '[\d\w-.+]+\@[\d\w-]+(\.[\d\w-]+)+'; #メールアドレスの正規表現

while(<>){
 if($_ =~ /^From.*?($grep)/i || $_ =~ /^Return-Path:.*?<($grep)>/i){
  $email = $1; #メールアドレス
 }
 if($_ =~ /^Subject:\s*(.+)$/i){
  $name .= $1; #名前
 }
 if($_ =~ /^\s*(=\?ISO-2022-JP.+\?=)\s*$/i){ #件名が途中で改行されてる時のため
  $name .= $1;
 }
 if($_ =~ /^mid:(.+)$/){
  $id = $1; #メルマガID
 }
}
if($email){
 if($id){
  my $stg = &get::setting_data($id); #メルマガ設定データ取得
  if($stg){
  ####### 読者テーブル内重複チェック
  &db::query("
   SELECT * FROM user_$id WHERE email='$email'
  ");
  my $href = $db::sth->fetchrow_hashref();
  if($href){
   #すでに登録済みの場合
   $sys_msg = <<EOM;
    送信いただいたメールアドレス「$email」はすでに登録済みです。
    このまま次回の配信をお待ちください。
EOM
   $sys_msg =~ s/\t//g;
   #メール返信処理
   &start::return_mail(
    '空メール登録処理エラー',
    $sys_msg,
   );
   exit;
  }
  if($name){
   #名前の入力があった場合
   $name = Unicode::Japanese->new(
    &proc::base64decode($name), #Base64デコード処理
    'jis'
   )->utf8; #JISからUTF8に変換
  }
  ######## 新規登録
  &db::query("
   INSERT IGNORE INTO user_$$pd{'id'} (
    no,  #オートインクリメント
    email,
    name,
    ・
    ・
    ・
   )
   VALUES(
    '',
    '$email',
    '$name',
    ・
    ・
    ・
   )
  ");
 }
 else{
  $sys_msg = <<EOM;
   登録しようとしたメールマガジンは存在しないか、すでに廃刊されています。
   お手数ですが、詳しくは発行者までお問い合わせください。
EOM
 }
}
else{
 $sys_msg = <<EOM;
  送信いただいた情報に不備があり登録できませんでした。
  空メール本文の内容は変更せずに送信してください。
  何度もこのエラーメールが返信される場合は、お手数ですが発行者までお問い合わせください。
EOM
}

########## エラー返信処理
if($sys_msg){
 &start::return_mail(
  '空メール登録処理エラー',
  $sys_msg,
 );
}
&db::close(); #DB切断

exit;

-----------------------------------------------------------------------------------



■Postfixエイリアス設定ファイル(/etc/aliase)に以下を追加
-----------------------------------------------------------------------------------
reg_kara_mail:  "|cd 'karame.cgiがあるディレクトリの絶対パス'; ./karame.cgi"
-----------------------------------------------------------------------------------

■リスタート
-----------------------------------------------------------------------------------
# newaliases

# /etc/init.d/postfix restart
-----------------------------------------------------------------------------------


■以下のようなメールを「reg_kara_mail@igreks.jp」宛に送る

 件名:自分の名前もしくは空白
 本文:mid:melmagaID

以上。


※レンタルサーバなどでエイリアスが設定できない場合は、後からcronなどでPOPアクセスして処理するなどの方法もある。(参考→http://www.igreks.jp/dev/2009/06/pop.html
この場合は返信メールが即時配信されない。


※ディズニーモバイルとかwilcomは調べてないけど、まあだいたい同じだべってことで。
結論から言うと、親プロセスの「wait;」の直後にもう一度MySQL接続処理を呼ぶ。
※2010/7/23追記:さらに各プロセスの頭でMySQL接続処理を呼ぶ。
ただそれだけ。


■MySQL接続用(db.pl)
--------------------------------------------------------------------------
package db;

use strict;

sub open{
my $dbs = "DBI:mysql:$dbname:$dbhost";
our $dbh = DBI->connect($dbs,$dbuser,$dbpass);
if(!$dbh){ die 'MySQL connection error!'; }
}

sub query{
my ($sql) = @_;
our $sth = $db::dbh->prepare($sql) || die $db::dbh->errstr();
my $exec = $sth->execute() || die $sth->errstr();
return $exec;
}

sub close{
$db::sth->finish();
$db::dbh->disconnect();
}

1;
--------------------------------------------------------------------------


■よくあるforkの処理
--------------------------------------------------------------------------
#!/usr/bin/perl

use strict;
use DBI;

require 'db.pl'; ######## 上のファイル読み込み
&db::open(); ######### 最初のMySQL接続

(親プロセス処理)

my $pid;
FORK: {
 if($pid = fork) {
  &db::open(); ###### ←【ここで単発接続】
  (親プロセス処理)

  close(STDOUT);
  wait;
  &db::open();  ###### ←【ここで再接続】これはfork終了後の処理用
 }
 elsif (defined $pid) {
  &db::open(); ###### ←【ここで単発接続】
  close(STDOUT);

  (子プロセス処理)

  exitしなくても勝手にexitされる?
 }
 elsif($! =~ /No more process/){
  sleep 5;
  redo FORK;
 }
 else{
  die 'Fork is not supported!';
 }
} # End Of Label:FORK


(親プロセス処理再開)

&db::close(); ######### 正規のMySQL接続切断
exit;
--------------------------------------------------------------------------



今まで、子プロセス内処理が終われば、全体の処理も終わるような構造のスクリプトばっかりだったので、なかなか気づかなかった。
ログも「mysql server has gone away...」ってしか出ないし。
標準出力を切ってるからブラウザからのデバックだけだと気がつかず、メルマガの空メール処理実装しようとして初めて気がついた。

一般的には、子プロセス内に入ったら、改めて子プロセス専用のコネクションを作らないと、子プロセスの終了時にMySQLコネクションが切断されてしまうらしい。
(つまり、親と同じコネクションを使っていると、親のコネクションも切れる)
全然しらんかった。


でも、なぜか子プロセスの最初で「&db::open();」してもダメだった。
perlのforkは親の変数が子に全部コピーされるから(同じデータベースハンドル、またはステートメントハンドルを使っている)なのかな?


わざわざ、子プロセス専用のSQL発行サブルーチン作るのも非効率だし、どうしようかなと悩んだ挙句こうなった。


時々、MilkyStepで即時配信メールが送れなくなるのはコレのせいだったりして。


※2010/7/23追記:
子プロセスの処理中に親プロセスが終了しても、子プロセスのMySQL接続が切れる。
また、親が終了する前に子のプロセスIDを使い始めると親のMySQL接続が切れる。
原因はよくわからんが、親・子それぞれの頭で接続処理を単発で入れたら上手く行った。
前記事(http://www.igreks.jp/dev/2010/01/mysql.html)のようにALTER TABLEを使って更新するのもいいのだが、できれば構造のテンプレートに合わせて一発でコピーしたい。

ただ単にテーブルの構造とデータをコピーする場合、普通は、

---------------------------------------------------------------------------------------
CREATE TABLE sample_new LIKE sample_original
INSERT INTO sample_new SELECT * FROM sample_original
---------------------------------------------------------------------------------------


しかし、新しい構造のテーブルにあわせて、構造を変更しながらデータをコピーするといった方法が見つからないため、スクリプトの処理を合わせて実現してみる。
あんまりスマートじゃないけど・・・

まず、最新の構造のテンプレートを作っておく。


■最新のテーブル構造のテンプレート(tbl.pl)
---------------------------------------------------------------------------------------

package tbl;

sub conf{
my %tbl = (

'table_a' => {
'column' => '
id INT AUTO_INCREMENT PRIMARY KEY,
column_1 VARCHAR(36),
column_2 VARCHAR(255),
column_3 VARCHAR(255)
',
'option' => '
ENGINE = MyISAM DEFAULT CHARSET = utf8
'
},

'table_b' => {
'column' => '
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
column_1 INT,
column_2 VARCHAR(255),
column_3 VARCHAR(255)
',
'option' => '
ENGINE = MyISAM DEFAULT CHARSET = utf8
'
},



);
return \%tbl;
}
1;

---------------------------------------------------------------------------------------


テンプレートを読み込んで新しいテーブルを一時作成し、
そのテーブルに既存のテーブルのデータをインポートする。

■更新処理(tbl_update.pl)
---------------------------------------------------------------------------------------
#!/usr/bin/perl

package::main;

my $dbh;



# この辺でデータベースへの接続処理(詳細割愛)

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

print "<p>データベースの再構築を開始します</p>\n";

# 既存のテーブル名をリストで取得
my $sth = $dbh->prepare("SHOW TABLES LIKE 'hoge_%'") || die $dbh->errstr();
$sth->execute();
my @hoge_tbl = ();
while(my @tbl = $sth->fetchrow_array()){
push(@hoge_tbl,$tbl[0]);
}

# テンプレート読み込み
require "./tbl.pl";
my $tbl = &tbl::conf(); # ※リファレンスで受け取り

foreach(@hoge_tbl){

# 既存テーブルのカラム名をリストで取得
my @cols = ();
my $sth = $dbh->prepare("DESCRIBE $_") || die $dbh->errstr();
$sth->execute();
while(my $href = $sth->fetchrow_hashref()){
push(@cols,$href->{'Field'});  # unshiftしちゃだめよ
}
my $cols = join(',',@cols);

#####################################
# テーブル名の末尾に個別にIDとかが付いてなければこの処理はいらない
$_ =~ /hoge_(.+?)(_.+?)?$/;
my $name = $1;
######################################

# 新しい構造の空テーブルを「pre_***」という名前で一旦作成
my $sth = $dbh->prepare("
CREATE TABLE IF NOT EXISTS pre_$_ (
$$tbl{$name}{'column'}
) $$tbl{$name}{'option'}
") || die $dbh->errstr();
$sth->execute();

# 既存のテーブルのデータを、カラム名を明示的に指定して挿入
# これをしないとカラムの数が合いませんよ!と怒られる
my $sth = $dbh->prepare("INSERT INTO pre_$_ ($cols) SELECT * FROM $_"); # ※1
$sth->execute();

# 既存のテーブル削除
my $sth = $dbh->prepare("DROP TABLE IF EXISTS $_");
$sth->execute();

# 新しいテーブルのリネーム("pre_"をとる)
my $sth = $dbh->prepare("ALTER TABLE pre_$_ RENAME $_");
$sth->execute();

}

print "<p>データベースの再構築が完了しました。</p>\n";

exit;
---------------------------------------------------------------------------------------


以上。

新しいテーブルにカラムが追加されてる場合は問題ないが、カラムが削除された場合は、多分※1のところでエラーになる・・・

こういうの一発でできるSQLないのかなぁ。

SQLだけでこういう処理をする方法が調べてもわからないので、スクリプト側でチェックしてみる。

メイン
------------------------------------------------------------------------------------------
#!/usr/bin/perl -w

use strict;
use DBI;
require './db.pl';

&db::open();

 my $table = 'table_name';
 ##追加カラム情報セット
 my @add = (
  ['field1' ,'INT' ],
  ['field2' ,'VARCHAR(255)' ],
  ['field3' ,'DATE' ],
  ['field4' ,'INT', ],
  ['field5' ,'VARCHAR(255)' ],
  ['field6' ,'VARCHAR(255)' ],
  ['field7' ,'INT' ]
 );
 my $cols = '';
 ##現在のテーブル情報を取得
 &db::query("DESCRIBE $table");
 while(my $href = $db::sth->fetchrow_hashref()){
  ##カラム名をセット
  $cols .= '<>'.$href->{'Field'}.'<>';
 }
 ##無ければ追加
 foreach(@add){
  if($cols !~ /<>$_[0]<>/){
    &db::query("ALTER TABLE $table ADD $_[0] $_[1]");
  }
 }

&db::close();
exit;
---------------------------------------------------------------------------------------



DB接続(db.pl)
---------------------------------------------------------------------------------------
package db;

use strict;

sub open{
my $dbs = "DBI:mysql:dbname:dbhost";
our $dbh = DBI->connect($dbs,dbuser,dbpass);
if(!$dbh){
die "データベースに接続できません";
}
}

sub query{
my ($sql) = @_;
our $sth = $db::dbh->prepare($sql) || die $db::dbh->errstr();
my $exec = $sth->execute || die $sth->errstr();
return $exec;
}

sub close{
$db::sth->finish;
$db::dbh->disconnect;
}

1;
---------------------------------------------------------------------------------------

perl2exeに四苦八苦

| コメント(0) | トラックバック(0)
かねてから気になっていたperl2exe(perlスクリプトファイルを実行ファイルにしてくれるソフト)についに挑戦。

Win32環境での情報などは結構転がってるのだが、Unix版で参考になるサイトがないので、仕方なく本家のユーザーマニュアルを読みながらやってみる。

まずは本家からダウンロード。
http://www.indigostar.com/perl2exe.htm


私の場合はperl5.10.0でLinux上でコンパイル予定なので、

・Perl2Exe V9.100 for Linux (Jan 18 2008)

↑これを選んでダウンロード&解凍。



なにやらマニュアルを見ると、

・exeファイルの作り方は、コマンドにて、
 「perl2exe yourscript.pl」
とするだけ。ふむふむ。

・特別なモジュールを読み込む場合は、スクリプト中に
  #perl2exe_include "somemodule.pm";
的なことを書けばよいと・・・

まあこの辺はWin32版と同じだな。


ほんでもって、Unixホストで動かす場合は、同梱されてるインタプリタじゃなくて、動かしてるサーバのインタプリタとコア/ローカルモジュールを使用する設定にできるらしい。

後々、こっちの方が楽そうなので、切り替えてみる。

--------------------------------------------------------------------
cd ~/perl2exe
rm -rf perl5
perl ./setup_perl2exe.pl
--------------------------------------------------------------------

「切り替えました!」的なことが表示されたので、多分OK。



とりあえず試しに使ってみる。

まず最初に、
--------------------------------------------------------------------
cd ~/perl2exe
./setup
--------------------------------------------------------------------


実行ファイル生成
--------------------------------------------------------------------
cd ~/perl2exe
./perl2exe hellocgi.pl
--------------------------------------------------------------------


同じディレクトリに「hellocgi」という実行ファイル(バイナリファイル)ができた!!


とりあえず動作確認。
--------------------------------------------------------------------
cd ~/perl2exe
./hellocgi
--------------------------------------------------------------------
(あくまで実行ファイルなのでファイル名を指定するだけ)

・・・・よっしゃ成功!!



次に、WEBサーバ(ブラウザ)から実行できるか実験。

マニュアルにはtelnetを使って・・・みたいなこと書いてあったけど、面倒くさいんで、

#!/usr/bin/perl -w

system "./hellocgi";

exit;

とだけ書いたperlスクリプトを実行ファイルと同じ場所に置いて、このファイルにアクセスする。


よしよし、ちゃんと表示される。html出力もOKですな♪




じゃあいよいよ、もっと複雑なDBが絡んだスクリプトに挑戦してみようってことで、やってみたのだが、もちろん一発でうまくいくわけがなく、以下のエラーが連発。

Warning: Can't locate VMS/Stdio.pm
at /usr/lib/perl5/5.10.0/File/Temp.pm line 149
@INC = /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi, /usr/local/lib/perl5/site_perl/5.10.0, /usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/vendor_perl/5.10.0, /usr/lib/perl5/vendor_perl, /usr/lib/perl5/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/5.10.0, ., .

Warning: Can't locate Compress/Bzip2.pm
at /usr/local/lib/perl5/site_perl/5.10.0/HTTP/Message.pm line 315
@INC = /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi, /usr/local/lib/perl5/site_perl/5.10.0, /usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/vendor_perl/5.10.0, /usr/lib/perl5/vendor_perl, /usr/lib/perl5/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/5.10.0, ., .

Warning: Can't locate Compress/Bzip2.pm
at /usr/local/lib/perl5/site_perl/5.10.0/HTTP/Message.pm line 447
@INC = /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi, /usr/local/lib/perl5/site_perl/5.10.0, /usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/vendor_perl/5.10.0, /usr/lib/perl5/vendor_perl, /usr/lib/perl5/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/5.10.0, ., .

Warning: Can't locate Compress/Bzip2.pm
at /usr/local/lib/perl5/site_perl/5.10.0/HTTP/Message.pm line 496
@INC = /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi, /usr/local/lib/perl5/site_perl/5.10.0, /usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/vendor_perl/5.10.0, /usr/lib/perl5/vendor_perl, /usr/lib/perl5/5.10.0/i386-linux-thread-multi, /usr/lib/perl5/5.10.0, ., .


(以下省略)



チーン。

まあ、エラーの言ってる意味はわかるのだが、Compress/Bzip2とか、聞いたこと無いようなモジュールまで要求してきやがる。

どうやら動作条件に関係なく、モジュール中にuseとかrequireとかの命令があったら、evalで呼ばれてても、とにかくそのモジュールはロードしなきゃならん仕様らしい。


仕方ないからインストールしてやるかと思い、cpan2rpm使おうとしたら「見つかりません!」的な。

ここまできたら、全部入れてやればいいんだろと思い、全部cpanからダウンロードしてきてmake installを実施。

しかしインストール中に今度は、

/bin/sh: gcc: command not found......

Cコンパイラまで追加すんのかよ!と思いつつ、こちらのサイト
http://memorva.jp/memo/linux/gcc_curses.php
を参考にしつつ、gccをインストール。

--------------------------------------------------------
yum install gcc*
--------------------------------------------------------

まあyumで一発だからいいけど。


これでようやく要求された全てのモジュールのmake install が完了。

インストール中に何やらいっぱい警告だのエラーだの表示されたけど、とりあえずpmファイルが入ったからいいや的な。


そしてもう一度実行ファイルを生成!

・・・お?

今度はエラーが出ない。

イエーイ!コンパイル成功〜〜〜〜!

さっそく実行〜!


あれ?

Software error:

DynaLoader object version 1.0801 does not match $DynaLoader::VERSION 1.08 at PERL2EXE_STORAGE/DynaLoader.pm line 93.
Compilation failed in require at PERL2EXE_STORAGE/DBI.pm line 157.
BEGIN failed--compilation aborted at PERL2EXE_STORAGE/DBI.pm line 157.
Compilation failed in require at ** line 15.
BEGIN failed--compilation aborted at ** line 15.


・・・うーむ・・

どうやらDBIの設定がいろいろ必要らしい。


苦戦は続く・・・



そして改めて説明をよく見ると、「Mysqlを使用する場合は、DBD::Mysql_PPを使用してください」
みたいなことが書かれている・・・

ということは、通常のCを使うデータベースドライバが使えないっつーことか。

このあたりで、ひょっとすると、これはものすごく使えないソフトなのでは・・・という不安がよぎる。


調べてみると案の定、Mysql_PPはいつまでたってもバージョンが0代だし、処理がくそ遅いという評判。


まあ全てPurePerlでやろうってんだからそうなるだろうな。

さらにMysql_PPの依存モジュールであるNet::Mysqlモジュールでもようわからんエラーが続出。




そして、ついに対応するのをあきらめた私。



結論:

データベース連携するプログラムはPerl2exeで(気軽に)使えない。
そうじゃないものなら使用する価値はまあまあある。
ただしスクリプトにモジュール名が書いてあったら必ず必要。


以上、ご参考までに。
前回(http://www.igreks.jp/dev/2009/10/perlmysql2.html)の続きで今度はバックアップファイルのリストア。

MilkyStepでは、サーバにファイルを残さずにローカルからアップロードしてリストアさせるので、今回はその流れで。

まず事前に・・・
use CGI;
my $F = CGI->new();


------アップロード処理用ルーチン---------------------------------------------------------------------

sub upload{
 my $fH = $F->upload('filename'); ### この辺でファイルチェック処理など適宜行う
 ### アップロードされたファイルを書き込み
 my ($buffer);
 open (OUT, ">./backup/$fH") || &show::error("ファイル($fH)が書き込みできません。");
 binmode (OUT);
 while(read($fH, $buffer, 1024)){
  print OUT $buffer;
 }
 close (OUT);
 close ($fH) if ($CGI::OS ne 'UNIX'); # for Windows
 chmod (0644, "./backup/$fH");
}


----リストア用ルーチン---------------------------------------------------------------------------------

sub restore{
 my $filename = $F->upload('filename');
 ### 圧縮ファイルを展開(今回の場合はgzip)tarだったら"tar -zxvf ファイル名"とか・・
 my $rec1 = system "gzip -d ./backup/$filename";

 ##### DBのテーブルをすべて消去しておく
 my @e_tables = $dbh->tables(); ### $dbhはデータベースハンドル(詳細割愛)
 foreach(@e_tables){
  &query("DROP TABLE IF EXISTS $_"); ### SQL発行&処理実行(詳細割愛)
 }

 #### リストア実行
 my $rec2 = system "mysql --user=$dbuser --password=$dbpass dbname < ./backup/展開されたファイル名";

 #### バックアップファイルは削除
 my $rec3 = system "rm ./backup/展開されたファイル名";

 #### エラー出力
 &show::error("圧縮ファイルの展開に失敗しました。") if $rec1 != 0;
 &show::error("DBの復元に失敗しました(mysql失敗)") if $rec2 != 0;
 &show::error("バックアップファイルの削除に失敗しました") if $rec3 != 0;
}


もちろんこれも前回同様Webサーバからmysqlコマンドが発行できなきゃダメなんだけど、レンタルサーバの場合どうなんだろ?
まあ、多分大丈夫だべ!


私の場合は、
"mysql --user=$dbuser --password=$dbpass dbname < ./backup/展開されたファイル名"
をしたときに、

unknown option '--skip-character-set-client-handshake'
(そんなオプションはねーよ!)

とエラーが出た。

何じゃこりゃと思ったら、以前(http://www.igreks.jp/dev/2008/11/movabletype422.html)に設定したmy.cnfの最後の一行だった。

この部分をコメントアウトしたら成功した。
前回の記事(http://www.igreks.jp/dev/2009/10/perlmysql.html)のちょい発展型

前回の場合だと、処理直後は圧縮したtar.gzファイルがサーバに残ってしまうので、標準出力後にファイルを削除するように改良。

サーバにバックアップファイルを残しておきたくない場合は便利かな?


sub backup{
 #### 念のためファイルが残ってたら削除
 system "rm ./backup/backup_*.tar.gz";
 #### 現在日時取得(詳細は割愛)
 my @cur = &get::current(); 
 my $datetime = "$cur[5]$cur[4]$cur[3]-$cur[2]$cur[1]$cur[0]";

 #### ダンプと圧縮
 system "mysqldump -a --opt --user=dbuser --password=$dbpass --default-character-set=binary dbname > ./backup/backup.mysql";
 system "tar -czPf ./backup/backup_$datetime.tar.gz ./backup/backup.mysql";

 #### ここで、Locationで飛ばさずに標準出力
 print "Content-Type: application/octet-stream\n";
 print "Content-Type: application/download; name=backup_$datetime.tar.gz\n";
 print "Content-Disposition: attachment; filename=backup_$datetime.tar.gz\n";
 print "\n";
 #### 圧縮ファイルの中身書き出し
 system "cat ./backup/backup_$datetime.tar.gz";

 #### サーバに残らぬよう削除
 system "rm ./backup/backup.mysql";
 system "rm ./backup/backup_$datetime.tar.gz";
 exit;
}


※catコマンド使用後は、ブラウザに出力が完了してるので削除が可能
※ヘッダー出力を system "echo ...." でやったらなぜか失敗したので、ヘッダー出力だけ普通にprintしたら成功した。失敗した理由わかるひと教えてください。

リストア方法→http://www.igreks.jp/dev/2009/10/perlmysql-1.html
PerlでMySQLのバックアップを行うサブルーチン。
※mysqldump使用


sub backup{
  #### 古いバックアップファイルを削除
  system "rm ./backup/backup_*.tar.gz";

  #### 現在日時取得
  my @cur = &get::current(); #詳細は省略
  my $datetime = "$cur[5]$cur[4]$cur[3]-$cur[2]$cur[1]$cur[0]";

  #### MySQLデータの取得と圧縮
  system "mysqldump -a --opt --user=$dbuser --password=$dbpass --default-character-set=binary $dbname > ./backup/backup.mysql";
  system "tar -czPf ./backup/backup_$datetime.tar.gz ./backup/backup.mysql";
  #### 一時ファイル消去
  system "rm ./backup/backup.mysql";

  #### ダウンロード実行
  print "Location: ./backup/backup_$datetime.tar.gz\n\n";

  exit;
}


※gzファイルが勝手に展開されてしまう場合はapacheの設定を変えてください。
※systemを使ってるので別にperlにこだわる必要なし。
※mysqldumpコマンドが使えなきゃ意味ない。

サーバにファイルを残したく無い場合→http://www.igreks.jp/dev/2009/10/perlmysql2.html
MySQLの一つのセルに、「,」(カンマ)を含む文字列があるとき、指定された文字列が、カンマで分割された要素の何番目かを調べるSQLが以下。

----------------------------------------------------------
SELECT FIND_IN_SET( 'Mon', 'Spa,ce,Mon,eky' );
-> 3

SELECT FIND_IN_SET( 'ce', 'Spa,ce,Mon,eky' );
-> 2
----------------------------------------------------------
※第1引数で指定した文字列が、引数STRLISTに含まれる値にマッチすれば、マッチしたリストの位置を返す。
※ STRLISTは、カンマ( , )で分割された文字列。
※引数STRLISTが空文字なら"0"を返す。
※どちらかの引数がNULLならNULLを返す。
※最初の引数にカンマが含まれる場合は、エラーとなる。


ユーザーデータの一つに、飼っているペットの種類とか、興味のある分野などの、複数の要素が含まれる可能性がある場合は、カンマで分割した文字列でVARCHAR型のカラムに入れておけば、この関数でWHERE指定できる。

例)
user_table
+−−−−+−−−−−−−−−−−+
|name  | pet_kind  |
+−−−−+−−−−−−−−−−−+
|Yamada | 2,3,6   |
+−−−−+−−−−−−−−−−−+
|Suzuki | 3,8,12,22 |
+−−−−+−−−−−−−−−−−+
    ・
    ・
    ・

※3番のペットを飼っている人の名前を調べる
-------------------------------------------------------------------------------------------
SELECT name FROM user_table WHERE FIND_IN_SET(3, pet_kind) > 0;
-------------------------------------------------------------------------------------------
結果:山田とスズキどちらもSLECTされる。


最初、MySQLもPHPとかperlみたいに、split的な関数があるんだろうなんて思って、たいして気にもせずデータを投入していたら、私の力では発見できなかった。

やばい!と思って、この関数で、かろうじて分割検索できたけど、カンマで分割していたのはたまたま。
もしカンマ以外の文字列で区切ってたら、とおもうと少しぞっとする。
メルマガ配信した累計秒数を管理画面で参照したりするのに、秒数をhh:mm:ss形式に
したいなーということで・・・

例えば、

10  → 00:00:10
60  → 00:01:00
3601  → 01:00:01

みたいな感じ。

これをスクリプト側で操作するとなると、累計秒をローカルタイム形式に直して
sprintfとかで整形してだらだらだら・・・

と、意外とけっこう面倒くさかったり無駄なメモリを消費したり、なんか効率悪い。
てゆーか、こういう処理はなるべく書きたくないし。

そう思って、ググってたら、目的を一発で達成できるなんとも便利なSQL文を
発見。

------------------------------------------------------------------

「SEC_TO_TIME( 秒数 )」

------------------------------------------------------------------

これで、秒数を勝手にhh:mm:ss形式に変換してくれる。


SELECT SEC_TO_TIME(秒数)

で変換&参照できるが、DBから参照するんであれば、最初から
データ型をTIMEにしておき、

※$timeには秒数を格納

INSERT INTO table VALUES('SEC_TO_TOME($time)')

とかで、あらかじめhh:mm:ss形式でデータをぶち込んでおいてやれば、
インタフェイスが変わったときも便利。


あらためて、データベースって素晴らしいと思った次第。


ちなみにこれの逆は、

TIME_TO_SEC( hh:mm:ss )で、

TIME_TO_SEC(01:00:01) は、 3601 となる。

このアーカイブについて

このページには、過去に書かれたブログ記事のうちMySQLカテゴリに属しているものが含まれています。

前のカテゴリはMilkyStepです。

次のカテゴリはPerlです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

Powered by Movable Type 4.22-ja