MySQLのInnoDBに対してAUTOCOMMITが無効であるのにも関わらずCOMMITするのを忘れていた。

MySQLのInnoDBに対してAUTOCOMMIT = 0の状態でsqlを発行しCOMMITしなかった(するのを忘れた)場合、次のようになった(idはAUTO_INCREMENT)。

a. スクリプト実行前

id, value
1 taro
2 jiro
3 saburo

b. スクリプト実行中(shiroを追加するsqlを発行/COMMITせずにスクリプト終了)

id, value
1 taro
2 jiro
3 saburo
4 shiro

c. スクリプト実行後

id, value
1 taro
2 jiro
3 saburo

d. 同じsqlを再度発行し、きちんとCOMMITした場合。

id, value
1 taro
2 jiro
3 saburo
5 shiro

そもそもの原因は、「sqlをphpスクリプト上で発行してもDBに反映されないが、同sqlをphpMyAdminから直接発行すると正常に反映される」という現象が発生し、ついでに対象となるテーブルのid(AUTO_INCREMENT)がなぜか飛び番号になってしまったこと。いろいろと調べた結果、原因は単純にAUTOCOMMITの設定値の問題だった。

MySQLでInnoDBテーブルを利用していた場合、トランザクション時の挙動はAUTOCOMMITの値によって異なる。AUTOCOMMIT = 1の場合は、ユーザーが明示的にmysql_query(“BEGIN”)としなくともトランザクションを自動的に設定してコミットまで行ってくれる。AUTOCOMMIT = 0の場合は、ユーザーがmysql_query(“BEGIN”)とすることが前提となっており、またコミットには同じく明示的にmysql_query(“COMMIT”)が必要となる(もしくはmysql_query(“ROLLBACK”)とすることで反映をキャンセルする)。

参照:13.5.10.2. InnoDB と AUTOCOMMIT

DBの更新については通常であれば次のように常にAUTOCOMMIT = 0として明示的にBEGINおよびCOMMIT/ROLLBACKを行うようにしていたが、今回は例外的に下記を通さずにsqlを個別に発行しなければならない状況になり、かつ、その発行の直前にAUTOCOMMIT = 0を発行していることを忘れていたためにいくらか悩んでしまった。

$dbHandle = mysql_connect(DB_HOST, DB_USER, DB_PASS) or die('Could not connect: ' . mysql_error());
mysql_select_db("TEST");

$sql = "INSERT INTO table_name ...";

mysql_query("SET AUTOCOMMIT = 0");
mysql_query("BEGIN");

try {
	$result = mysql_query($sql);	
	if (!$result) {
		throw new Exception('exception : ' . mysql_error()) ;
	}	
	mysql_query("COMMIT");

} catch(Exception $e) {
	echo $e->getMessage;	
	mysql_query("ROLLBACK");
}

if (!$dbHandle) {
    mysql_close($dbHandle) ;
}

ちなみにAUTOCOMMITの値は下記sqlによって取得する

SELECT @@autocommit;

なお上記のテーブルの履歴で、dにおいて追加されたshiroのidが5となっているのは次の理由からだと思われる。

1. bにおいて発行されたsqlによってidが一つ使用(予約?)される。
2. cにおいてCOMMITなしに接続が切れることによるROLLBACKでレコードは元に戻されるがidは元に戻らない。
3. よってdの段階においては一旦使用されたidを飛ばしてその次の5が使用される。

参照:13.5.10.9. 暗黙的なトランザクション コミットとロールバック

もし自動コミット モードがオフで、最後のトランザクションを明示的にコミットせずに接続を閉じると、MySQL はそのトランザクションをロールバックします。

railsアプリの立ち上げまで簡単にメモ

rvmのインストールに続き、アプリを取り急ぎ立ち上げるまでを簡単にメモ。まずフォルダを作りGemfileを準備する。

$ rvm use 1.9.3
$ mkdir myapp
$ cd myapp
$ vim Gemfile

Gemfileの内容はsourceとrailsのみを記述する。

source "http://rubygems.org/"
gem "rails", "3.2.0"

bundleから各種gemをインストールする。その際に–path vendor/bundleを付けることによって、各アプリ毎にgemを保存する(vendor/bundle以下)。

$ bundle install --path vendor/bundle
Fetching source index for http://rubygems.org/
Installing rake (0.9.2.2)
・・・省略
Installing rails (3.2.0)
Your bundle is complete! It was installed into ./vendor/bundle

railsコマンドを実行。その際にGemfileの上書きに対するアラートが出るが、そのまま実行する。

$ bundle exec rails new .
省略
Overwrite /path/to/book/Gemfile? (enter "h" for help) [Ynaqdh] Y
省略

バージョンによるものなのか不明だが、下記エラーが表示された場合にはtherubyracerが足りないので、上書きしたGemfileにgem ‘therubyracer’を追加して再度bundle install –path vendor/bundleを実行する。

/usr/local/rvm/gems/ruby-1.9.3-p125/gems/bundler-1.0.22/lib/bundler/resolver.rb:280:in `resolve': Could not find gem 'jquery-rails (>= 0) ruby' in any of the gem sources listed in your Gemfile. (Bundler::GemNotFound)

memberテーブルにnameとemailカラムを作成しつつscaffold。

$ bundle exec rails g scaffold member name:string email:string

migrate後にテスト。

$ bundle exec rake db:migrate
$ bundle exec rake test

DBのリセット。

$ bundle exec rake db:migrate:reset
$ bundle exec rake db:seed

サーバーの立ち上げ。

$ bundle exec rails s
=> Booting WEBrick
=> Rails 3.2.0 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2012-02-25 20:53:09] INFO  WEBrick 1.3.1
[2012-02-25 20:53:09] INFO  ruby 1.9.3 (2012-02-16) [i686-linux]
[2012-02-25 20:53:09] INFO  WEBrick::HTTPServer#start: pid=27380 port=3000

ウェブサーバーから下記アドレスにアクセス。

http://192.168.1.72:3000/members/

明日でおしまい

明日で長年勤めさせていただいた社会人学校の講師業が終了する。通常の仕事とは別であったため、主に土曜日に準備にあたり日曜日に教壇に立つという日々で、それが当たり前のように感じるほど。そして、来週からの週休二日生活に一抹の不安を覚える。

学校では二十歳前の学生さんから年配の方まで本当に多くの方に教えさせていただいた。今でこそ授業の組み立てや資料づくりなどをある程度はスムースに出来るようになったけれども、始めた当時を思い返してみるとそれはもう酷いもので、よく嫌にならずについてきてくれたなと思う。当時の生徒さんと時々お会いすることがあって、「先生(私)に教わって良かった」などという有難いお言葉をいただくが、むしろこちらから謝りたい気持ち。

とはいえ、それまで他人にものを教えたことも/教わったこともなく、まして人前で話すことが得意ではなかったこともあって、いろいろと試行錯誤をしながらの授業だった。特に、他人から教わった経験があまりに少ないことが気がかりで、どんな言葉や言い回しが分かりやすいのか、どんな間をとったら良いのか、表情は、声のトーンは、と考えたらきりがない。各種書籍や業界のセミナーやカンファレンス、講演会などに出かけていき、少しずつ勉強をしていった。

そんな中で最も心がけたことは、ある物事を説明する場合にそれを自身が理解できた瞬間を思い出して、その前後のプロセスと共に説明をすること。「理解」の捉え方は人によって千差万別だけれど、それでもそれは物事の抽象度を高めていったその極限、もしくはその過程の一部分であって、かつ演繹が可能な考え方を手に入れることに変わりはないと思う。言葉にしてみれば簡単なことでも、講座の中で生徒さんの考え方をその方向に導いていくことは本当に難しかった。それに、生徒さんの来校の目的によっても方向性を変えなければならないし、そこのところの見極めには非常に悩んだ。ともかく、僕の思うところが少しでも伝わっていたら嬉しいな。

また、講師/生徒としての立場を超えて、個人的に友人としてお付き合いをさせていただいている方も何人かいて、実のところでそんなつながりを生み出してくれる場所が無くなってしまうことが最も寂しい。自分とは異なる年代、考え方を持つ方々とのお話は非常に楽しく、私がむしろ生徒として勉強をさせていただいていたようなものだから。

つらつらと思ったことを文章にしてみた。なかなかうまくまとまらないけれど、最後の準備をしながらこんなことを考えていた。

rvmをsudoを付けて(マルチユーザーとして)インストールする

rvmを管理者としてインストールしてみる(続きrubyも)。こちらのマニュアルのMulti-Userの項目を参考に進める。

$ bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

インストール自体はそれほどかからずに完了する。Single-Userの場合は、それぞれのホームディレクトリ以下に、Multi-Userの場合は/usr/local/rvm以下にインストールされる。また、インストール後は再ログインをする。

Once you have added the users you want to be able to use RVM to the rvm group, those users MUST log out and back in to gain rvm group membership because group memberships are only evaluated by the operating system at initial login time.

また、その後の関連コマンドについては下記のように入力する。sudo rvm -vとするとsudo: rvm: command not foundと怒られる。

$ sudo rvm -v

ではなく

$rvmsudo rvm -v

次にrubyをインストールする。まず、インストールに当たっての必要要件を確認する。

$ rvmsudo rvm requirements
省略

Additional Dependencies:
# For Ruby / Ruby HEAD (MRI, Rubinius, & REE), install the following:
  ruby: /usr/bin/apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion

省略

このうち、For Rubyの部分を参考にして足りないライブラリをインストールする。また、rvmsudo rvm list knownによってインストール可能なrubyのバージョン等を確認する。続いて該当バージョンをインストール。

$ rvmsudo rvm list known
$ rvmsudo rvm install 1.9.3
省略
Install of ruby-1.9.3-p125 - #complete

最後にパスを通して、また通常使用するバージョンを指定する。

$ echo '[[ -s "/usr/local/rvm/scripts/rvm" ]] && . "/usr/local/rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
$ rvm use 1.9.3 --default
Using /usr/local/rvm/gems/ruby-1.9.3-p125

rubyの削除は次のコマンドで行う

//ruby削除(ソースコードも削除する)
$rvmsudo rvm remove 1.9.3
//ruby削除(ソースコードは削除しない)
$rvmsudo rvm uninstall 1.9.3

マップを読み込んでから印刷ダイアログを自動的に表示する

次の流れで印刷をしたかったが、印刷ダイアログを出力(window.print())するタイミングによってページに含まれるGoogle Mapsが表示されないことがあった。おそらく、window.print()をloadScript()内の最後に書いていたので、非同期で読み込まれるGoogle Mapsの表示を待たずに印刷ダイアログを出力していたのだと思う。

  1. ページの「印刷」ボタンをクリック
  2. 印刷用にページを再構築(データを再度抽出し、Google Mapsを含めてページを作る)
  3. 印刷ダイアログを自動的に表示

そこで、google.maps.event.addListenerを利用してmapがidle状態に変化したら(つまり読み込みが完了したら)印刷ダイアログを表示するように変更したところ期待通りに動いた。当たり前といえば当たり前なんだけど。

サンプルはこちら

[PHP]





テスト


[/PHP]

サーバーの不具合について

11/20から断続的にDBがダウンしていた。アクセスログなどを確認すると、どうも怪しい場所からの連続アクセスがあったみたい。現在は復旧済。

アクセスされた方にはご迷惑をおかけいたしました。

vsftpd 2.3.2でlocal_umask=022がうまく動かない

/etc/vsftpd.confにlocal_umask=0022を設定しても、アップロードしたファイルのパーミッションが0700になってしまう。

# Default umask for local users is 077. You may wish to change this to 022,
# if your users expect that (022 is used by most other ftpd's)
local_umask=0022

umask=0077で0700になるのであれば基準となるパーミションは0777であり、umask=0022に設定すれば0755となると思っていたのだけれど、VSFTPD.CONFのマニュアルによると、ディレクトリ/ファイルが「アップロードで作成された場合」はこの限りではなく、デフォルトは0666である様子。

file_open_mode
The permissions with which uploaded files are created. Umasks are applied on top of this value. You may wish to change to 0777 if you want uploaded files to be executable.
Default: 0666

よって同ファイルにfile_open_mode=0777を追加することで解決した。

# Default umask for local users is 077. You may wish to change this to 022,
# if your users expect that (022 is used by most other ftpd's)
local_umask=0022
file_open_mode=0777

ファイルを「vsftpdを通しながらアップロード以外で作成する」という場面が思いつかず、こうすればとりあえず使える的な内容に。お恥ずかしい限り。

jQuery ui datepickerで入力可能な日付の制限をする

jQuery UIのdatepickerで、入力可能な日付の制限を別に入力された値によって変化させる方法を調べてみた。最初は、選択ボックスの更新時に

$( "#datepicker" ).datepicker({ minDate: '-1m', maxDate: '+1m' });

などと単純に再呼び出しをすれば良いかと思っていたけれど、それでは更新されなかった。そこでマニュアルをよく読んでみると、次の記述の通りsetterのような使い方をしないといけなかった様子(よく読んでいなかっただけ・・・)。

Get or set the maxDate option, after init.
//getter
var maxDate = $( ".selector" ).datepicker( "option", "maxDate" );
//setter
$( ".selector" ).datepicker( "option", "maxDate", '+1m +1w' );

結果、無事に更新が出来るようになった。そのサンプルはこちら。

2012年1月10日追記

onCloseを利用して日付の選択時にチェックを行うように変更した。例えば、土日のみ/ある特定日のみなど。ある一定期間内での入力制限はできても、同じ事を個別にはできなさそうなので、取りあえずはこんなふうにするしかないのかなあ。

また、ソースコードは次の通り。

[php]




日付取得サンプル

jQuery ui datepickerで入力可能な日付の制限をする


プラン選択
日程選択



[/php]

simplehtmldomを利用してCOOKPADのレシピを取得する

COOKPADのレシピを大量に取得する必要にせまられ簡単なスクリプトを組む。取得すべきレシピのIDがあらかじめ分かっていたために仕組みは非常に単純で、レシピ詳細ページのHTMLを丸ごと引っ張ってきて、該当の要素、ID、CLASS名を頼りにテキストを抽出するだけの内容。

当初、HTMLからの要素の抽出は正規表現の利用を考えていたのだけれど、予想に反して手間がかかりそうだったので「HTMLを扱うならjqueryのように出来ないかな」とスクリプトを探していたところPHP Simple HTML DOM Parserを見つけた。これはphpからHTMLをjqueryのように扱うためのライブラリで、書き方もjqueryに似ていてとても使いやすい。

“simplehtmldomを利用してCOOKPADのレシピを取得する”の続きを読む