わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

PostgreSQL + ActiveRecordで,属性名に悩む

シェルから,psql abc xyzというコマンドで,データベースabcに,ユーザxyzとして接続するとします.そして,「select user;」と「select usr;」を問い合わせると,結果は次のようになります.

xyz=> select user;
 current_user
--------------
 xyz
(1 行)

xyz=> select usr;
ERROR:  列"usr"は存在しません
行 1: select usr;
             ^

ここで出るuserは,現在実行しているユーザ名を返す関数であり,current_userと同じであることが,ここここに書かれています.
困ったことに,データベースabcの中で定義している,テーブルresultsの中に,userという名前の属性があります.定義したときは問題にならなかったのですが,SELECTの際に

xyz=> select * from results where user='takehikom';

としても,レコードが得られません.かわりに

xyz=> select * from results where results.user='takehikom';

とする必要があります.
さて,resultsと複数形のテーブル名にしていることから,感づく方もいらっしゃると思いますが,テーブルはRuby + ActiveRecordで定義し,また問い合わせもそこから行っています.次のRubyのコードは,不具合なく動きます.

array = Result.find(:all, :conditions => {:user => "takehikom"}, :order => "start_at DESC")

start_atというのは,これもresultsテーブルの中で定義している属性名で,開始日時を格納しています.ここで,userがtakehikomで,かつ,start_atが今年の4月1日以降のものを取り出したいと考えました.まずは,こう.

user = "takehikom"
start_at = "2010-04-01"
array = Result.find(:all, :conditions => ["user=? and start_at>=?", user, start_at], :order => "start_at DESC")

しかし,これでは取得できません.ここのuserが,冒頭に書いた関数になってしまったようです.次に,arrayへの代入のところを

array = Result.find(:all, :conditions => ["?.user=? and start_at>=?", Result.table_name, user, start_at], :order => "start_at DESC")

としたらどうかというと,Rubyの実行時エラーとなりました.エラーメッセージを見ると…

RuntimeError: ERROR C42601 M"."またはその近辺で構文エラー P43 Fscan.l L907 Rbase_yyerror: SELECT * FROM "results" WHERE ('results'.user='takehikom' and start_at>='2010-04-01') ORDER BY start_at DESC (ActiveRecord::StatementInvalid)

「'results'.user」って変ですね.「results.user」でないと,いけません.psqlから,

xyz=> SELECT * FROM "results" WHERE ('results'.user='takehikom' and start_at>='2010-04-01') ORDER BY start_at DESC;

ではうまくいかず,

xyz=> SELECT * FROM "results" WHERE (results.user='takehikom' and start_at>='2010-04-01') ORDER BY start_at DESC;

だと大丈夫なのを確認しました.
さてどうしますか…プレースホルダで実現するのは無理そうなので,

array = Result.find(:all, :conditions => ["#{Result.table_name}.user=? and start_at>=?", user, start_at], :order => "start_at DESC")

として,解決しました.
結局のところ,テーブル名,属性名を割り当てるときに,SQLのキーワード,関数名,システムカタログの名前*1などと同じにしないよう,配慮しないといけませんね….
関連:

*1:http://www.postgresql.jp/document/8.4/html/catalogs.htmlを見たところ,すべて「pg_」から始まっているので,バッティングする可能性はまずないでしょう.いや,何か作るときに,しでかすかも….