Skip to content

Conversation

@ZerGo0
Copy link

@ZerGo0 ZerGo0 commented Nov 26, 2025

This fixes #4938

The main problem is that Cloudflare D1 seems to ignore PRAGMA foreign_keys=OFF/ON; for migrations like this where you recreate a table and some other tables has a FK onDelete: "cascade" on the table that gets recreated. This causes full data loss on the dependent tables, and this PR fixes that by creating backup tables for the FL dependent tables. This bug currently blocks a better-auth update from 1.3.7 to anything higher.

I wouldn't mind putting this behind a config setting since this is only a problem with specific sqlite implementations, but I feel like most people would miss that and lose their data...

I'm also not 100% sure if I caught all cases, but I've added tests to validate the fix and ran the generate command locally with the following schema to validate the fix:

import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const account = sqliteTable("account", {
  id: integer("account_id").primaryKey(),
  // CHANGE THE COLUMN TYPE HERE TO FORCE TABLE RECREATION
  name: text("name").notNull(),
});

export const organization = sqliteTable("organization", {
  id: integer("org_id").primaryKey(),
  name: text("name"),
});

export const project = sqliteTable("project", {
  id: integer("project_id").primaryKey(),
  accountId: integer("account_id").references(() => account.id, {
    onDelete: "cascade",
  }),
  orgId: integer("org_id").references(() => organization.id, {
    onDelete: "cascade",
  }),
  title: text("title"),
});

export const task = sqliteTable("task", {
  id: integer("task_id").primaryKey(),
  projectId: integer("project_id").references(() => project.id, {
    onDelete: "cascade",
  }),
  summary: text("summary"),
});

export const attachment = sqliteTable("attachment", {
  id: integer("attachment_id").primaryKey(),
  taskId: integer("task_id").references(() => task.id, { onDelete: "cascade" }),
  projectId: integer("project_id").references(() => project.id, {
    onDelete: "cascade",
  }),
});

export const tag = sqliteTable("tag", {
  id: integer("tag_id").primaryKey(),
  projectId: integer("project_id").references(() => project.id, {
    onDelete: "cascade",
  }),
  label: text("label"),
});

export const comment = sqliteTable("comment", {
  id: integer("comment_id").primaryKey(),
  taskId: integer("task_id").references(() => task.id, { onDelete: "cascade" }),
  body: text("body"),
});

Added logic to collect cascade dependents and ensure their data is preserved, preventing data loss in scenarios with foreign key constraints.
@ZerGo0 ZerGo0 changed the title [drizzle-kit]: fix sqlite data loss on table recreation for tables with FK onDelete cascade [drizzle-kit]: fix Cloudflare D1 data loss on table recreation for tables with FK onDelete cascade Nov 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Migration generator silently causes cascade data loss during SQLite table recreation without warning

1 participant