symfonyのmoveFile()メソッドでファイルが移動できない

symfonyではアップロードファイルを、任意の場所に移動させる moveFile というメソッドがあるのですが、このメソッドは失敗したときにエラーを出してくれない。


$this->getRequest()->moveFile('filename', $destFileName)
なので失敗していて、かつ原因がわからない場合は、lib/symfony/sfWebRequest.class.php

@move_uploaded_file(...
となっているところの「@」をはずしてやるとよい。まあ、大抵はディレクトリのパーミッションの問題だったりするんだけど。

MySQLで、PrimaryKey以外を条件としてINSERT or UPDATE のような事を実現する

MySQLの REPLACE 文は、プライマリーキー(正確にはPrimaryKey、またはUniqKey)を条件にして一致するものがある場合は DELETE&INSERT、一致するものがなければ INSERT のみを行うという事ができるます。

似たような事を行うものとして、Oracleの MERGE 文がありますが、こちらはプライマリーキーでなくとも任意の条件を指定して INSERT、または UPDATE を行うことができます。

さて、MySQL でも PrimaryKey以外を条件に INSERT or UPDATE を行いたい事があったので、やり方を考えてみました(※PrimaryKey は AutoIncrement になっているという前提)。


CREATE TABLE user (
id INT(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(30),
birthday DATE,
PRIMARY KEY(id)

上のような(id,name,birthday)カラムをもつ user テーブルがあったとき、、
name カラムを条件として、(name,birthday) = ('山田太郎','1980-05-15') のレコードを INSERT or UPDATE するSQLを以下に示します。


REPLACE user
SELECT id,name,'1980-05-15'
FROM dual LEFT JOIN user
ON user.name = '山田太郎'

該当レコードが存在する場合、id の値が入るので普通に REPLACE 文が実行されます。該当レコードが存在しない場合、id が NULL となるためマッピングされるレコードなし→INSERT文が実行される→id は AutoIncrement される、となりINSERTが成功します。
(※'山田太郎'が複数いるケースは考えないです・・

以上で、MySQL の REPLACE 文で PrimaryKey 以外を条件にして更新ができました。

WindowsのApacheでPHPを動かそうとしたときに、Cannot load mysql extension. といわれた

Windows 上に、Apache + PHP + MySQL の環境を構築していて、ハマった問題と解決策のメモ。

■事象


PHPからMySQLに繋ごうとすると、Cannot load mysql extension.というエラーが発生。Apache のログにも以下が出力されていた。
PHP Warning: PHP Startup: Unable to load dynamic library 'C:/Program Files/php-5.2.6/ext/php_mysql.dll'
PHPのインストール&設定は、Webなどに掲載されているとおりに正しく実施している

■原因&対策


Windows上でPHPから、MySQLに繋ぐには、適切なバージョンのlibmysql.dll がパスの通ったところになければならない。
普通にWindows用のPHPをインストールすると、インストールディレクトリにlibmysql.dll があるので(インストールディレクトリにパスを通していれば)問題にはならないが、この「適切なバージョンの」というところがポイント。
僕の環境では古いMySQLがインストールされていて、かつ、そのMySQLがパスの前の方に指定してあったため、別の「libmySQL.dll」が読み込まれて、上記エラーが発生していました。パスの順序を修正して解決。

いやあ、悩んだ・・・orz

MySQLでNULLを含むカラムにINDEXを張っても大丈夫

「NULLを含むカラムにINDEXを張っても使用されないので、できればNULLを意味するデフォルト値を設定しよう」という記述を何回かみたことがあって、てっきりそうだと思い込んでいました。が、MySQLのマニュアルには、


column_name IS NULL を使用した検索では、column_name にインデックスが
張られている場合にインデックスが使用されます。
http://dev.mysql.com/doc/refman/4.1/ja/mysql-indexes.html
と書いてありました。たぶん「NULLが含まれていると駄目な場合もある」のだと思いますが、上記のようなケース(IS NULLの比較)では問題ないようです。

自分で調べてみるものです。

Eclipseプロジェクト内に、Validate対象外のディレクトリを作る方法

Eclipseプロジェクト内に、Tomcatのランタイムを格納すると便利だと思ってやってみたのですが、TomcatのサンプルのWebアプリが検証にひっかかってしまって、Eclipse上に大量のエラーがでてしまいました。

Tomcatのサンプルを捨ててしまうという解決策も考えたのですが、そもそも「Eclipseで特定のディレクトリ以下を検証の対象外とする方法はないのか?」という点が気になったので調べてみました。

結論からいうと、Eclipseの機能としてはない、が少しトリッキーな方法で実現できました。

まず、Eclipseの検証機能ですが、これはプロジェクト単位で有効/無効を切り替えることはできますが、ディレクトリ単位では無理なようです。一方で、Eclipseの機能として「.(ドット)」で始まるディレクトリは管理対象(検証対象)としない、という機能があります。これを利用しました。

つまり、Eclipseプロジェクト内に「.Tomcat」というディレクトリを作りました。そして、その下にTomcat一式を置くと、、、
検証エラーを発生させることなく、Tomcat一式をEclipseプロジェクト内に置くことができました。
ちなみに、「.(ドット)」から始まるディレクトリは管理対象(=検証対象)にはならないのですが、ライブラリ選択や、サーバ選択では普通にでてきました。万事解決。

Java1.6+Antを動かそうとすると、java.lang.NoClassDefFoundError: org/apache/tools/ant/launch/Launcher

LinuxFedora)に、Java1.6とAntをインストールして、いざAntを動かそうとすると表題のエラーが発生。
Javaは、jdk-6u7-linux-i586-rpm.bin をダウンロードしてインストール、Antは yum install antでインストールしたもの。

原因調査のために、まず、javaコマンドから実行

java -cp /usr/share/java/ant-launcher.jar org.apache.tools.ant.launch.Launcher --help

これは問題なく動いた。ということは、/usr/bin/ant の問題か。次に /usr/bin/ant の中身を見てみると "-execdebug" というオプションがあることを発見。これをつけて実行してみると、


error: JAVAVER_LIBDIR /usr/share/java-1.6.0 does not exist
こんなメッセージがでてきた。実際に /usr/share/ をみてみると、java-1.4.2/、java-1.5.0/ はあるのに、java-1.6.0/ はない。じゃあ、作ればいいのかな、と思って作って実行してみると、、、


error: JAVAVER_JNIDIR /usr/lib/java-1.6.0 does not exist
さっきのエラーは消えて別の同じようなエラーが発生。こっちも同様にディレクトリを作成してい再実行してみる。すると、、、

成功!
動きました。こんな解決方法で良かったのかどうかは分かりませんが、まあ、当面の作業には問題ないので良しとしよう。。。

XMLHttpRequestオブジェクトでリダイレクトをハンドリングする方法

以前に、同じタイトルのエントリを書いたのですが、それよりもスマートなやり方がわかったので、書いておきます。

やりたいことについては前のエントリで書いたとおり。

  1. XMLHttpRequestで送ったリクエストが、サーバで認証エラー(セッションタイムアウトなど)となる
  2. このときサーバは認証エラー(=401)を返すのではなく、ログインページへのリダイレクトを返す
  3. クライアント(XMLHttpRequest)は、リダイレクト応答をうけとり、自動的にリダイレクト先を追跡する
  4. 結果、クライアントはログインページのHTMLを取得する(このときステータスコード=200)
  5. しかし、そもそもクライアントはJSON形式のデータを期待しているためスクリプトエラーとなる

前のエントリでは、XMLHttpRequestでレスポンスヘッダが使えることに着目して、"Content-Type"ヘッダが"text/html"だったらページを強制的に遷移させる方法でした。しかし、この方法は「"Content-Type"ヘッダが"text/html"である正常なレスポンス」があったら使えません。そこで以下の方法を思いつきました。4. および 6. のところが今回紹介する方法になります。

  1. XMLHttpRequestで送ったリクエストが、サーバで認証エラー(セッションタイムアウトなど)となる
  2. このときサーバは認証エラー(=401)を返すのではなく、ログインページへのリダイレクトを返す
  3. クライアント(XMLHttpRequest)は、リダイレクト応答をうけとり、自動的にリダイレクト先を追跡する
  4. ログインページでは、レスポンスヘッダに "AjaxRedirect: /Login.jsp" を追加する
  5. 結果、クライアントはログインページのHTMLを取得する(このときステータスコード=200)
  6. クラインアンは、レスポンスヘッダをチェックし、"AjaxRedirect" ヘッダが設定されているので、その値のURLを追跡する

以上で、XMLHttpRequestを使ったリダイレクトの完成。サンプルコードも載せておきます。

<%
response.setHeader("AjaxRedirect", request.getContextPath() + "/Login.jsp");
%>
httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == 4 && httpRequest.status == 200) {
    var redctUrl = httpRequest.getResponseHeader("AjaxRedirect");
    if (redctUrl != null) 
      // リダイレクトURLが設定されていれば追跡
      window.location.href = redctUrl;