2009年10月31日土曜日

Zend_Db_Adapter_Abstractを継承した拡張自作Adapter

どうも、俺@遊び帰宅です。
眠いっす。

Zend Frameworkを使ってWebアプリケーションを作ってて
「あー、DBへのクエリのログ取りたいなー」ってよくあると思います。
特にトランザクションの発生するクエリに関しては、Webプログラマとしては必需品だと思います。

方法はすべてのXXController内でクエリを発行する度に
<?php
// Zend_Db_Adapter_Abstractでクエリ発行
$adapter->query("INSERT INTO foo_table (column) VALUES ('value')");

// Zend_Db_Profiler
$profiler = $adapter->getProfiler();
$queries = $profiler->getQueryProfiles(Zend_Db_Profiler::INSERT);
if (is_array($queries)) {
foreach ($queries as $query) {
file_put_contents($filename, $query->getQuery());
}
}
と、簡単なサンプルを書くとこんな感じでしょうか。

ただ、これだとクエリを発行する度にZend_Db_Profilerを生成し、ログを取得しなければなりません。
面倒くさいです。

そんな面倒くさがりな貴方にはZend_Db_Adapter_Abstractを継承した自作クラスを作成することをオススメします。
例えばPostgreSQLを使っている場合は
<?php
/**
* Zend_Db_Adapter_Pdo_Pgsqlを継承した自作アダプター
*/
class MyPdoPgsql extends Zend_Db_Adapter_Pdo_Pgsql {
/**
* @Override
*/
public function insert($table, array $bind) {
// 略 Zend_Db_Adapter_Pdo_Pgsql::insert()と同じ処理

// Zend_Db_Profilerでログる
$profiler = $adapter->getProfiler();
$queries = $profiler->getQueryProfiles(Zend_Db_Profiler::INSERT);
if (is_array($queries)) {
foreach ($queries as $query) {
file_put_contents($filename, $query->getQuery());
}
}
}


/**
* @Override
*/
public function update($table, array $bind, $where) {
// 略 Zend_Db_Adapter_Pdo_Pgsql::update()と同じ処理

// Zend_Db_Profilerでログる
$profiler = $adapter->getProfiler();
$queries = $profiler->getQueryProfiles(Zend_Db_Profiler::UPDATE);
if (is_array($queries)) {
foreach ($queries as $query) {
file_put_contents($filename, $query->getQuery());
}
}
}

public function delete($table, $where = '') {
// 以下同じ
}
}
このようにinsert/update/deleteメソッドにログ処理を書いてZend_Db_Adapterの子クラスを作成します。

あとはindex.php内の処理でZend_Db_Adapter_Abstractを生成するときに
<?php
$db = new MyPdoPgsql($options);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
のようにデフォルトアダプターをセットしておけば、全てOK!!

基本的にZend FrameworkのクエリはZend_Db_Adapter_Abstractを経由して発行されるので、あとは自動でログを取ってくれます。
他にもトランザクションや例外処理も全てこの中に書けば、面倒なことを書かずに済みますね。


以上、面倒くさがり屋さんへ。

2009年10月22日木曜日

phpで三項演算子の注意点

どうも、俺@始業です。
別に暇なわけじゃないですよ。

今日はphpで三項演算子を利用した際の注意点をメモります。
三項演算子は例えば
<?php
$a = 1;
// 三項演算子
echo $a == 1 ? "$aは1です。" : "$aは1ではありません。";
と書くと、出力は
$aは1です。
となります。
書き方は
(式) ? 真の場合 : 偽の場合
です。
三項演算子は短く条件分岐を書くことができるので、phpでプログラムを書いた際のView側などで利用できます。

で、
三項演算子を入れ子(ネスト)にした場合、php三項演算子ならではの注意点があります。
<?php
$a = 1;
// 三項演算子
echo $a == 1 ? "$aは1です。" : $a == 2 ? "$aは2です。" : "$aは1でも2でもありません。";
とすると、プログラマーは
$aは1です。
と出力されると予想します。
ところがドッコイこの結果は
$aは2です。
と出力されてしまいます。危ないですねー。
理由はPHPマニュアル@三項演算子にあるように、phpの三項演算子は左から右へ評価の順が移っていくので、上の例でいくと
1)$a == 1 が評価されtrue
2)"$aは1です。"をechoしようとする
3)(1)で評価されたtrueが次の $a == 2で評価されtrue
4)"$aは2です。"をechoする。
という流れになるみたいです。たぶん、、。

要は(3)で$a == 2 が評価されるときに(1)で評価された結果(true)を持ち越してしまうみたいです。

というわけで、どうしても三項演算子を利用して入れ子をしたい場合は
<?php
echo $a == 1 ? "$aは1です。" : ($a == 2 ? "$aは2です。" : "$aは1でも2でもありません。");
と書きましょう。


以上ーー。

2009年10月7日水曜日

PostgreSQLでコマンドラインから直接クエリ実行

おはようございます、俺@朝礼後です。

今日はPostgreSQLでコマンドラインで直接クエリを実行する方法をメモります。
説明が難しいのですが、「コマンドラインから直接」というのは
$ psql foo_database
psql> SELECT * FROM bar;
というのではなく、
postgresqlサーバへログインせずにクエリ発行、結果取得という意味です。
(※内部的にはpostgresqlログインしてるのかもやけど、、、)


やり方は、
$ psql -c 'SELECT * FROM bar' foo_database
です。
ちなみに
$ psql -c 'SELECT * FROM bar' -o output_file foo_database
とやると、
output_fileへクエリ結果を出力します。

ついでにMySQLでは
$ mysql -e 'SELECT * FROM bar' foo_database
で直接クエリを実行できます。



以上どぇぇぇぇぇす。

2009年9月16日水曜日

MySQLのALTER TABLE構文のまとめ

こんばんわ、俺@家でまったり中です。
今日は、プログラマがよく使うであろうMySQL(ver5.x)のALTER TABLE構文についてメモします。
ALTER TABLE構文はMySQLのテーブルの構造を変更する時に使います。

1)カラムを追加
mysql> ALTER TABLE [テーブル名] ADD COLUMN [カラム名] [型] [制約] [FIRST | AFTER カラム名]
最後の[FIRST|AFTER カラム名]はなくてもOK。
例:
msql> ALTER TABLE table_name ADD COLUMN name VARCHAR(64) NOT NULL DEFAULT ''

2)カラムを削除
mysql> ALTER TABLE [テーブル名] DROP COLUMN [カラム名]

3)テーブル名変更
mysql> ALTER TABLE [テーブル名] RENAME TO [新テーブル名]

4)カラムの型を変更
mysql> ALTER TABLE [テーブル名] MODIFY [カラム名] [型] [制約]
または
mysql> ALTER TABLE [テーブル名] CHANGE COLUMN [カラム名] [新カラム名] [型] [制約]

5)カラム名を変更
mysql> ALTER TABLE [テーブル名] CHANGE COLUMN [カラム名] [新カラム名]

6)インデックスを追加
mysql> ALTER TABLE [テーブル名] ADD INDEX [インデックス名] ([カラム名,...])
または
mysql> CREATE INDEX [インデックス名] ON [テーブル名] ([カラム名,...])

7)インデックスを削除
mysql> ALTER TABLE [テーブル名] DROP INDEX [インデックス名]
または
mysql> DROP INDEX [インデックス名] ON [テーブル名]

8)AUTO_INCREMENTの値を変更
mysql> ALTER TABLE [テーブル名] AUTO_INCREMENT = [値]

9)テーブルの使用しているエンジン(ENGINE)を変更
mysql> ALTER TABLE [テーブル名] ENGINE = [ENGINE名]

10)テーブルのデフォルト文字セットを変更
mysql> ALTER TABLE [テーブル名] DEFAULT CHARSET = [文字コード名]

11)デフォルト値を変更
mysql> ALTER TABLE [テーブル名] ALTER COLUMN [カラム名] SET DEFAULT [デフォルト値]

12)デフォルト値を削除
mysql> ALTER TABLE [テーブル名] ALTER COLUMN [カラム名] DROP DEFAULT

詳しくはMySQL::5.1マニュアル ALTER TABLE構文をば。

以上どぇぇぇぇーす。

2009年9月9日水曜日

mmeasureのインストール

どうも、俺@仕事中、2度目の投稿です。
決して暇な訳ではありませんよ。

mmeasureのインストールについて覚書き。
インストールは至って簡単シンプルです。
まずはmmeasure projectからソースをダウンロード。2009年9月9日時点でのバージョンは1.0.7でした。

次に解凍
# tar zxvf mmeasure-1.0.7.tar.gz
出てきたディレクトリを/usr/local下(デフォルト)へ移動
# mv mmeasure/ /usr/local
# cd /usr/local/mmeasure


perlのモジュールをインストールします。
cpanについてはオデの日記@cpanモジュールを見てね。
# cpan install Socket
# cpan install DBI // ←MySQLを起動させてないとダメっぽい
# cpan install Jcode
# cpan install DBD::mysql // ←ココでずっこける
RRDsモジュールはrrdtoolをインストールすればOKの模様。
# wget 'http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.0.x/rrdtool-1.0.49.tar.gz'
# tar zxvf rrdtool-1.0.49.tar.gz
# rrdtool-1.0.49
# ./configure
# make
# make install // ←ついでにrrdtoolインスト
# cd /usr/local/src/rrdtool-1.0.x/perl-shared
# perl Makefile.PL
# make && make install
で、DBD::mysqlですが、何をどう頑張ってもインストールできませんでした。
同じくインストールできなかったけど、頑張ればインストできた人がいれば解決方法を教えて下さい。
とりえずyumで
# yum install perl-DBD-MySQL
で何とかインスト。

設定ファイルを修正
# cp mmeasure_template.conf mmeasure.conf
# vim mmeasure.conf
で、この設定ファイルを色々変更します。デフォルトの設定ファイルには間違いもあるので必ず修正します。
#------------------
# MySQL
#------------------

MYSQL_HOST=[MySQLのIP]
MYSQL_USER=mmeasure # あとでmmeasureユーザを追加します。
MYSQL_PASSWORD=password # 接続パスワード
MYSQL_PORT=3306
MYSQL_SOCKET=/tmp/mysql.sock
MYSQL_SLOW_QUERY_LOG="/usr/local/mysql/var/mysql-slow" # スローログのPATH。ない場合はコメントアウトで
#------------------
# Mail
#------------------

MAIL_HOST=foo.com # 自サーバホスト名
MAIL_FROM=info@foo.com # 実在するアカウントで!
MAIL_TO=bar@tohost.com
#MAIL_CC= # いらん!
#MAIL_BCC= # いらん!
MAIL_SUBJECT="alert" # メールの件名を適当に。
#------------------
# Pathes
#------------------

PATH_RRDTOOL="/usr/local/rrdtool-1.0.49/bin/rrdtool" # パスが違う場合は修正
#------------------
# Web setting
#------------------

WEBDIR="$PATH_MEASURE/web/images/graphs" # /imagesを追加!
WIDTH_SMALL="200" # 追加!
HEIGHT_SMALL="100" # 追加!
WIDHT_LARGE="400" # 追加!
HEIGHT_LARGE="100" # 追加!

次にMySQLサーバにmmeasureユーザの追加です。僕の環境ではローカル接続の別サーバにMySQLがあったので
# mysql -h 192.168.0.2 -u mysql
mysql> GRANT ALL ON *.* to mmeasure@192.168.0.1 indentified by 'password';
mysql> SET password FOR mmeasure@192.168.0.1 = old_password('password'); // ←MySQL4.1以前の互換
mysql> FLUSH PRIVILEGES;
でOK。一応接続テストもしておく。
# mysql -h 192.168.0.2 -u mmeasure -p
Enter password: パスワード入力


次に、MySQLのデータを取るために./mmeasure/daemon/data-mysql/read-data.plを修正します。ココはだいぶハマったのですが、クエリ回数、クエリ比率などを表示するために変更するべきです。
# vim ./mmeasure/daemon/data-mysql/read-data.pl
50 my %status = select_2column( $db, "show global status" ); # globalを追加

あと、僕の場合ですが、mmeasure.errでアラートメール送信時のエラーが出まくっていたのでメール送信処理部分をコメントアウトしました。アラートはNAGIOSとかで監視してるので、、、。
# vim ./mmeasure/daemon/daemon-mmeasure.pl
224 # alerter_sendmail( $conf{ 'MAIL_HOST' },
225 # $conf{ 'MAIL_TO' }, $conf{ 'MAIL_FROM' },
226 # $conf{ 'MAIL_SUBJECT' }, $alert_message,
227 # $conf{ 'MAIL_CC' }, $conf{ 'MAIL_BCC' } );
なんでエラーが出るのか謎です。時間があるときに調査します。分かる方いたら教えてください><
エラー内容
# vim ./mmeasure/daemon/log/mmeasure.err
52 RCPT command failure(Bad file descriptor)... at ../library/mailer.pl line 93.
53 print() on closed filehandle SH at ../library/mailer.pl line 127 (#1)
54 (W closed) The filehandle you're printing on got itself closed sometime
55 before now. Check your control flow.
56
57 readline() on closed filehandle SH at ../library/mailer.pl line 128 (#2)
58 (W closed) The filehandle you're reading from got itself closed sometime
59 before now. Check your control flow.
60
61 Use of uninitialized value in substitution (s///) at ../library/mailer.pl line
62 205 (#3)
63 (W uninitialized) An undefined value was used as if it were already
64 defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
65 To suppress this warning assign a defined value to your variables.
66
67 To help you figure out what was undefined, perl tells you what operation
68 you used the undefined value in. Note, however, that perl optimizes your
69 program and the operation displayed in the warning may not necessarily
70 appear literally in your program. For example, "that $foo" is
71 usually optimized into "that " . $foo, and the warning will refer to
72 the concatenation (.) operator, even though there is no . in your
73 program.
74
75 Use of uninitialized value in pattern match (m//) at ../library/mailer.pl line
76 130 (#3)
77 print() on closed filehandle SH at ../library/mailer.pl line 132 (#1)
78 DATA command failure(Bad file descriptor)... at ../library/mailer.pl line 134.
79 print() on closed filehandle SH at ../library/mailer.pl line 143 (#1)
80 readline() on closed filehandle SH at ../library/mailer.pl line 144 (#2)
81 Use of uninitialized value in pattern match (m//) at ../library/mailer.pl line
82 146 (#3)
83 print() on closed filehandle SH at ../library/mailer.pl line 148 (#1)
84 Body & header send failure(Bad file descriptor)... at ../library/mailer.pl line 150.
85 print() on closed filehandle SH at ../library/mailer.pl line 156 (#1)

次はApacheにAliasを作ります。
# vim /usr/local/apache2/conf/httpd.conf
Alias /mmeasure "/usr/local/mmeasure/web"

# /usr/local/apache2/bin/apachectl restart

ではデーモンの起動!
# ./mmeasure/daemon/mmeasure.sh start


以上~!あとはチューニングアドバイスを信じるも信じないもアナタ次第!