Rubyでpg gemを使って、postgresqlに接続してクエリの実行結果を見ていたところ、boolean型のカラムの戻り値がTRUEの場合にt
、FALSEの場合にf
となる現象を確認しました。
CREATE TABLE test1 (a boolean, b text); INSERT INTO test1 VALUES (TRUE, 'sic est'); INSERT INTO test1 VALUES (FALSE, 'non est'); SELECT * FROM test1; a | b ---+--------- t | sic est f | non est
最初はpg側で設定やオプションの指定をミスってるのかなと思ったのですが、そうではなくboolean型のカラムの戻り値がt, f
となるのはPostgreSQLの仕様のようでした。先ほど添付したSQLも以下のリンク先の公式ドキュメントから転載しました。
booleanのデータ型を出力する関数は例 8.2にあるように、常にtかfを出力します。
なぜboolean値をt, f
と表示しているのか背景を調べてみたところ、メモリ領域を節約するためとのことでした。
t
と表示するのではなくtrue
と表示するためにはt
と表示するのと比べて単純に4倍のメモリ領域が必要になります。内部では正しくboolean型の値を記録しているそうで、表示上のパフォーマンスを考慮した決定だったようです。
でもtrue, falseで取得したい
少なくともRubyでpg gem経由でデータを取得すると、boolean型の値はt, f
となってしまうため扱いが面倒です。
conn = PG.connect( dbname: 'samples' ) query = <<~EOS SELECT boolean_column FROM samples EOS res = conn.exec(query) puts res.first # { "boolean_column" => "t" }
GitHub - ged/ruby-pg: A PostgreSQL client library for Ruby
何か回避方法はないか調べてみたところ、明示的に型をキャストすることで、この問題を解決することが出来ました。
今回は文字列のtrue, false
にして欲しいので::text
を使用して、text型にキャストさせます。
明示的なキャスト要求の構文は、CAST(x AS typename)、もしくは、x::typename式です。
conn = PG.connect( dbname: 'samples' ) query = <<~EOS SELECT boolean_column::text FROM samples EOS res = conn.exec(query) puts res.first # { "boolean_column" => "true" }
無事に期待していた値を取得することが出来ました。
他にも::boolean
を試してみましたが、結果は変わらずt, f
のままでした。
またCASE文を使って、明示的にboolean型に変換してみても、結果は変わりませんでした。
query = <<~EOS SELECT boolean_column::boolean FROM samples EOS # { "boolean_column" => "t" }
query = <<~EOS SELECT CASE WHEN boolean_column THEN TRUE ELSE FALSE END AS boolean_column FROM samples EOS # { "boolean_column" => "t" }
pg gemのexecが内部で呼び出しているPostgreSQLが提供しているPQexec関数を確認しましたが、boolean型の値をt, f
とせずにそのまま表記させるようなオプション値はないようです。
::text
とするのが一番楽ですね。
Railsでこの現象に遭遇しないのはActiveRecordがパースする処理をしてくれているからのようです。