PostgreSQL8.3系への移行は悩み多き道
PostgreSQL June 30th, 2008
PostgreSQLの7.4系から一気に8.3系に移行すると大変なことになると知った。
例えばこんなテーブルとデータを用意する。
$ cat ./test_db.sql
CREATE TABLE test_table (
datetime timestamp without time zone NOT NULL DEFAULT '2008-01-01 00:00:00'
);
INSERT INTO test_table (datetime) VALUES ('2008-06-30 01:23:45');
これを7.4系(5432ポートで起動)と8.3系(5433ポートで起動)のPostgreSQLに流し込む。
$ psql -Upostgres -p 5433 -f ./test_db.sql db_test $ psql -Upostgres -p 5432 -f ./test_db.sql db_test
何が問題かというと、例えば次のようなSQLを発行した場合
$ psql -Upostgres -p 5432 -c "SELECT SUBSTR(datetime, 1, 4) FROM test_table" db_test substr -------- 2008 (1 row)
7.4系ではSUBSTRのような文字列型データを扱う関数を利用した場合、値が自動的にキャストされていたのだが、
psql -Upostgres -p 5433 -c "SELECT SUBSTR(datetime, 1, 4) FROM test_table" db_test ERROR: function substr(timestamp without time zone, integer, integer) does not exist at character 8 HINT: No function matches the given name and argument types. You might need to add explicit type casts. STATEMENT: SELECT substr(datetime, 1, 4) FROM test_table ERROR: function substr(timestamp without time zone, integer, integer) does not exist at character 8 HINT: No function matches the given name and argument types. You might need to add explicit type casts.
8.3系ではパフォーマンスを稼ぐためか、この自動キャストがなくなっている。もちろん
$ psql -Upostgres -p 5433 -c "SELECT SUBSTR(datetime::text, 1, 4) FROM test_table" db_test substr -------- 2008 (1 row)
明示的にキャストすれば問題ないのだが、移行する全てのアプリケーションのSQLをチェックしなおす必要がある。他にも、日付のカラムをLIKE検索している場合などにも同じようなことになる。
$ psql -Upostgres -p 5433 -c "SELECT * FROM test_table WHERE datetime LIKE '%0%'" db_test
ERROR: operator does not exist: timestamp without time zone ~~ unknown at character 42
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
STATEMENT: SELECT * FROM test_table WHERE datetime LIKE '%0%'
ERROR: operator does not exist: timestamp without time zone ~~ unknown at character 42
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
$ psql -Upostgres -p 5433 -c "SELECT * FROM test_table WHERE datetime::text LIKE '%0%'" db_test
datetime
---------------------
2008-06-30 01:23:45
(1 row)
たしかに、型を正しく理解してSQLを書くのが大事なわけだが、今さらこの挙動は困る。