🗂

entのStorageKeyの使い所: インデックス名がPostgreSQLの識別子の上限を超えてインデックスを作成できない問題を回避する

2024/07/23に公開

こんにちは。バックエンドエンジニアの霞本です。

Sprocket Appの開発では、GoのO/Rマッパーとしてentを使用しています。entではバージョン管理型マイグレーションが可能になっており、Goで書いたスキーマ定義からマイグレーションに必要なSQL文を含むマイグレーションファイルを生成できます。しかし、マイグレーションファイルをPostgreSQLに適用した際、インデックス名がPostgreSQLの識別子の長さの上限を超えているためにインデックスが作成されないという問題がありました。この記事では、entのマイグレーションを使用する際に、PostgreSQLの識別子の長さの制限によりインデックスを作成できない問題を回避する方法を見ていきます。

作成するインデックスの例

例として、次の図のようなテーブル構成にて作成されるインデックスを考えます。

ER図

one_to_one_relationship_referencedone_to_one_relationship_referringの1対1の関係は、entのスキーマとして次のように定義します。

// テーブル間の関係を定義
func (OneToOneRelationshipReferenced) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("one_to_one_relationship_referring", OneToOneRelationshipReferring.Type).
        Unique(),
    }
}

このスキーマ定義ではone_to_one_relationship_referringテーブルのone_to_one_relationship_referenced_idがユニークになります。そのため、このスキーマ定義から生成されるマイグレーションファイル群には次のようなユニークインデックスを作成するクエリが含まれます。

-- 生成したマイグレーションファイル
CREATE UNIQUE INDEX "one_to_one_relationship_referring_one_to_one_relationship_referenced_id_key" ON "one_to_one_relationship_referring" ("one_to_one_relationship_referenced_id");

マイグレーションにおける問題

今回は、このスキーマ定義から生成されたマイグレーションファイルをgolang-migrateによりPostgreSQLに適用していました。しかし、マイグレーションを実行してもインデックスは作成されていませんでした。

インデックスが作成されていないため、新たにマイグレーションファイルを生成すると、過去のマイグレーションファイルに含まれているインデックス作成のクエリが再度含まれてしまっていました。

-- 新たに生成したマイグレーションファイル
-- 過去のマイグレーションファイルに含まれているインデックス作成のクエリがこのファイルにも含まれてしまう
CREATE UNIQUE INDEX "one_to_one_relationship_referring_one_to_one_relationship_referenced_id_key" ON "one_to_one_relationship_referring" ("one_to_one_relationship_referenced_id");

インデックスが作成されない原因

今回作成しようとしたインデックスの名前は、one_to_one_relationship_referring_one_to_one_relationship_referenced_id_keyです。これは、テーブル名とカラム名をアンダースコアにより結合して末尾に_keyを加えたものとなっています。

一方、PostgreSQLには識別子の長さに上限があります。PostgreSQLの識別子の長さの上限は63バイトです。今回はテーブル名がone_to_one_relationship_referring、カラム名がone_to_one_relationship_referenced_idとなります。このようにテーブル名やカラム名が長いと、インデックス名がPostgreSQLの識別子の長さの上限である63バイトを簡単に超えてしまいます。

今回のケースでは、長いテーブル名やカラム名を含んだインデックスの名前が63バイトを超えてしまっていたため、インデックスを作成できていませんでした。

-- インデックス名が長いため作成できない
CREATE UNIQUE INDEX "one_to_one_relationship_referring_one_to_one_relationship_referenced_id_key" ON "one_to_one_relationship_referring" ("one_to_one_relationship_referenced_id");

PostgreSQLの識別子の長さの制限を回避する方法

インデックス名がPostgreSQLの識別子の長さの制限を避けるための方法として、StorageKeyを設定できます。StorageKeyをエッジに対して適用すると、外部キー制約を設定するカラムの名前を任意に変更できます。StorageKeyは次のように設定します。

func (OneToOneRelationshipReferenced) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("one_to_one_relationship_referring", OneToOneRelationshipReferring.Type).
        Unique().
        // StorageKeyを設定して、外部キー制約を設定するカラムの名前を変更する
        StorageKey(edge.Column("referenced_id")),
    }
}

このスキーマ定義から生成するマイグレーションファイルに含まれるインデックス作成のクエリは、次のようになります。

-- PostgreSQLの識別子の長さの上限を超えないインデックス名となった
CREATE UNIQUE INDEX "one_to_one_relationship_referring_referenced_id_key" ON "one_to_one_relationship_referring" ("referenced_id");

生成されたインデックス名 one_to_one_relationship_referring_referenced_id_key は51バイトであり、63バイトに収まるインデックス名となりました。

インデックス名がPostgreSQLの識別子の長さの上限を超えていないため、マイグレーションにてインデックスの作成が成功します。新たに生成するマイグレーションファイルにも、同じインデックスを作成するクエリが含まれなくなりました。

まとめ

この記事では、GoのO/Rマッパーであるentを使用したマイグレーションにおいて、PostgreSQLのインデックスを作成できない問題と回避策を紹介しました。具体的には、インデックス名がPostgreSQLの識別子の長さの上限を超えているために、インデックス作成が失敗していることが原因でした。この問題を回避するために、StorageKeyを設定してインデックス名に使用されるカラム名を短くできます。

Sprocketで働きませんか?

弊社ではカジュアル面談を実施しております。
ご興味を持たれましたら、こちらからご応募お待ちしております。

Sprocketテックブログ

Discussion