PostgreSQL は SQL 文をケースインセンシティブに解釈する。TypeORM での調整方法
TypeORM と PostgreSQL の組み合わせで軽くつまづいた…。
目次
経緯
TypeORM を作って、次のようなエンティティを定義した。
@Entity()
export default class MyCustomer {
@PrimaryColumn({ type: 'varchar' })
userName: string;
}
コレを PostgreSQL に対して Sync すると、次のようなテーブルとカラムが自動生成される。
項目 | 名前 | ケース |
---|---|---|
テーブル名 | my_customer |
スネークケース |
カラム名 | userName |
キャメルケース |
- クラス名から生成されるテーブル名は、パスカルケースからスネークケースに。
- フィールド名から生成されるカラム名は、そのままキャメルケース。
…ケースが統一されていなくて違和感があるが、まぁ良いだろう。テーブルのことを考えたくなくて Sync したんだし。
psql
で問題発生
そう思ったのも束の間、テストのために psql
で PostgreSQL に繋いで UPDATE
文を書いてみると、なぜかうまくいかなかった。
admin=# SELECT userName from my_customer;
ERROR: column "userName" does not exist
そんなカラム存在しないよ、だと…?
SELECT * FROM my_customer;
と叩けばちゃんと userName
カラムが見えているのに、何故だ…。
PostgreSQL はケースインセンシティブ
そこで調べてみると、PostgreSQL は SQL 文をケースインセンシティブ、つまり大文字・小文字の区別をせずに解釈することが分かった。
内部的には大文字・小文字を区別して userName
カラムを作っているけど、SQL 文の中で userName
と書いても、username
と解釈されてしまうワケだ。
回避策 : ダブルクォートで囲む
回避策はあって、
SELECT "userName" from my_customer;
このようにカラム名やテーブル名をダブルクォートで囲むことで、大文字・小文字を区別して認識させられる。
# テーブル名も囲んだりして UPDATE する例
UPDATE "AdminUsers"
SET "userName" = 'John Doe'
WHERE "userId" = 1;
TypeORM が付ける名前を修正する
TypeORM が内部的に生成する SQL 文にはダブルクォートが書かれるようなので、TypeORM だけ使う上ではコレでも別に問題にならない。
しかし、自分で PostgreSQL を直で触ったりする時に面倒臭いし、何よりテーブル名とカラム名でケースに統一感がないのもキモいので、調整することにする。
一番手っ取り早いのは、デコレータ内で名前を渡してやること。
// ↓ 実際のテーブル名は複数形に
@Entity('my_customers')
export default class MyCustomer {
// ↓ name プロパティで実際のカラム名をスネークケースに
@PrimaryColumn({ name: 'user_name', type: 'varchar' })
userName: string;
}
こんな感じで指定できる。
以下の記事で紹介されているように、名前付けルールを自分で調整し、pluralize
パッケージを使って複数形を導いて自動適用したりもできる。
まとめ
TypeORM に限らず、O/R マッパーを使う際は、バックエンドとなる DB の仕様もちゃんと意識しておかないと、やっぱりコケるところがありますね…。