Open4

Cloudflare D1とDrizzleの組み合わせてで困ったこと

chimamechimame

同名なカラム名を持つ2つのテーブルをJOINすると取得データがおかしくなる。

現象

/*
Post = sqliteTable(
  'Post',
  {
    id: integer('id').primaryKey(),
    slug: text('slug').notNull(),
    userId: integer('userId'),
  }
)

User = sqliteTable(
  'User',
  {
    id: integer('id').primaryKey(),
    name: text('name'),
  }
)
*/

db
  .select()
  .from(Post)
  .innerJoin(User, eq(Post.userId, User.id))
  .all()

// => idというカラム列が2つのテーブルに存在して、おかしくなる。

原因

Cloudflare D1の不具合

https://github.com/drizzle-team/drizzle-orm/issues/555

https://github.com/cloudflare/workers-sdk/issues/3160

暫定対応

  1. SELECT項目を絞る
  2. ASを付けて別名にする
  3. SQLの発行回数が増えるが結合せずに別々にデータを取得する
chimamechimame

Drizzleのexists関数を使ったSQLが実行できない

現象

db
  .select()
  .from(User)
  .where(
    exists(
      db
        .select()
        .from(Post)
        .where(eq(Post.userId, User.id))
    )
  )
  .all()

// => 吐かれるSQLが実行できない

原因

吐かれるSQLが以下となる

SELECT
  "User"."id",
  "User"."name"
FROM
  "User"
WHERE
  EXISTS((
    SELECT
       "Post"."id",
       "Post"."slug",
       "Post"."userId"
    FROM
      "Post"
    WHERE
      "Post"."userId" = "User"."id"
  ))

exists 関数のカッコが1つ多い状態でSQLが吐かれるため、シンタックスエラーとなる。

暫定対応

  1. exists 関数を使わず自分でSQLを書く
  2. 別のSQL(例えばGroup Byを使用したサブクエリ)に書き換える
chimamechimame

複数のアプリケーションが同一のD1(SQLite)を参照する場合の開発環境に工夫が必要

現象

言葉では難しいが、例えばユーザ用に公開するサービス用のアプリケーションと運用者使う管理画面のアプリケーションの2つがあった場合にどちらも同じD1ないしSQLiteを参照することになると思う。この場合に以下のようなworkspaceを運用したとした場合にアプリケーションごとにSQLiteのファイルが必要にある

root
  ┣ packages
  ┃    ┣ database
  ┃    ┃    ┣ .wrangler // wrangler d1 migrations apply するとここにSQLiteができる
  ┃    ┃    ┣ migrations
  ┃    ┃    ┣ src
  ┃    ┃    ┃   ┗ schema.ts // 2つのアプリケーションが参照するDBのスキーマを格納
  ┃    ┃    ┗ package.json
  ┃    ┣ user
  ┃    ┃    ┣ .wrangler // 実際のアプリケーションが参照するD1(SQLiteのファイルはここ)
  ┃    ┃    ┗ package.json
  ┃    ┗ admin
  ┃         ┣ .wrangler // 実際のアプリケーションが参照するD1(SQLiteのファイルはここ)
  ┃         ┗ package.json
  ┗ package.json

原因

wranglerが参照するSQLiteのパスはアプリケーションディレクトリから固定のため。

暫定対応

SQLiteをレプリケーションすればいいが、開発環境なんでとりあえずSQLiteのファイルコピーで逃げる

chimamechimame

Drizzle-kitでマイグレートの変更が検知できないバグがある

現象

具体的に遭遇したのはindexの変更。例えば以下のようにindexを変更したが、drizzle-kit generate:sqlite では「No schema changes, nothing to migrate 😴」といって変更を検知してくれなかった

const PostTag = sqliteTable(
  'PostTag',
  {
    id: integer('id').primaryKey(),
    postId: integer('postId')
      .notNull()
      .references(() => Post.id),
    tagId: integer('tagId')
      .notNull()
      .references(() => Tag.id),
  },
  (postTag) => ({
//  uniqueIdx: index('PostTag_uniqueIdx').on( // 変更前は uniqueIndexではなく、indexとして定義していたため修正
    uniqueIdx: uniqueIndex('PostTag_uniqueIdx').on(
      postTag.postId,
      postTag.tagId
    ),
    postIdIdx: index('PostTag_postIdIdx').on(postTag.postId),
  })
)

原因

たぶんバグ。Issueには上がってなかった。

暫定対応

とりあえず適当にカラム追加して、追加したカラムのSQLじゃなくて、DROP INDEXCREATE INDEX に書き換えた。後マイグレーションのディレクトリに吐かれる meta/00xx_snapshot.json に定義が出力されるのでそれに追加したカラム情報を消す。(indexが変更されているのはしっかり反映されてたのでバグだと思う)